Index: branches/5.3.x/LICENSE =================================================================== --- branches/5.3.x/LICENSE (revision 16394) +++ branches/5.3.x/LICENSE (revision 16395) Property changes on: branches/5.3.x/LICENSE ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,2 ## Merged /in-portal/branches/5.1.x/LICENSE:r15851 Merged /in-portal/branches/5.2.x/LICENSE:r16196,16198-16199,16201,16203-16205,16212,16231-16233,16235-16236,16241,16243,16245-16247,16249-16251,16259-16264,16266-16268,16270-16274,16276-16279,16286,16288,16290-16291,16293-16300,16304-16309,16313-16314,16317-16318,16338-16339,16347-16348,16350,16352-16359,16362-16363,16367,16373-16374,16376-16377,16379,16388 Index: branches/5.3.x/robots.txt =================================================================== --- branches/5.3.x/robots.txt (revision 16394) +++ branches/5.3.x/robots.txt (revision 16395) Property changes on: branches/5.3.x/robots.txt ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,2 ## Merged /in-portal/branches/5.1.x/robots.txt:r15851 Merged /in-portal/branches/5.2.x/robots.txt:r16196,16198-16199,16201,16203-16205,16212,16231-16233,16235-16236,16241,16243,16245-16247,16249-16251,16259-16264,16266-16268,16270-16274,16276-16279,16286,16288,16290-16291,16293-16300,16304-16309,16313-16314,16317-16318,16338-16339,16347-16348,16350,16352-16359,16362-16363,16367,16373-16374,16376-16377,16379,16388 Index: branches/5.3.x/admin/system_presets/simple/system_logs_system-log.php =================================================================== --- branches/5.3.x/admin/system_presets/simple/system_logs_system-log.php (revision 16394) +++ branches/5.3.x/admin/system_presets/simple/system_logs_system-log.php (revision 16395) Property changes on: branches/5.3.x/admin/system_presets/simple/system_logs_system-log.php ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +Id \ No newline at end of property Index: branches/5.3.x/core/kernel/db/db_tag_processor.php =================================================================== --- branches/5.3.x/core/kernel/db/db_tag_processor.php (revision 16394) +++ branches/5.3.x/core/kernel/db/db_tag_processor.php (revision 16395) @@ -1,3109 +1,3110 @@ <?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 kDBTagProcessor extends kTagProcessor { /** + * Mapping between "list_name" tag attribute values and generated Specials. + * + * @var array + */ + protected $nameToSpecialMapping = array(); + + /** * Returns true if "new" button was pressed in toolbar * * @param Array $params * @return bool */ function IsNewMode($params) { $object = $this->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('<span class="debug_error">no filters defined</span> for prefix <b>' . $this->Prefix . '</b>, but <b>DrawFilterMenu</b> 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 ) { $this->Application->HandleEvent(new kEvent($this->getPrefixSpecial() . ':OnRemoveFilters')); $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('<span class="debug_error">no refresh intervals defined</span> for prefix <strong>'.$this->Prefix.'</strong>, but <strong>DrawAutoRefreshMenu</strong> 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 ) { + if ( getArrayValue($this->nameToSpecialMapping, $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]) ) { + if ( !array_key_exists($list_name, $this->nameToSpecialMapping) ) { // 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]; + $special = $this->nameToSpecialMapping[$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); + $this->nameToSpecialMapping[$list_name] = $special; } 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']) ? '<tr>' : ''); } 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) : '<td> </td>'; $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']) ? '</tr>' : ''); } 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) : '<td> </td>'; 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) : '</tr>'; } $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) { // <inp2:SetField field="Value" src=p:cust_{$custom_name}"/> $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 <strong>'.$object->Prefix.'</strong> field <strong>'.$field.'</strong>', 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', $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', $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', $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 href="(.*)".*>(.*)<\/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 <SQL>SELECT f1 AS OptionName, f2 AS OptionValue... FROM <PREFIX>TableName </SQL> * 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 "<strong>' . $object->GetDBField('VariableName') . '</strong>" 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(); + $form_name .= '_' . kUtil::crc32(json_encode($params)); $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 = '<button style="background-image: url(' . $icon_url . ');" onclick="' . kUtil::escape($button_onclick, kUtil::ESCAPE_HTML) . '" class="cms-btn-new ' . $button_class . '">' . kUtil::escape($button_title, kUtil::ESCAPE_HTML) . ' </button>'; 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 = '<form method="POST" style="display: inline; margin: 0px" name="' . $form_name_escaped . '" id="' . $form_name_escaped . '" action="' . $edit_url . '"></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 "<strong>' . $this->getPrefixSpecial() . ':' . __METHOD__ . '</strong>")', 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 + * Checks, that requested option is checked inside field value. * - * @param Array $params - * @return bool + * @param array $params Tag params. + * + * @return boolean */ - function Selected($params) + protected function Selected(array $params) { + /** @var kDBItem $object */ $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); + if ( strpos($value, '|') !== false ) { + $selected_values = explode('|', substr($value, 1, -1)); + } + else { + $selected_values = array((string)$value); } - return $value; + return in_array((string)$params['value'], $selected_values, true); } /** * 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 "<strong>' . $form_name . '</strong>" failed, since object "<strong>' . $this->getPrefixSpecial() . '</strong>" 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/cat_tag_processor.php =================================================================== --- branches/5.3.x/core/kernel/db/cat_tag_processor.php (revision 16394) +++ branches/5.3.x/core/kernel/db/cat_tag_processor.php (revision 16395) @@ -1,965 +1,968 @@ <?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 kCatDBTagProcessor extends kDBTagProcessor { /** * Permission Helper * * @var kPermissionsHelper */ var $PermHelper = null; public function __construct() { parent::__construct(); $this->PermHelper = $this->Application->recallObject('PermissionsHelper'); } function ItemIcon($params) { $config = $this->getUnitConfig(); $grid = $config->getGridByName($params['grid']); if ( !array_key_exists('Icons', $grid) ) { return ''; } $icons = $grid['Icons']; if ( array_key_exists('name', $params) ) { $icon_name = $params['name']; return array_key_exists($icon_name, $icons) ? $icons[$icon_name] : ''; } $status_field = $config->getStatusField(true); if ( !$status_field ) { return $icons['default']; } $object = $this->getObject($params); /* @var $object kDBList */ $value = $object->GetDBField($status_field); // sets base status icon if ( $value == STATUS_ACTIVE ) { /*if ( $object->HasField('IsPop') && $object->GetDBField('IsPop') ) { $value = 'POP'; } if ( $object->HasField('IsHot') && $object->GetDBField('IsHot') ) { $value = 'HOT'; }*/ if ( $object->HasField('IsNew') && $object->GetDBField('IsNew') ) { $value = 'NEW'; } /*if ( $object->HasField('EditorsPick') && $object->GetDBField('EditorsPick') ) { $value = 'PICK'; }*/ } return array_key_exists($value, $icons) ? $icons[$value] : $icons['default']; } /** * Allows to create valid mod-rewrite compatible link to module item * * @param Array $params * @param string $id_prefix * @return string */ function ItemLink($params, $id_prefix = null) { if ($this->Application->isAdmin) { // link from Admin to Front-end $params['prefix'] = '_FRONT_END_'; if ( $this->Application->ConfigValue('UseModRewrite') ) { $params['__MOD_REWRITE__'] = 1; } else { $params['index_file'] = 'index.php'; } } if ( !isset($params['pass']) ) { $params['pass'] = 'm,'.$this->Prefix; } // set by PrintList2 tag $item_id = $this->Application->GetVar($this->getPrefixSpecial() . '_id'); if ( !$item_id && ($this->Special != 'next' && $this->Special != 'previous') ) { // set from page url $item_id = $this->Application->GetVar($this->Prefix . '_id'); } $object = $this->getObject($params); /* @var $object kDBItem */ $params['m_cat_page'] = 1; $params['m_cat_id'] = $object->GetDBField('CategoryId'); $params['pass_category'] = 1; $params[$this->Prefix . '_id'] = $item_id ? $item_id : $object->GetID(); return $this->Application->ProcessParsedTag('m', 't', $params); } /** * Builds link for browsing current item on Front-End * * @param Array $params * @return string */ function PageBrowseLink($params) { $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(); $params['editing_mode'] = $settings['default_editing_mode']; $params['m_theme'] = $themes_helper->getCurrentThemeId(); $params['admin'] = 1; return $this->ItemLink($params); } function CategoryPath($params) { if ($this->Application->isAdminUser) { // path for module root category in admin if (!isset($params['cat_id'])) { $params['cat_id'] = $this->Application->RecallVar($params['session_var'], 0); } } else { // path for category item category in front-end $object = $this->getObject($params); $params['cat_id'] = $object->GetDBField('CategoryId'); } $navigation_bar = $this->Application->recallObject('kNavigationBar'); /* @var $navigation_bar kNavigationBar */ return $navigation_bar->build($params); } function BuildListSpecial($params) { if ($this->Special != '') return $this->Special; if ( isset($params['parent_cat_id']) ) { $parent_cat_id = $params['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'); } } $recursive = isset($params['recursive']); $list_unique_key = $this->getUniqueListKey($params).$recursive; if ($list_unique_key == '') { return parent::BuildListSpecial($params); } return crc32($parent_cat_id.$list_unique_key); } 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 ListReviews($params) { $prefix = $this->Prefix . '-rev'; $review_tag_processor = $this->Application->recallObject($prefix . '.item_TagProcessor'); /* @var $review_tag_processor kDBTagProcessor */ return $review_tag_processor->PrintList($params); } function ReviewCount($params) { $review_tag_processor = $this->Application->recallObject('rev.item_TagProcessor'); /* @var $review_tag_processor kDBTagProcessor */ return $review_tag_processor->TotalRecords($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.'.$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); $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 = preg_replace('/^(Content&\|&|Content)/i', '', $object->GetField('CachedNavbar')); $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; } /** * Allows to determine if original value should be shown * * @param Array $params * @return bool */ function DisplayOriginal($params) { // original id found & greater then zero + show original $display_original = isset($params['display_original']) && $params['display_original']; $object = $this->getObject($params); /* @var $object kCatDBItem */ $owner_field = $this->getUnitConfig()->getOwnerField('CreatedById'); $perm_value = $this->PermHelper->ModifyCheckPermission($object->GetDBField($owner_field), $object->GetDBField('CategoryId'), $this->Prefix); return $display_original && ($perm_value == 1) && $this->Application->GetVar($this->Prefix.'.original_id'); } /** * 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 kCatDBItem */ // 1. category restriction $params['cat_id'] = $object->isLoaded() ? $object->GetDBField('ParentPath') : $this->Application->GetVar('m_cat_id'); // 2. owner restriction $owner_field = $this->getUnitConfig()->getOwnerField('CreatedById'); $is_owner = $object->GetDBField($owner_field) == $this->Application->RecallVar('user_id'); return $perm_helper->TagPermissionCheck($params, $is_owner); } /** * Creates link to current category or to module root category, when current category is home * * @param Array $params * @return string */ function SuggestItemLink($params) { if (!isset($params['cat_id'])) { $params['cat_id'] = $this->Application->GetVar('m_cat_id'); } if ($params['cat_id'] == 0) { $params['cat_id'] = $this->Application->findModule('Var', $this->Prefix, 'RootCat'); } $params['m_cat_page'] = 1; return $this->Application->ProcessParsedTag('c', 'CategoryLink', $params); } /** * Allows to detect if item has any additional images available * * @param Array $params * @return string */ function HasAdditionalImages($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $cache_key = $object->Prefix . '_additional_images[%' . $this->Application->incrementCacheSerial($object->Prefix, $object->GetID(), false) . '%]'; $ret = $this->Application->getCache($cache_key); if ( $ret === false ) { $this->Conn->nextQueryCachable = true; $sql = 'SELECT ImageId FROM ' . $this->Application->getUnitConfig('img')->getTableName() . ' WHERE ResourceId = ' . $object->GetDBField('ResourceId') . ' AND DefaultImg != 1 AND Enabled = 1'; $ret = $this->Conn->GetOne($sql) ? 1 : 0; $this->Application->setCache($cache_key, $ret); } return $ret; } /** * Checks that item is pending * * @param Array $params * @return bool */ function IsPending($params) { $object = $this->getObject($params); $pending_status = Array (STATUS_PENDING, STATUS_PENDING_EDITING); return in_array($object->GetDBField('Status'), $pending_status); } function IsFavorite($params) { static $favorite_status = Array (); $object = $this->getObject($params); /* @var $object kDBList */ if ( !isset($favorite_status[$this->Special]) ) { $resource_ids = $object->GetCol('ResourceId'); $user_id = $this->Application->RecallVar('user_id'); $sql = 'SELECT FavoriteId, ResourceId FROM ' . $this->Application->getUnitConfig('fav')->getTableName() . ' WHERE (PortalUserId = ' . $user_id . ') AND (ResourceId IN (' . implode(',', $resource_ids) . '))'; $favorite_status[$this->Special] = $this->Conn->GetCol($sql, 'ResourceId'); } return isset($favorite_status[$this->Special][$object->GetDBField('ResourceId')]); } /** * Returns item's editors pick status (using not formatted value) * * @param Array $params * @return bool */ function IsEditorsPick($params) { $object = $this->getObject($params); return $object->GetDBField('EditorsPick') == 1; } function FavoriteToggleLink($params) { $fav_prefix = $this->Prefix.'-fav'; $params['pass'] = implode(',', Array('m', $this->Prefix, $fav_prefix)); $params[$fav_prefix.'_event'] = 'OnFavoriteToggle'; return $this->ItemLink($params); } /** * Checks if item is passed in url * * @param Array $params * @return bool */ function ItemAvailable($params) { return $this->Application->GetVar($this->getPrefixSpecial().'_id') > 0; } function SortingSelected($params) { $list =& $this->GetList($params); $user_sorting_start = $this->getUserSortIndex(); // remove language prefix from $current_sorting_field $current_sorting_field = preg_replace('/^l[\d]+_(.*)/', '\\1', $list->GetOrderField($user_sorting_start)); $current_sorting = $current_sorting_field . '|' . $list->GetOrderDirection($user_sorting_start); return strtolower($current_sorting) == strtolower($params['sorting']) ? $params['selected'] : ''; } function CombinedSortingDropDownName($params) { $list =& $this->GetList($params); if ($list->isMainList()) { return parent::CombinedSortingDropDownName($params); } return $list->Prefix . '_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); if ($list->isMainList()) { return parent::SubmitName($params); } return 'events[' . $list->Prefix . '][' . $params['event'] . ']'; } /** * Returns prefix + any word (used for shared between categories per page settings) * * @param Array $params * @return string */ function VarName($params) { $list =& $this->GetList($params); if ($list->isMainList()) { return parent::VarName($params); } return $list->Prefix . '_' . $params['type']; } /** * Checks if we are viewing module root category * * @param Array $params * @return bool */ function IsModuleHome($params) { $root_category = $this->Application->findModule('Var', $this->Prefix, 'RootCat'); return $root_category == $this->Application->GetVar('m_cat_id'); } /** * Dynamic votes indicator * * @param Array $params * * @return string */ function VotesIndicator($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $rating_helper = $this->Application->recallObject('RatingHelper'); /* @var $rating_helper RatingHelper */ $small_style = array_key_exists('small_style', $params) ? $params['small_style'] : false; return $rating_helper->ratingBar($object, true, '', $small_style); } function RelevanceIndicator($params) { $object = $this->getObject($params); - $search_results_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search'; + /** @var kSearchHelper $search_helper */ + $search_helper = $this->Application->recallObject('SearchHelper'); + + $search_results_table = $search_helper->getSearchTable(); $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); } function SearchResultField($params) { $ret = $this->Field($params); $keywords = unserialize( $this->Application->RecallVar('highlight_keywords') ); $opening = $this->Application->ParseBlock( Array('name' => $this->SelectParam($params, 'highlight_opening_render_as,block_highlight_opening')) ); $closing = $this->Application->ParseBlock( Array('name' => $this->SelectParam($params, 'highlight_closing_render_as,block_highlight_closing')) ); foreach ($keywords as $index => $keyword) { $keywords[$index] = preg_quote($keyword, '/'); } return preg_replace('/('.implode('|', $keywords).')/i', $opening.'\\1'.$closing, $ret); } /** * Shows keywords, that user searched * * @param Array $params * @return bool */ function SearchKeywords($params) { $keywords = $this->Application->GetVar('keywords'); $sub_search = $this->Application->GetVar('search_type') == 'subsearch'; return ($keywords !== false) && !$sub_search ? $keywords : $this->Application->RecallVar('keywords'); } function AdvancedSearchForm($params) { $search_table = $this->Application->getUnitConfig('confs')->getTableName(); $module_name = $this->Application->findModule('Var', $this->Prefix, 'Name'); $sql = 'SELECT * FROM ' . $search_table . ' WHERE (ModuleName = ' . $this->Conn->qstr($module_name) . ') AND (AdvancedSearch = 1) ORDER BY DisplayOrder'; $search_config = $this->Conn->Query($sql); $ret = ''; foreach ($search_config as $record) { $params['name'] = $this->SelectParam($params, 'and_or_render_as,and_or_block'); $params['field'] = $record['FieldName']; $params['andor'] = $this->Application->ParseBlock($params); $params['name'] = $this->SelectParam($params, $record['FieldType'] . '_render_as,' . $record['FieldType'] . '_block'); $params['caption'] = $this->Application->Phrase($record['DisplayName']); $ret .= $this->Application->ParseBlock($params); } return $ret; } /** * 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_name1 = $this->Application->incrementCacheSerial('c', $local ? $category_id : null, false); $serial_name2 = $this->Application->incrementCacheSerial($this->Prefix, null, false); $cache_key = 'categoryitems_last_updated[%' . $serial_name1 . '%][%' . $serial_name2 . '%]'; $row_data = $this->Application->getCache($cache_key); if ( $row_data === false ) { $config = $this->getUnitConfig(); 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(item_table.Modified) AS ModDate, MAX(item_table.CreatedOn) AS NewDate FROM ' . $config->getTableName() . ' 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 c.TreeLeft BETWEEN ' . $tree_left . ' AND ' . $tree_right; } else { // scan all categories in system $sql = 'SELECT MAX(Modified) AS ModDate, MAX(CreatedOn) AS NewDate FROM ' . $config->getTableName(); } $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); } /** * Counts category item count in system (not category-dependent) * * @param Array $params * @return int */ function ItemCount($params) { $count_helper = $this->Application->recallObject('CountHelper'); /* @var $count_helper kCountHelper */ $today_only = isset($params['today']) && $params['today']; return $count_helper->ItemCount($this->Prefix, $today_only); } /** * Displays list of allowed categories on "Suggest Link" and similar forms. * * @param array $params Tag params. * * @return string * @access protected */ protected function CategorySelector($params) { $category_id = isset($params['category_id']) && is_numeric($params['category_id']) ? $params['category_id'] : false; if ( $category_id === false ) { // if category id not given use module root category $category_id = $this->Application->findModule('Var', $this->Prefix, 'RootCat'); } $id_field = $this->Application->getUnitConfig('c')->getIDField(); // get category list (permission based) $categories = $this->Conn->Query($this->getCategorySelectorQuery($category_id), $id_field); $block_params = $this->prepareTagParams($params); $block_params['name'] = $params['render_as']; $block_params['strip_nl'] = 2; $ret = ''; foreach ($categories as $category_id => $category_data) { // print category $block_params['separator'] = isset($params['category_id']) ? $params['separator'] : ''; // return original separator, remove separator for top level categories $block_params['category_id'] = $category_id; $block_params['category_name'] = $category_data['CategoryName']; $cached_navbar = preg_replace('/^(Content&\|&|Content)/i', '', $category_data['CachedNavbar']); $block_params['full_path'] = str_replace('&|&', ' > ', $cached_navbar); $ret .= $this->Application->ParseBlock($block_params); // print it's children $block_params['separator'] = ' ' . $params['separator']; $ret .= $this->CategorySelector($block_params); } return $ret; } /** * Returns given category sub-categories, that user have rights to view. * * @param int $category_id Category. * * @return array * @access protected */ protected function getCategorySelectorQuery($category_id) { $category_config = $this->Application->getUnitConfig('c'); $title_field = $category_config->getTitleField(); $where_clause = Array ( 'c.ParentId = ' . $category_id, 'c.Status = ' . STATUS_ACTIVE, ); $sql = 'SELECT c.' . $title_field . ' AS CategoryName, c.' . $category_config->getIDField() . ', c.l' . $this->Application->GetVar('m_lang') . '_CachedNavbar AS CachedNavbar FROM ' . $category_config->getTableName() . ' c'; $count_helper = $this->Application->recallObject('CountHelper'); /* @var $count_helper kCountHelper */ list ($sql, $where_clause) = $count_helper->attachViewPermissionCheck('c', $sql, $where_clause); return $sql . ' WHERE (' . implode(') AND (', $where_clause) . ') ORDER BY c.' . $title_field . ' ASC'; } function PrintMoreCategories($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $category_ids = $this->Field($params); if (!$category_ids) { return ''; } $category_ids = explode('|', substr($category_ids, 1, -1)); $category_config = $this->Application->getUnitConfig('c'); $id_field = $category_config->getIDField(); $title_field = $category_config->getTitleField(); $table_name = $category_config->getTableName(); $sql = 'SELECT '.$title_field.' AS CategoryName, '.$id_field.', l' . $this->Application->GetVar('m_lang') . '_CachedNavbar AS CachedNavbar FROM '.$table_name.' WHERE '.$id_field.' IN ('.implode(',', $category_ids).')'; $categories = $this->Conn->Query($sql, $id_field); $block_params = $this->prepareTagParams($params); $block_params['name'] = $params['render_as']; $ret = ''; foreach ($categories as $category_id => $category_data) { $block_params['category_id'] = $category_id; $block_params['category_name'] = $category_data['CategoryName']; $cached_navbar = preg_replace('/^(Content&\|&|Content)/i', '', $category_data['CachedNavbar']); $block_params['full_path'] = str_replace('&|&', ' > ', $cached_navbar); $ret .= $this->Application->ParseBlock($block_params); } return $ret; } function DownloadFileLink($params) { $params[$this->getPrefixSpecial().'_event'] = 'OnDownloadFile'; return $this->ItemLink($params); } function ImageSrc($params) { list ($ret, $tag_processed) = $this->processAggregatedTag('ImageSrc', $params, $this->getPrefixSpecial()); return $tag_processed ? $ret : false; } /** * Registers hit for item (one time per session) * * @param Array $params */ function RegisterHit($params) { $object = $this->getObject($params); /* @var $object kCatDBItem */ if ($object->isLoaded()) { $object->RegisterHit(); } } /** * Returns link to item's author public profile * * @param Array $params * @return string */ function ProfileLink($params) { $object = $this->getObject($params); $owner_field = array_key_exists('owner_field', $params) ? $params['owner_field'] : 'CreatedById'; $params['user_id'] = $object->GetDBField($owner_field); unset($params['owner_field']); return $this->Application->ProcessParsedTag('m', 'Link', $params); } /** * Checks, that "view in browse mode" functionality available * * @param Array $params * @return bool */ function BrowseModeAvailable($params) { $valid_special = $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('CategoryId'), 'pass' => 'all,'.$this->Prefix, 'no_pass_through' => 1, ); return $this->Application->HREF($edit_template,'', $url_params); } function LanguageVisible($params) { $field = $this->SelectParam($params, 'name,field'); preg_match('/l([\d]+)_(.*)/', $field, $regs); $params['name'] = $regs[2]; return $this->HasLanguageError($params) || $this->Application->GetVar('m_lang') == $regs[1]; } function HasLanguageError($params) { static $languages = null; if ( !isset($languages) ) { $language_config = $this->Application->getUnitConfig('lang'); $sql = 'SELECT ' . $language_config->getIDField() . ' FROM ' . $language_config->getTableName() . ' WHERE Enabled = 1'; $languages = $this->Conn->GetCol($sql); } $field = $this->SelectParam($params, 'name,field'); $object = $this->getObject($params); /* @var $object kDBItem */ foreach ($languages as $language_id) { $check_field = 'l' . $language_id . '_' . $field; if ( $object->GetErrorMsg($check_field, false) ) { return true; } } return false; } /** * Returns list of categories, that have add/edit permission for current category item type * * @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')); } return json_encode($categories); } - } \ No newline at end of file + } Index: branches/5.3.x/core/kernel/db/dblist.php =================================================================== --- branches/5.3.x/core/kernel/db/dblist.php (revision 16394) +++ branches/5.3.x/core/kernel/db/dblist.php (revision 16395) @@ -1,1767 +1,1767 @@ <?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!'); /** * DBList * */ class kDBList extends kDBBase implements Iterator, Countable { // kDBList filter types (then, types are divided into classes) /** * Having filter [step1] * */ const HAVING_FILTER = 1; /** * Where filter [step1] * */ const WHERE_FILTER = 2; /** * Aggregated filter [step1] * */ const AGGREGATE_FILTER = 3; /** * System filter [step2, AND] * */ const FLT_SYSTEM = 1; /** * User filter [step2, OR] * @deprecated * */ const FLT_NORMAL = 2; /** * User "Search" filter [step2, OR] * */ const FLT_SEARCH = 3; /** * User "View Menu" filter [step2, AND] * */ const FLT_VIEW = 4; /** * User "Custom" (above grid columns) filter [step2, AND] * */ const FLT_CUSTOM = 5; /** * kMultipleFilter AND filter [step3] * */ const FLT_TYPE_AND = 'AND'; /** * kMultipleFilter OR filter [step3] * */ const FLT_TYPE_OR = 'OR'; /** * Totals for fields specified in config * * @var Array * @access protected */ protected $Totals = Array (); /** * Remembers if totals were calculated * * @var bool * @access protected */ protected $TotalsCalculated = false; /** * List of "ORDER BY" fields * * @var Array * @access protected */ protected $OrderFields = Array (); /** * Counted total number of records in the query - without pagination (system+user filters) * * @var int * @access protected */ protected $RecordsCount = 0; /** * Record count with only system filters applied * * @var int * @access protected */ protected $NoFilterCount = 0; /** * Record count selected to be showed on current page * * @var int * @access protected */ protected $SelectedCount = 0; /** * Array of selected records * * @var Array * @access public */ public $Records; /** * Current record index * * @var int * @access protected */ protected $CurrentIndex = 0; /** * List items per-page * * @var int * @access protected */ protected $PerPage; /** * Page count in list based on PerPage & RecordsCount attributes * * @var int * @access protected */ protected $TotalPages; /** * Current page number - used when forming LIMIT clause of SELECT statement * * @var int * @access protected */ protected $Page; /** * Offset for LIMIT clause, calculated in {@link kDBList::PerPage()} * * @var int * @access protected */ protected $Offset; /** * WHERE filter objects * * @var kMultipleFilter[] * @access protected */ protected $WhereFilter = Array (); /** * HAVING filter objects * * @var kMultipleFilter[] * @access protected */ protected $HavingFilter = Array (); /** * AGGREGATED filter objects * * @var kMultipleFilter[] * @access protected */ protected $AggregateFilter = Array (); /** * List of "GROUP BY" fields * * @var Array * @access protected */ protected $GroupByFields = Array (); /** * Remembers if list was queried * * @var bool * @access protected */ protected $Queried = false; /** * Remembers if list was counted * * @var bool * @access protected */ protected $Counted = false; /** * Name of the grid, used to display the list * * @var string */ var $gridName = ''; /** * Identifies this list as main on the page, that allows to react on "page", "per_page" and "sort_by" parameters from url * * @var bool * @access protected */ protected $mainList = false; /** * Holds field errors * * @var Array * @access protected */ protected $FieldErrors = Array (); /** * Creates kDBList * * @return kDBList * @access public */ public function __construct() { parent::__construct(); $this->OrderFields = Array(); foreach ( $this->getFilterStructure() as $filter_params ) { $property_name = $filter_params['type']; $filter_group =& $this->$property_name; $filter_group[$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) { $this->getFilterCollection($filter_type, $filter_scope)->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) { return $this->getFilterCollection($filter_type, $filter_scope)->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) { $this->getFilterCollection($filter_type, $filter_scope)->removeFilter($name); } /** * Returns filter collection. * * @param integer $filter_type Is filter having filter or where filter. * @param integer $filter_scope Filter subtype: FLT_NORMAL,FLT_SYSTEM,FLT_SEARCH,FLT_VIEW,FLT_CUSTOM. * * @return kMultipleFilter */ protected function getFilterCollection($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' ); /** @var kMultipleFilter[] $filters */ $property_name = $filter_source[$filter_type]; $filters =& $this->$property_name; return $filters[$filter_scope]; } /** * Clear all filters * * @access public */ public function clearFilters() { foreach ( $this->getFilterStructure() as $filter_params ) { $property_name = $filter_params['type']; $filter_group =& $this->$property_name; $filter_group[$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)) { 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 if ( $this->Application->HttpQuery->refererIsOurSite() ) { // no records & page > 1, try to reset to last page $this->SetPage($this->GetTotalPages()); $this->Query($force); } else { // no records & page > 1, show 404 page trigger_error('Unknown page <strong>' . $this->Page . '</strong> in <strong>' . $this->getPrefixSpecial() . '</strong> 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, $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 ) { + $fields = str_replace('%2$s', $this->Application->GetVar('m_lang'), $fields); + 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 */ - 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] ); - } + $where->addFilter( + 'system_where', + $this->extractCalculatedFields($this->WhereFilter[self::FLT_SYSTEM]->getSQL()) + ); 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('<span class="debug_error">Incorrect sorting</span> defined (field = <b>'.$field.'</b>; direction = <b>'.$direction.'</b>) in config for prefix <b>'.$this->Prefix.'</b>', 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 "<strong>' . $name . '</strong>" doesn\'t exist in prefix <strong>' . $this->getPrefixSpecial() . '</strong>', 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: "<strong>' . rtrim($parent_prefix . '.' . $special, '.') . '</strong>"; sub-prefix: "<strong>' . $this->getPrefixSpecial() . '</strong>")', 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|.*?$)/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); } } Index: branches/5.3.x/core/kernel/db/db_event_handler.php =================================================================== --- branches/5.3.x/core/kernel/db/db_event_handler.php (revision 16394) +++ branches/5.3.x/core/kernel/db/db_event_handler.php (revision 16395) @@ -1,3448 +1,3460 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); define('EH_CUSTOM_PROCESSING_BEFORE',1); define('EH_CUSTOM_PROCESSING_AFTER',2); /** * Note: * 1. When addressing variables from submit containing * Prefix_Special as part of their name use * $event->getPrefixSpecial(true) instead of * $event->getPrefixSpecial() as usual. This is due PHP * is converting "." symbols in variable names during * submit info "_". $event->getPrefixSpecial optional * 1st parameter returns correct current Prefix_Special * for variables being submitted such way (e.g. variable * name that will be converted by PHP: "users.read_only_id" * will be submitted as "users_read_only_id". * * 2. When using $this->Application-LinkVar on variables submitted * from form which contain $Prefix_Special then note 1st item. Example: * LinkVar($event->getPrefixSpecial(true).'_varname',$event->getPrefixSpecial().'_varname') * */ /** * EventHandler that is used to process * any database related events * */ class kDBEventHandler extends kEventHandler { /** * Checks permissions of user * * @param kEvent $event * @return bool * @access public */ public function CheckPermission(kEvent $event) { $section = $event->getSection(); if ( !$this->Application->isAdmin ) { $allow_events = Array ('OnSearch', 'OnSearchReset', 'OnNew'); if ( in_array($event->Name, $allow_events) ) { // allow search on front return true; } } elseif ( ($event->Name == 'OnPreSaveAndChangeLanguage') && !$this->UseTempTables($event) ) { // allow changing language in grids, when not in editing mode return $this->Application->CheckPermission($section . '.view', 1); } if ( !preg_match('/^CATEGORY:(.*)/', $section) ) { // only if not category item events if ( (substr($event->Name, 0, 9) == 'OnPreSave') || ($event->Name == 'OnSave') ) { if ( $this->isNewItemCreate($event) ) { return $this->Application->CheckPermission($section . '.add', 1); } else { return $this->Application->CheckPermission($section . '.add', 1) || $this->Application->CheckPermission($section . '.edit', 1); } } } if ( $event->Name == 'OnPreCreate' ) { // save category_id before item create (for item category selector not to destroy permission checking category) $this->Application->LinkVar('m_cat_id'); } 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]) ) { // <inp2:lang.auto-phrase_Field name="DateFormat"/> - returns field DateFormat value from language (LanguageId is extracted from current phrase object) $main_object = $this->Application->recallObject($regs[1]); /* @var $main_object kDBItem */ 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 != '') ) { + $event->setEventParam(kEvent::FLAG_ID_FROM_REQUEST, true); + return $ret; } // 2. get id from env (used in front) $ret = $this->Application->GetVar($event->getPrefixSpecial() . '_id'); if ( ($ret !== false) && ($ret != '') ) { + $event->setEventParam(kEvent::FLAG_ID_FROM_REQUEST, true); + 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); + $event->setEventParam(kEvent::FLAG_ID_FROM_REQUEST, true); } } else { // if selected ids are not yet stored $this->StoreSelectedIDs($event); - return $this->Application->GetVar($event->getPrefixSpecial() . '_id'); // StoreSelectedIDs sets this variable + + // StoreSelectedIDs sets this variable. + $ret = $this->Application->GetVar($event->getPrefixSpecial() . '_id'); + + if ( ($ret !== false) && ($ret != '') ) { + $event->setEventParam(kEvent::FLAG_ID_FROM_REQUEST, true); + + return $ret; + } } 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 <strong>' . $event->getPrefixSpecial() . '</strong> <span class="debug_error">not passed</span>', E_USER_NOTICE); } $this->Application->SetVar($event->getPrefixSpecial() . '_id', $first_id); return $ret; } /** * Returns stored selected ids as an array * * @param kEvent $event * @param bool $from_session return ids from session (written, when editing was started) * @return Array * @access protected */ protected function getSelectedIDs(kEvent $event, $from_session = false) { if ( $from_session ) { $wid = $this->Application->GetTopmostWid($event->Prefix); $var_name = rtrim($event->getPrefixSpecial() . '_selected_ids_' . $wid, '_'); $ret = $this->Application->RecallVar($var_name); } else { $ret = $this->Application->GetVar($event->getPrefixSpecial() . '_selected_ids'); } return explode(',', $ret); } /** * Stores IDs, selected in grid in session * * @param kEvent $event * @return void * @access protected */ protected function OnStoreSelected(kEvent $event) { $this->StoreSelectedIDs($event); $id = $this->Application->GetVar($event->getPrefixSpecial() . '_id'); if ( $id !== false ) { $event->SetRedirectParam($event->getPrefixSpecial() . '_id', $id); $event->SetRedirectParam('pass', 'all,' . $event->getPrefixSpecial()); } } /** * Returns associative array of submitted fields for current item * Could be used while creating/editing single item - * meaning on any edit form, except grid edit * * @param kEvent $event * @return Array * @access protected */ protected function getSubmittedFields(kEvent $event) { $items_info = $this->Application->GetVar($event->getPrefixSpecial(true)); $field_values = $items_info ? array_shift($items_info) : Array (); return $field_values; } /** * Removes any information about current/selected ids * from Application variables and Session * * @param kEvent $event * @return void * @access protected */ protected function clearSelectedIDs(kEvent $event) { $prefix_special = $event->getPrefixSpecial(); $ids = implode(',', $this->getSelectedIDs($event, true)); $event->setEventParam('ids', $ids); $wid = $this->Application->GetTopmostWid($event->Prefix); $session_name = rtrim($prefix_special . '_selected_ids_' . $wid, '_'); $this->Application->RemoveVar($session_name); $this->Application->SetVar($prefix_special . '_selected_ids', ''); $this->Application->SetVar($prefix_special . '_id', ''); // $event->getPrefixSpecial(true) . '_id' too may be } /** * Common builder part for Item & List * * @param kDBBase|kDBItem|kDBList $object * @param kEvent $event * @return void * @access protected */ protected function dbBuild(&$object, kEvent $event) { // for permission checking inside item/list build events $event->setEventParam('top_prefix', $this->Application->GetTopmostPrefix($event->Prefix, true)); if ( $event->getEventParam('form_name') !== false ) { $form_name = $event->getEventParam('form_name'); } else { $request_forms = $this->Application->GetVar('forms', Array ()); $form_name = (string)getArrayValue($request_forms, $object->getPrefixSpecial()); } $object->Configure($event->getEventParam('populate_ml_fields') || $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 <strong>checkItemStatus</strong>, leading to "404 Not Found"', E_USER_NOTICE); $this->Application->UrlManager->show404(); } /** * Builds item (loads if needed) * * Pattern: Prototype Manager * * @param kEvent $event * @access protected */ protected function OnItemBuild(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ $this->dbBuild($object, $event); $sql = $this->ItemPrepareQuery($event); $sql = $this->Application->ReplaceLanguageTags($sql); $object->setSelectSQL($sql); // 2. loads if allowed $auto_load = $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; + $id_from_request = $event->getEventParam(kEvent::FLAG_ID_FROM_REQUEST); - if ( $user_id != USER_ROOT && !$this->Application->isAdmin && !($editing_mode || $this->checkItemStatus($event)) ) { + if ( $user_id != USER_ROOT + && !$this->Application->isAdmin + && !($editing_mode || ($id_from_request ? $this->checkItemStatus($event) : true)) + ) { // non-root user AND on front-end AND (not editing mode || incorrect status) $perm_status = false; } } else { $perm_status = false; } if ( !$perm_status ) { // when no permission to view item -> redirect to no permission template $this->_processItemLoadingError($event, $status_checked); } } $actions = $this->Application->recallObject('kActions'); /* @var $actions Params */ $actions->Set($event->getPrefixSpecial() . '_GoTab', ''); $actions->Set($event->getPrefixSpecial() . '_GoId', ''); $actions->Set('forms[' . $event->getPrefixSpecial() . ']', $object->getFormName()); } /** * Processes case, when item wasn't loaded because of lack of permissions * * @param kEvent $event * @param bool $status_checked * @throws kNoPermissionException * @return void * @access protected */ protected function _processItemLoadingError($event, $status_checked) { $current_template = $this->Application->GetVar('t'); $redirect_template = $this->Application->isAdmin ? 'no_permission' : $this->Application->ConfigValue('NoPermissionTemplate'); $error_msg = 'ItemLoad Permission Failed for prefix [' . $event->getPrefixSpecial() . '] in <strong>' . ($status_checked ? 'checkItemStatus' : 'CheckPermission') . '</strong>'; if ( $current_template == $redirect_template ) { // don't perform "no_permission" redirect if already on a "no_permission" template if ( $this->Application->isDebugMode() ) { $this->Application->Debugger->appendTrace(); } trigger_error($error_msg, E_USER_NOTICE); return; } if ( MOD_REWRITE ) { $redirect_params = Array ( 'm_cat_id' => 0, 'next_template' => '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 "<strong>' . $event->getPrefixSpecial() . '</strong>" (' . $event_params . ')'); }*/ $this->dbBuild($object, $event); if ( !$object->isMainList() && $event->getEventParam('main_list') ) { // once list is set to main, then even "requery" parameter can't remove that /*$passed = $this->Application->GetVar('passed'); $this->Application->SetVar('passed', $passed . ',' . $event->Prefix);*/ $object->becameMain(); } $object->setGridName($event->getEventParam('grid')); $sql = $this->ListPrepareQuery($event); $sql = $this->Application->ReplaceLanguageTags($sql); $object->setSelectSQL($sql); $object->reset(); if ( $event->getEventParam('skip_parent_filter') === false ) { $object->linkToParent($this->getMainSpecial($event)); } $this->AddFilters($event); $this->SetCustomQuery($event); // new!, use this for dynamic queries based on specials for ex. $this->SetPagination($event); $this->SetSorting($event); $actions = $this->Application->recallObject('kActions'); /* @var $actions Params */ $actions->Set('remove_specials[' . $event->getPrefixSpecial() . ']', '0'); $actions->Set($event->getPrefixSpecial() . '_GoTab', ''); } /** * Returns special of main item for linking with sub-item * * @param kEvent $event * @return string * @access protected */ protected function getMainSpecial(kEvent $event) { $main_special = $event->getEventParam('main_special'); if ( $main_special === false ) { // main item's special not passed if ( substr($event->Special, -5) == '-item' ) { // temp handler added "-item" to given special -> process that here return substr($event->Special, 0, -5); } // by default subitem's special is used for main item searching return $event->Special; } return $main_special; } /** * Apply any custom changes to list's sql query * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetCustomQuery(kEvent $event) { } /** * Set's new per-page for grid * * @param kEvent $event * @return void * @access protected */ protected function OnSetPerPage(kEvent $event) { $per_page = $this->Application->GetVar($event->getPrefixSpecial(true) . '_PerPage'); $event->SetRedirectParam($event->getPrefixSpecial() . '_PerPage', $per_page); $event->SetRedirectParam('pass', 'all,' . $event->getPrefixSpecial()); if ( !$this->Application->isAdminUser ) { $list_helper = $this->Application->recallObject('ListHelper'); /* @var $list_helper ListHelper */ $this->_passListParams($event, 'per_page'); } } /** * Occurs when page is changed (only for hooking) * * @param kEvent $event * @return void * @access protected */ protected function OnSetPage(kEvent $event) { $page = $this->Application->GetVar($event->getPrefixSpecial(true) . '_Page'); $event->SetRedirectParam($event->getPrefixSpecial() . '_Page', $page); $event->SetRedirectParam('pass', 'all,' . $event->getPrefixSpecial()); if ( !$this->Application->isAdminUser ) { $this->_passListParams($event, 'page'); } } /** * Passes through main list pagination and sorting * * @param kEvent $event * @param string $skip_var * @return void * @access protected */ protected function _passListParams($event, $skip_var) { $param_names = array_diff(Array ('page', 'per_page', 'sort_by'), Array ($skip_var)); $list_helper = $this->Application->recallObject('ListHelper'); /* @var $list_helper ListHelper */ foreach ($param_names as $param_name) { $value = $this->Application->GetVar($param_name); switch ($param_name) { case 'page': if ( $value > 1 ) { $event->SetRedirectParam('page', $value); } break; case 'per_page': if ( $value > 0 ) { if ( $value != $list_helper->getDefaultPerPage($event->Prefix) ) { $event->SetRedirectParam('per_page', $value); } } break; case 'sort_by': $event->setPseudoClass('_List'); $object = $event->getObject(Array ('main_list' => 1)); /* @var $object kDBList */ if ( $list_helper->hasUserSorting($object) ) { $event->SetRedirectParam('sort_by', $value); } break; } } } /** * Set's correct page for list based on data provided with event * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetPagination(kEvent $event) { $object = $event->getObject(); /* @var $object kDBList */ // get PerPage (forced -> session -> config -> 10) $object->SetPerPage($this->getPerPage($event)); // main lists on Front-End have special get parameter for page $page = $object->isMainList() ? $this->Application->GetVar('page') : false; if ( !$page ) { // page is given in "env" variable for given prefix $page = $this->Application->GetVar($event->getPrefixSpecial() . '_Page'); } if ( !$page && $event->Special ) { // when not part of env, then variables like "prefix.special_Page" are // replaced (by PHP) with "prefix_special_Page", so check for that too $page = $this->Application->GetVar($event->getPrefixSpecial(true) . '_Page'); } if ( !$object->isMainList() ) { // main lists doesn't use session for page storing $this->Application->StoreVarDefault($event->getPrefixSpecial() . '_Page', 1, true); // true for optional if ( $page ) { // page found in request -> store in session $this->Application->StoreVar($event->getPrefixSpecial() . '_Page', $page, true); //true for optional } else { // page not found in request -> get from session $page = $this->Application->RecallVar($event->getPrefixSpecial() . '_Page'); } if ( !$event->getEventParam('skip_counting') ) { // when stored page is larger, then maximal list page number // (such case is also processed in kDBList::Query method) $pages = $object->GetTotalPages(); if ( $page > $pages ) { $page = 1; $this->Application->StoreVar($event->getPrefixSpecial() . '_Page', 1, true); } } } $object->SetPage($page); } /** * Returns current per-page setting for list * * @param kEvent $event * @return int * @access protected */ protected function getPerPage(kEvent $event) { $object = $event->getObject(); /* @var $object kDBList */ $per_page = $event->getEventParam('per_page'); if ( $per_page ) { // per-page is passed as tag parameter to PrintList, InitList, etc. $config_mapping = $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->setID($id); $object->SetFieldsFromHash($field_values); $event->setEventParam('form_data', $field_values); $this->customProcessing($event, 'before'); // look at kDBItem' Create for ForceCreateId description, it's rarely used and is NOT set by default if ( $object->Create($event->getEventParam('ForceCreateId')) ) { $this->customProcessing($event, 'after'); $event->SetRedirectParam('opener', 'u'); return; } $event->redirect = false; $event->status = kEvent::erFAIL; $this->Application->SetVar($event->getPrefixSpecial() . '_SaveEvent', 'OnCreate'); } /** * Updates kDBItem * * @param kEvent $event * @return void * @access protected */ protected function OnUpdate(kEvent $event) { if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $event->status = kEvent::erFAIL; return; } $this->_update($event); $event->SetRedirectParam('opener', 'u'); } /** * Updates data in database based on request * * @param kEvent $event * @return void * @access protected */ protected function _update(kEvent $event) { $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object kDBItem */ $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) ); if ( $items_info ) { foreach ($items_info as $id => $field_values) { $object->Load($id); $object->SetFieldsFromHash($field_values); $event->setEventParam('form_data', $field_values); $this->customProcessing($event, 'before'); if ( $object->Update($id) ) { $this->customProcessing($event, 'after'); $event->status = kEvent::erSUCCESS; } else { $event->status = kEvent::erFAIL; $event->redirect = false; break; } } } } /** * Delete's kDBItem object * * @param kEvent $event * @return void * @access protected */ protected function OnDelete(kEvent $event) { if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $event->status = kEvent::erFAIL; return; } $temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event)); /* @var $temp_handler kTempTablesHandler */ $temp_handler->DeleteItems($event->Prefix, $event->Special, Array ($this->getPassedID($event))); } /** * Deletes all records from table * * @param kEvent $event * @return void * @access protected */ protected function OnDeleteAll(kEvent $event) { $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); $event->setEventParam('form_data', $field_values); $this->customProcessing($event, 'before'); if ( $object->Create() ) { $this->customProcessing($event, 'after'); $event->SetRedirectParam($event->getPrefixSpecial(true) . '_id', $object->GetID()); } else { $event->status = kEvent::erFAIL; $event->redirect = false; } } /** * Reloads form to loose all changes made during item editing * * @param kEvent $event * @return void * @access protected */ protected function OnReset(kEvent $event) { //do nothing - should reset :) if ( $this->isNewItemCreate($event) ) { // just reset id to 0 in case it was create $object = $event->getObject( Array ('skip_autoload' => true) ); /* @var $object kDBItem */ $object->setID(0); $this->Application->SetVar($event->getPrefixSpecial() . '_id', 0); } } /** * Apply same processing to each item being selected in grid * * @param kEvent $event * @return void * @access protected */ protected function iterateItems(kEvent $event) { if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) { $event->status = kEvent::erFAIL; return ; } $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object kDBItem */ $ids = $this->StoreSelectedIDs($event); if ( $ids ) { $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);*/ + $this->Application->StoreVar('export_special', $event->Special); + $this->Application->StoreVar('export_grid', $this->Application->GetVar('grid', 'Default')); $redirect_params = Array ( $this->Prefix . '.export_event' => 'OnNew', 'pass' => 'all,' . $this->Prefix . '.export' ); $event->setRedirectParams($redirect_params); } /** * Apply some special processing to object being * recalled before using it in other events that * call prepareObject * * @param kDBItem|kDBList $object * @param kEvent $event * @return void * @access protected */ protected function prepareObject(&$object, kEvent $event) { if ( $event->Special == 'export' || $event->Special == 'import' ) { $export_helper = $this->Application->recallObject('CatItemExportHelper'); /* @var $export_helper kCatDBItemExportHelper */ $export_helper->prepareExportColumns($event); } } /** * Returns specific to each item type columns only * * @param kEvent $event * @return Array * @access public */ public function getCustomExportColumns(kEvent $event) { return Array (); } /** * Export form validation & processing * * @param kEvent $event * @return void * @access protected */ protected function OnExportBegin(kEvent $event) { $export_helper = $this->Application->recallObject('CatItemExportHelper'); /* @var $export_helper kCatDBItemExportHelper */ $export_helper->OnExportBegin($event); } /** * Enter description here... * * @param kEvent $event * @return void * @access protected */ protected function OnExportCancel(kEvent $event) { $this->OnGoBack($event); } /** * Allows configuring export options * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeExportBegin(kEvent $event) { } /** * Deletes export preset * * @param kEvent $event * @return void * @access protected */ protected function OnDeleteExportPreset(kEvent $event) { $field_values = $this->getSubmittedFields($event); if ( !$field_values ) { return ; } $preset_key = $field_values['ExportPresets']; $export_settings = $this->Application->RecallPersistentVar('export_settings'); if ( !$export_settings ) { return ; } $export_settings = unserialize($export_settings); if ( !isset($export_settings[$event->Prefix]) ) { return ; } $to_delete = ''; foreach ($export_settings[$event->Prefix] as $key => $val) { if ( implode('|', $val['ExportColumns']) == $preset_key ) { $to_delete = $key; break; } } if ( $to_delete ) { unset($export_settings[$event->Prefix][$to_delete]); $this->Application->StorePersistentVar('export_settings', serialize($export_settings)); } } /** * Saves changes & changes language * * @param kEvent $event * @return void * @access protected */ protected function OnPreSaveAndChangeLanguage(kEvent $event) { if ( $this->UseTempTables($event) ) { $event->CallSubEvent('OnPreSave'); } if ( $event->status == kEvent::erSUCCESS ) { $this->Application->SetVar('m_lang', $this->Application->GetVar('language')); $data = $this->Application->GetVar('st_id'); if ( $data ) { $event->SetRedirectParam('st_id', $data); } } } /** * Used to save files uploaded via 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); $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 '<suggestions>'; if ( kUtil::isAssoc($data) ) { foreach ($data as $key => $title) { echo '<item value="' . kUtil::escape($key, kUtil::ESCAPE_HTML) . '">' . kUtil::escape($title, kUtil::ESCAPE_HTML) . '</item>'; } } else { foreach ($data as $title) { echo '<item>' . kUtil::escape($title, kUtil::ESCAPE_HTML) . '</item>'; } } echo '</suggestions>'; } /** * 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 ('', '')); } } Index: branches/5.3.x/core/kernel/db/dbitem.php =================================================================== --- branches/5.3.x/core/kernel/db/dbitem.php (revision 16394) +++ branches/5.3.x/core/kernel/db/dbitem.php (revision 16395) @@ -1,1609 +1,1611 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); /** * DBItem * */ class kDBItem extends kDBBase { /** * Description * * @var array Associative array of current item' field values * @access protected */ protected $FieldValues = Array (); /** * Unformatted field values, before parse * * @var Array * @access protected */ protected $DirtyFieldValues = Array (); /** * Holds item values after loading (not affected by submit) * * @var Array * @access protected */ protected $OriginalFieldValues = Array (); /** * If set to true, Update will skip Validation before running * * @var array Associative array of current item' field values * @access public */ public $IgnoreValidation = false; /** * Remembers if object was loaded * * @var bool * @access protected */ protected $Loaded = false; /** * Holds item' primary key value * * @var int Value of primary key field for current item * @access protected */ protected $ID; /** * This object is used in cloning operations * * @var bool * @access public */ public $inCloning = false; /** * Validator object reference * * @var kValidator */ protected $validator = null; /** * Creates validator object, only when required * */ public function initValidator() { if ( !is_object($this->validator) ) { $validator_class = $this->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 Fields hash. * @param Array $set_fields Optional param, field names in target object to set, other fields will be skipped * * @return void */ 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 $set_fields * @return void * @access public */ public function SetDBFieldsFromHash($hash, $set_fields = Array ()) { if ( !$set_fields ) { $set_fields = array_keys($hash); } $set_fields = array_intersect($set_fields, array_keys($this->Fields)); foreach ($set_fields as $field_name) { $this->SetDBField($field_name, $hash[$field_name]); } } /** * Returns part of SQL WHERE clause identifying the record, ex. id = 25 * * @param string $method Child class may want to know who called GetKeyClause, Load(), Update(), Delete() send its names as method * @param Array $keys_hash alternative, then item id, keys hash to load item by * @see kDBItem::Load() * @see kDBItem::Update() * @see kDBItem::Delete() * @return string * @access protected */ protected function GetKeyClause($method = null, $keys_hash = null) { if ( !isset($keys_hash) ) { $keys_hash = Array ($this->IDField => $this->ID); } $ret = ''; foreach ($keys_hash as $field => $value) { $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) { + $this->Clear(); + 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(); + return false; } 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(); + return false; } 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()); 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($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'); // 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', 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('<b>Query</b> method is called in class <strong>' . get_class($this) . '</strong> for prefix <strong>' . $this->getPrefixSpecial() . '</strong>'); } protected function saveCustomFields() { if ( !$this->customFields || $this->inCloning ) { return true; } $cdata_key = rtrim($this->Prefix . '-cdata.' . $this->Special, '.'); $cdata = $this->Application->recallObject($cdata_key, null, Array ('skip_autoload' => true)); /* @var $cdata kDBItem */ $resource_id = $this->GetDBField('ResourceId'); $cdata->Load($resource_id, 'ResourceId'); $cdata->SetDBField('ResourceId', $resource_id); $ml_formatter = $this->Application->recallObject('kMultiLanguage'); /* @var $ml_formatter kMultiLanguage */ $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ $languages = $ml_helper->getLanguages(); foreach ($this->customFields as $custom_id => $custom_name) { $force_primary = $cdata->GetFieldOption('cust_' . $custom_id, 'force_primary'); if ( $force_primary ) { $cdata->SetDBField($ml_formatter->LangFieldName('cust_' . $custom_id, true), $this->GetDBField('cust_' . $custom_name)); } else { foreach ($languages as $language_id) { $cdata->SetDBField('l' . $language_id . '_cust_' . $custom_id, $this->GetDBField('l' . $language_id . '_cust_' . $custom_name)); } } } return $cdata->isLoaded() ? $cdata->Update() : $cdata->Create(); } /** * Returns specified field value from all selected rows. * Don't affect current record index * * @param string $field * @param bool $formatted * @param string $format * @return Array */ public function GetCol($field, $formatted = false, $format = null) { if ($formatted) { return Array (0 => $this->GetField($field, $format)); } return Array (0 => $this->GetDBField($field)); } /** * Set's loaded status of object * * @param bool $is_loaded * @access public * @todo remove this method, since item can't be marked as loaded externally */ public function setLoaded($is_loaded = true) { $this->Loaded = $is_loaded; } /** * 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; } } Index: branches/5.3.x/core/kernel/db/cat_event_handler.php =================================================================== --- branches/5.3.x/core/kernel/db/cat_event_handler.php (revision 16394) +++ branches/5.3.x/core/kernel/db/cat_event_handler.php (revision 16395) @@ -1,3090 +1,3106 @@ <?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 kCatDBEventHandler extends kDBEventHandler { /** * Allows to override standard permission mapping * * @return void * @access protected * @see kEventHandler::$permMapping */ protected function mapPermissions() { parent::mapPermissions(); $permissions = Array( 'OnSaveSettings' => 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'); + $export_events = array('OnSaveSettings', 'OnResetSettings', 'OnExportBegin'); + + if ( in_array($event->Name, $export_events) || ($event->Special == 'export' && $event->Name == 'OnNew') ) { + /** @var kPermissionsHelper $perm_helper */ + $perm_helper = $this->Application->recallObject('PermissionsHelper'); + $perm_value = $this->Application->CheckPermission('in-portal:main_import.view'); + + return $perm_helper->finalizePermissionCheck($event, $perm_value); } 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 + /** @var kSearchHelper $search_helper */ + $search_helper = $this->Application->recallObject('SearchHelper'); + + $search_sql = ' FROM ' . $search_helper->getSearchTable() . ' 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('<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 $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 = $this->Application->unescapeRequestVariable(trim($this->Application->GetVar('keywords'))); $query_object = $this->Application->recallObject('kHTTPQuery'); /* @var $query_object kHTTPQuery */ + /** @var kSearchHelper $search_helper */ + $search_helper = $this->Application->recallObject('SearchHelper'); + + $search_table = $search_helper->getSearchTable(); $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); + $search_helper->ensureEmptySearchTable(); $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 */ - + // Keyword string processing. $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)'; }*/ if ( count($positive_words) > 1 ) { $condition = $field . ' LIKE "%' . implode(' ', $positive_words) . '%"'; $relevance_parts[] = 'IF(' . $condition . ', ' . $weight_sum . ', 0)'; } // search by partial word matches too 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'; + /** @var kSearchHelper $search_helper */ + $search_helper = $this->Application->recallObject('SearchHelper'); + + $search_table = $search_helper->getSearchTable(); $sql = 'SHOW TABLES LIKE "' . $search_table . '"'; + $ids = array(); 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('kHTTPQuery'); /* @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('<br />', $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'; + /** @var kSearchHelper $search_helper */ + $search_helper = $this->Application->recallObject('SearchHelper'); + // Building final search query. + $search_table = $search_helper->getSearchTable(); $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); + $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] = $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] = $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] = $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); + extract($search_data, EXTR_SKIP); 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 */ $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 */ 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')); } 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->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)); } /** * 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 + // Don't allow creating records on behalf of another user. + if ( !$this->Application->isAdminUser && !defined('CRON') ) { + $object->SetDBField($owner_field, $object->GetOriginalField($owner_field)); + } + + // Auto-assign records to currently logged-in user. + if ( !$object->GetDBField($owner_field) ) { $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); $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); $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); $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); } } 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); } } $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()); } } } Index: branches/5.3.x/core/kernel/application.php =================================================================== --- branches/5.3.x/core/kernel/application.php (revision 16394) +++ branches/5.3.x/core/kernel/application.php (revision 16395) @@ -1,3070 +1,3092 @@ <?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. */ use InPortal\Core\kernel\Console\ConsoleApplication; defined('FULL_PATH') or die('restricted access!'); /** * Basic class for Kernel4-based Application * * This class is a Facade for any other class which needs to deal with Kernel4 framework.<br> * The class encapsulates the main run-cycle of the script, provide access to all other objects in the framework.<br> * <br> * The class is a singleton, which means that there could be only one instance of kApplication in the script.<br> * This could be guaranteed by NOT calling the class constructor directly, but rather calling kApplication::Instance() method, * which returns an instance of the application. The method guarantees that it will return exactly the same instance for any call.<br> * See singleton pattern by GOF. */ class kApplication implements kiCacheable { /** * Location of module helper class (used in installator too) */ const MODULE_HELPER_PATH = '/../units/helpers/modules_helper.php'; /** * Is true, when Init method was called already, prevents double initialization * * @var bool */ public $InitDone = false; /** * Holds internal NParser object * * @var NParser * @access public */ public $Parser; /** * Holds parser output buffer * * @var string * @access protected */ protected $HTML = ''; /** * The main Factory used to create * almost any class of kernel and * modules * * @var kFactory * @access protected */ protected $Factory; /** * Template names, that will be used instead of regular templates * * @var Array * @access public */ public $ReplacementTemplates = Array (); /** * Registered routers, that are used during url building and parsing. * * @var array */ public $routers = array(); /** * Reference to debugger * * @var Debugger * @access public */ public $Debugger = null; /** * Holds all phrases used * in code and template * * @var kPhraseCache * @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: * <code> * $application =& kApplication::Instance(); * </code> * or in an object: * <code> * $this->Application =& kApplication::Instance(); * </code> * to get the instance of kApplication. Note that we call the Instance method as STATIC - directly from the class. * To use descendant of standard kApplication class in your project you would need to define APPLICATION_CLASS constant * BEFORE calling kApplication::Instance() for the first time. If APPLICATION_CLASS is not defined the method would * create and return default KernelApplication instance. * * Pattern: Singleton * * @static * @return kApplication * @access public */ public static function &Instance() { static $instance = false; if ( !$instance ) { $class = defined('APPLICATION_CLASS') ? APPLICATION_CLASS : 'kApplication'; $instance = new $class(); } return $instance; } /** * Initializes the Application * * @param string $factory_class * @return bool Was Init actually made now or before * @access public * @see kHTTPQuery * @see Session * @see TemplatesCache */ public function Init($factory_class = 'kFactory') { if ( $this->InitDone ) { return false; } 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 routers. $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('kHTTPQuery'); $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(); 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') ) { + if ( !$this->RecallVar('curr_iso') && !(defined('IS_INSTALL') && IS_INSTALL) ) { $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')); + if ( PHP_SAPI !== 'cli' && !$this->isAdmin ) { + $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('kEventManager'); $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(); $this->Factory->configureAutoloader(); return; } // use makeClass over recallObject, since used before kApplication initialization during installation $modules_helper = $this->makeClass('kModulesHelper'); /* @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(); $this->Factory->configureAutoloader(); } /** * Checks if passed language id if valid and sets it to primary otherwise * * @return void * @access public */ public function VerifyLanguageId() { /** @var LanguagesItem $lang */ $lang = $this->recallObject('lang.current'); if ( !$lang->isLoaded() || (!$this->isAdmin && !$lang->GetDBField('Enabled')) ) { if ( !defined('IS_INSTALL') ) { $this->ApplicationDie('Unknown or disabled language'); } } } /** * 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; } /** @var ThemeItem $theme */ $theme = $this->recallObject('theme.current'); 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 classes, that are used before unit configs (where class registration usually is done) are read. * * Called automatically while initializing kApplication. * * @return void * @access public */ public function RegisterDefaultClasses() { // Database. $this->registerClass('IDBConnection', KERNEL_PATH . '/db/i_db_connection.php'); $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'); // Cache. $this->registerClass('kCacheManager', KERNEL_PATH . '/managers/cache_manager.php'); $this->registerClass('kCache', KERNEL_PATH . '/utility/cache.php'); + $this->registerClass('kSubscriptionItem', KERNEL_PATH . '/managers/subscription_manager.php'); // Unit configs. $this->registerClass('kUnitConfigReader', KERNEL_PATH . '/utility/unit_config_reader.php'); $this->registerClass('kUnitConfigCloner', KERNEL_PATH . '/utility/unit_config_cloner.php'); // Urls. $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'); // Events. $this->registerClass('kEventManager', KERNEL_PATH . '/event_manager.php'); $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'); // Misc. $this->registerClass('kPhraseCache', KERNEL_PATH . '/languages/phrases_cache.php'); $this->registerClass('kModulesHelper', KERNEL_PATH . self::MODULE_HELPER_PATH); // Aliased. $this->registerClass('Params', KERNEL_PATH . '/utility/params.php', 'kActions'); $this->registerClass('kMainTagProcessor', KERNEL_PATH . '/processors/main_processor.php', 'm_TagProcessor'); $this->registerClass('kEmailSendingHelper', KERNEL_PATH . '/utility/email_send.php', 'EmailSender'); } /** * Registers default build events * * @return void - * @access protected */ - protected function RegisterDefaultBuildEvents() + public 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('<strong>Design Template</strong>: ' . $t . '; <strong>CategoryID</strong>: ' . $this->GetVar('m_cat_id')); } } /*else { $cms_handler->SetCatByTemplate(); }*/ if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) { $this->Debugger->appendMemoryUsage('Application before Parsing:'); } $this->HTML = $this->Parser->Run($t); if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) { $this->Debugger->appendMemoryUsage('Application after Parsing:'); } } /** * Returns console application. * * @return ConsoleApplication */ public function getConsoleApplication() { return $this->makeClass('InPortal\Core\kernel\Console\ConsoleApplication'); } /** - * Only renders template + * Replaces current rendered template with given one. + * + * @param string|null $template Template. * - * @see kDBEventHandler::_errorNotFound() + * @return void */ - public function QuickRun() + public function QuickRun($template) { - // discard any half-parsed content - ob_clean(); + /** @var kThemesHelper $themes_helper */ + $themes_helper = $this->recallObject('ThemesHelper'); + + // Set Web Request variables to affect link building on template itself. + $this->SetVar('t', $template); + $this->SetVar('m_cat_id', $themes_helper->getPageByTemplate($template)); + $this->SetVar('passed', 'm'); - // replace current page content with 404 + // Replace current page content with given template. $this->InitParser(); - $this->HTML = $this->Parser->Run($this->GetVar('t')); + $this->Parser->Clear(); + $this->HTML = $this->Parser->Run($template); } /** * 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.<br> * * @param string $var Variable name to set * @param mixed $val Variable value * @return void * @access public */ public function SetVar($var,$val) { $this->HttpQuery->Set($var, $val); } /** * Deletes kHTTPQuery variable * * @param string $var * @return void * @todo Think about method name */ public function DeleteVar($var) { $this->HttpQuery->Remove($var); } /** * Deletes Session variable * * @param string $var * @return void * @access public */ public function RemoveVar($var) { $this->Session->RemoveVar($var); } /** * Removes variable from persistent session * * @param string $var * @return void * @access public */ public function RemovePersistentVar($var) { $this->Session->RemovePersistentVar($var); } /** * Restores Session variable to it's db version * * @param string $var * @return void * @access public */ public function RestoreVar($var) { $this->Session->RestoreVar($var); } /** * Returns session variable value * * Return value of $var variable stored in Session. An optional default value could be passed as second parameter. * * @param string $var Variable name * @param mixed $default Default value to return if no $var variable found in session * @return mixed * @access public * @see Session::RecallVar() */ public function RecallVar($var,$default=false) { return $this->Session->RecallVar($var,$default); } /** * Returns variable value from persistent session * * @param string $var * @param mixed $default * @return mixed * @access public * @see Session::RecallPersistentVar() */ public function RecallPersistentVar($var, $default = false) { return $this->Session->RecallPersistentVar($var, $default); } /** * Stores variable $val in session under name $var * * Use this method to store variable in session. Later this variable could be recalled. * * @param string $var Variable name * @param mixed $val Variable value * @param bool $optional * @return void * @access public * @see kApplication::RecallVar() */ public function StoreVar($var, $val, $optional = false) { $session = $this->recallObject('Session'); /* @var $session Session */ $this->Session->StoreVar($var, $val, $optional); } /** * Stores variable to persistent session * * @param string $var * @param mixed $val * @param bool $optional * @return void * @access public */ public function StorePersistentVar($var, $val, $optional = false) { $this->Session->StorePersistentVar($var, $val, $optional); } /** * Stores default value for session variable * * @param string $var * @param string $val * @param bool $optional * @return void * @access public * @see Session::RecallVar() * @see Session::StoreVar() */ public function StoreVarDefault($var, $val, $optional = false) { $session = $this->recallObject('Session'); /* @var $session Session */ $this->Session->StoreVarDefault($var, $val, $optional); } /** * Links HTTP Query variable with session variable * * If variable $var is passed in HTTP Query it is stored in session for later use. If it's not passed it's recalled from session. * This method could be used for making sure that GetVar will return query or session value for given * variable, when query variable should overwrite session (and be stored there for later use).<br> * This could be used for passing item's ID into popup with multiple tab - * in popup script you just need to call LinkVar('id', 'current_id') before first use of GetVar('id'). * After that you can be sure that GetVar('id') will return passed id or id passed earlier and stored in session * * @param string $var HTTP Query (GPC) variable name * @param mixed $ses_var Session variable name * @param mixed $default Default variable value * @param bool $optional * @return void * @access public */ public function LinkVar($var, $ses_var = null, $default = '', $optional = false) { if ( !isset($ses_var) ) { $ses_var = $var; } if ( $this->GetVar($var) !== false ) { $this->StoreVar($ses_var, $this->GetVar($var), $optional); } else { $this->SetVar($var, $this->RecallVar($ses_var, $default)); } } /** * Returns variable from HTTP Query, or from session if not passed in HTTP Query * * The same as LinkVar, but also returns the variable value taken from HTTP Query if passed, or from session if not passed. * Returns the default value if variable does not exist in session and was not passed in HTTP Query * * @param string $var HTTP Query (GPC) variable name * @param mixed $ses_var Session variable name * @param mixed $default Default variable value * @return mixed * @access public * @see LinkVar */ public function GetLinkedVar($var, $ses_var = null, $default = '') { $this->LinkVar($var, $ses_var, $default); return $this->GetVar($var); } /** * Renders given tag and returns it's output * * @param string $prefix * @param string $tag * @param Array $params * @return mixed * @access public * @see kApplication::InitParser() */ public function ProcessParsedTag($prefix, $tag, $params) { $processor = $this->Parser->GetProcessor($prefix); /* @var $processor kDBTagProcessor */ return $processor->ProcessParsedTag($tag, $params, $prefix); } /** * Return 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 '<strong>Debug output above !!!</strong><br/>' . "\n"; if ( array_key_exists('HTTP_REFERER', $_SERVER) ) { - echo 'Referer: <strong>' . $_SERVER['HTTP_REFERER'] . '</strong><br/>' . "\n"; + echo 'Referer: <strong>' . kUtil::escape($_SERVER['HTTP_REFERER'], kUtil::ESCAPE_HTML) . '</strong><br/>' . "\n"; } echo "Proceed to redirect: <a href=\"{$location}\">{$location}</a><br/>\n"; } else { if ( $js_redirect ) { // show "redirect" template instead of redirecting, // because "Set-Cookie" header won't work, when "Location" // header is used later $this->SetVar('t', 'redirect'); $this->SetVar('redirect_to', $location); // make all additional parameters available on "redirect" template too foreach ($params as $name => $value) { $this->SetVar($name, $value); } return; } else { if ( $this->GetVar('ajax') == 'yes' && ($t != $this->GetVar('t') || !$this->isSOPSafe($location, $t)) ) { // redirection to other then current template during ajax request OR SOP violation kUtil::safeDefine('DBG_SKIP_REPORTING', 1); echo '#redirect#' . $location; } elseif ( headers_sent() != '' ) { // some output occurred -> redirect using javascript - echo '<script type="text/javascript">window.location.href = \'' . $location . '\';</script>'; + echo '<script type="text/javascript">window.location.href = \'' . kUtil::escape($location, kUtil::ESCAPE_JS) . '\';</script>'; } else { // no output before -> redirect using HTTP header // header('HTTP/1.1 302 Found'); header('Location: ' . $location, true, $response_code); } } } // session expiration is called from session initialization, // that's why $this->Session may be not defined here $session = $this->recallObject('Session'); /* @var $session Session */ if ( $this->InitDone ) { // if redirect happened in the middle of application initialization don't call event, // that presumes that application was successfully initialized $this->HandleEvent(new kEvent('adm:OnBeforeShutdown')); } $session->SaveData(); ob_end_flush(); exit; } /** * Determines if real redirect should be made within AJAX request. * * @param string $url Location. * @param string $template Template. * * @return boolean * @link http://en.wikipedia.org/wiki/Same-origin_policy */ protected function isSOPSafe($url, $template) { $parsed_url = parse_url($url); if ( $parsed_url['scheme'] . '://' != PROTOCOL ) { return false; } if ( $parsed_url['host'] != SERVER_NAME ) { return false; } if ( defined('PORT') && isset($parsed_url['port']) && $parsed_url['port'] != PORT ) { return false; } return true; } /** * 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 Event. * * @return void */ public function HandleEvent(kEvent $event) { $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); } /** + * Finds the absolute path to the file where the class is defined. + * + * @param string $class The name of the class. + * + * @return string|false + */ + public function findClassFile($class) + { + return $this->Factory->findClassFile($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, array $event_params = array(), 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, array $arguments = array()) { return $this->Factory->makeClass($pseudo_class, $arguments); } /** * Returns sub-classes of given ancestor class. * * @param string $ancestor_class Ancestor class. * @param boolean $concrete_only Return only non-abstract classes. * * @return array */ public function getSubClasses($ancestor_class, $concrete_only = true) { return $this->Factory->getSubClasses($ancestor_class, $concrete_only); } /** * 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 = '') { while ( ob_get_level() ) { ob_end_clean(); } if ( $this->isDebugMode() ) { $message .= $this->Debugger->printReport(true); } $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 ? '<?xml version="' . $xml_version . '" encoding="' . CHARSET . '"?>' : ''; } /** * Returns category tree * * @param int $category_id * @return Array * @access public */ public function getTreeIndex($category_id) { $tree_index = $this->getCategoryCache($category_id, 'category_tree'); if ( $tree_index ) { $ret = Array (); list ($ret['TreeLeft'], $ret['TreeRight']) = explode(';', $tree_index); return $ret; } return false; } /** * Base category of all categories * Usually replaced category, with ID = 0 in category-related operations. * * @return int * @access public */ public function getBaseCategory() { // same, what $this->findModule('Name', 'Core', 'RootCat') does // don't cache while IS_INSTALL, because of kInstallToolkit::createModuleCategory and upgrade return $this->ModuleInfo['Core']['RootCat']; } /** * Deletes all data, that was cached during unit config parsing (excluding unit config locations) * * @param Array $config_variables * @access public */ public function DeleteUnitCache($config_variables = null) { $this->cacheManager->DeleteUnitCache($config_variables); } /** * Deletes cached section tree, used during permission checking and admin console tree display * * @return void * @access public */ public function DeleteSectionCache() { $this->cacheManager->DeleteSectionCache(); } /** * Sets data from cache to object * * @param Array $data * @access public */ public function setFromCache(&$data) { $this->Factory->setFromCache($data); $this->UnitConfigReader->setFromCache($data); $this->EventManager->setFromCache($data); $this->ReplacementTemplates = $data['Application.ReplacementTemplates']; $this->routers = $data['Application.Routers']; $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.Routers' => $this->routers, '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(); } } Index: branches/5.3.x/core/kernel/managers/url_manager.php =================================================================== --- branches/5.3.x/core/kernel/managers/url_manager.php (revision 16394) +++ branches/5.3.x/core/kernel/managers/url_manager.php (revision 16395) @@ -1,527 +1,500 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2010 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class kUrlManager extends kBase { /** * Processor of plain urls (initialized always) * * @var kPlainUrlProcessor * @access public */ public $plain = null; /** * Processor of rewritten urls (initialized on demand only) * * @var kRewriteUrlProcessor * @access public */ public $rewrite = null; /** * Tells, that rewrite system is ready * * @var bool * @access protected */ protected $rewriteReady = false; /** * Physical template name mapping to their template names based in "Structure & Data" section * * @var Array */ protected $structureTemplateMapping = Array (); /** * Creates instead of kUrlManager class */ public function __construct() { parent::__construct(); // don't use kApplication::recallObject, since it will call kApplication::EventManager, which isn't ready yet $this->plain = $this->Application->makeClass('kPlainUrlProcessor', Array (&$this)); } /** * Delay initialization of rewrite processor, since it uses site domains & http query * * @return void * @access public */ public function initRewrite() { if ( $this->rewriteReady ) { return; } $this->rewrite = $this->Application->recallObject('kRewriteUrlProcessor', null, Array (), Array (&$this)); $this->rewriteReady = true; } /** * Return href for template * * @param string $t Template path * @param string $prefix index.php prefix - could be blank, 'admin' * @param Array $params * @param string $index_file * @return string */ public function HREF($t, $prefix = '', $params = Array (), $index_file = null) { if ( !$t ) { // when template not specified, use current $t = $this->Application->GetVar('t'); } $t = preg_replace('/^Content\//i', '', $t); if ( substr($t, -4) == '.tpl' ) { // cut template extension (deprecated link format) $t = substr($t, 0, strlen($t) - 4); } if ( substr($t, 0, 3) == 'id:' ) { // link to structure page using it's id $params['m_cat_id'] = substr($t, 3); $t = $this->structureTemplateMapping[$t]; } if ( array_key_exists('use_section', $params) ) { $use_section = $params['use_section']; unset($params['use_section']); } if ( isset($use_section) && $use_section ) { $theme_id = isset($params['m_theme']) ? $params['m_theme'] : null; $t = $this->getSectionTemplate($t, $theme_id); } if ( preg_match('/external:(.*)/', $t, $regs) ) { // external url return $regs[1]; } if ( $this->Application->isAdmin && $prefix == '' ) { $prefix = ADMIN_DIRECTORY; } if ( $this->Application->isAdmin && $prefix == '_FRONT_END_' ) { $prefix = ''; } if ( isset($params['_auto_prefix_']) ) { unset($params['_auto_prefix_']); // this is parser-related param, do not need to pass it here } $ssl = isset($params['__SSL__']) ? $params['__SSL__'] : NULL; if ( $ssl !== NULL ) { $session = $this->Application->recallObject('Session'); /* @var $session Session */ $target_url = rtrim($this->Application->BaseURL('', $ssl), '/'); $cookie_url = trim($session->CookieDomain . $session->CookiePath, '/.'); // set session to GET_ONLY, to pass sid only if sid is REAL AND session is set if ( !preg_match('#' . preg_quote($cookie_url) . '#', $target_url) && $session->SessionSet ) { // when SSL<->NON-SSL redirect to different domain pass SID in url $session->SetMode(Session::smGET_ONLY); } } if ( isset($params['opener']) && $params['opener'] == 'u' ) { $ret = $this->processPopupClose($prefix, $params); if ( $ret !== false ) { return $ret; } else { //define('DBG_REDIRECT', 1); $t = $this->Application->GetVar('t'); } } $pass = isset($params['pass']) ? $params['pass'] : ''; // pass events with url $pass_events = false; if ( isset($params['pass_events']) ) { $pass_events = $params['pass_events']; unset($params['pass_events']); } $map_link = ''; if ( isset($params['anchor']) ) { $map_link = '#' . $params['anchor']; unset($params['anchor']); } $rewrite = true; if ( isset($params['__NO_REWRITE__']) ) { $rewrite = false; unset($params['__NO_REWRITE__']); } $force_rewrite = false; if ( isset($params['__MOD_REWRITE__']) ) { $force_rewrite = true; unset($params['__MOD_REWRITE__']); } $force_no_sid = false; if ( isset($params['__NO_SID__']) ) { $force_no_sid = true; unset($params['__NO_SID__']); } $domain = ''; if ( isset($params['domain']) ) { $domain = $params['domain']; unset($params['domain']); } // append pass through variables to each link to be build $params = array_merge($this->getPassThroughVariables($params), $params); $session = $this->Application->recallObject('Session'); if ( $session->NeedQueryString() && !$force_no_sid ) { $params['sid'] = $this->Application->GetSID(); } if ( $force_rewrite || ($this->Application->RewriteURLs($ssl) && $rewrite) ) { if ( !$this->rewriteReady ) { $this->initRewrite(); } $url = $this->rewrite->build($t, $params, $pass, $pass_events); } else { unset($params['pass_category']); // we don't need to pass it when mod_rewrite is off $index_file = $this->getIndexFile($prefix, $index_file, $params); $url = $index_file . '?' . $this->plain->build($t, $params, $pass, $pass_events); } if ( $prefix ) { $prefix = trim($prefix, '/') . '/'; } return $this->Application->BaseURL($domain, $ssl) . $prefix . $url . $map_link; } /** * Returns popup's parent window url and optionally removes it from opener stack * * @param string $prefix * @param Array $params * @return bool|string * @access protected */ protected function processPopupClose($prefix = '', $params = Array ()) { $opener_stack = $this->Application->makeClass('kOpenerStack'); /* @var $opener_stack kOpenerStack */ if ( $opener_stack->isEmpty() ) { return false; } $ssl = isset($params['__SSL__']) ? $params['__SSL__'] : null; list($index_file, $env) = explode('|', $opener_stack->get(kOpenerStack::LAST_ELEMENT, true)); if ( $prefix ) { $prefix = trim($prefix, '/') . '/'; } $ret = $this->Application->BaseURL('', $ssl) . $prefix . $index_file . '?' . ENV_VAR_NAME . '=' . $env; if ( isset($params['m_opener']) && $params['m_opener'] == 'u' ) { $opener_stack->pop(); $opener_stack->save(true); if ( $opener_stack->isEmpty() ) { // remove popups last templates, because popup is closing now $this->Application->RemoveVar('last_template_' . $opener_stack->getWindowID()); $this->Application->RemoveVar('last_template_popup_' . $opener_stack->getWindowID()); // don't save popups last templates again :) $this->Application->SetVar('skip_last_template', 1); } // store window relations /*$window_relations = $this->Application->RecallVar('window_relations'); $window_relations = $window_relations ? unserialize($window_relations) : Array (); if (array_key_exists($wid, $window_relations)) { unset($window_relations[$wid]); $this->Application->StoreVar('window_relations', serialize($window_relations)); }*/ } return $ret; } /** * Returns variables with values that should be passed through with this link + variable list * * @param Array $params * @return Array * @access public */ public function getPassThroughVariables(&$params) { static $cached_pass_through = null; if ( isset($params['no_pass_through']) && $params['no_pass_through'] ) { unset($params['no_pass_through']); return Array (); } // because pass through is not changed during script run, then we can cache it if ( is_null($cached_pass_through) ) { $cached_pass_through = Array (); $pass_through = $this->Application->GetVar('pass_through'); if ( $pass_through ) { // names of variables to pass to each link $cached_pass_through['pass_through'] = $pass_through; $pass_through = explode(',', $pass_through); foreach ($pass_through as $pass_through_var) { $cached_pass_through[$pass_through_var] = $this->Application->GetVar($pass_through_var); } } } return $cached_pass_through; } /** * Returns index file, that could be passed as parameter to method, as parameter to tag and as constant or not passed at all * * @param string $prefix * @param string $index_file * @param Array $params * @return string * @access protected */ protected function getIndexFile($prefix, $index_file = null, &$params) { static $cache = Array (); if ( isset($params['index_file']) ) { $index_file = $params['index_file']; unset($params['index_file']); return $index_file; } if ( isset($index_file) ) { return $index_file; } if ( defined('INDEX_FILE') ) { return INDEX_FILE; } // detect index file only once for given script and $cut_prefix $php_self = $_SERVER['PHP_SELF']; $cut_prefix = BASE_PATH . '/' . trim($prefix, '/'); if ( isset($cache[$php_self . ':' . $cut_prefix]) ) { return $cache[$php_self . ':' . $cut_prefix]; } $cache[$php_self . ':' . $cut_prefix] = trim(preg_replace('/' . preg_quote($cut_prefix, '/') . '(.*)/', '\\1', $php_self), '/'); return $cache[$php_self . ':' . $cut_prefix]; } /** * Returns theme template filename and it's corresponding page_id based on given seo template * * @param string $seo_template * @return string * @access public */ public function getPhysicalTemplate($seo_template) { $physical_template = false; $found_templates = array_keys($this->structureTemplateMapping, $seo_template); foreach ($found_templates as $found_template) { if ( substr($found_template, 0, 3) == 'id:' ) { // exclude virtual templates continue; } // several templates matched (physical and sym-linked to it) $physical_template = $found_template; } if ( $physical_template === false ) { // physical template from ".smsignore" file return $seo_template; } list ($physical_template,) = explode(':', $physical_template, 2); // template_path:theme_id => seo_template return $physical_template; } /** * Returns seo template by physical template * * @param string $physical_template * @return string * @access public */ public function getSeoTemplate($physical_template) { $mapping_key = $physical_template . ':' . $this->Application->GetVar('m_theme'); return isset($this->structureTemplateMapping[$mapping_key]) ? $this->structureTemplateMapping[$mapping_key] : ''; } /** * Returns template name, that corresponds with given virtual (not physical) page id * * @param int $page_id * @return string|bool * @access public */ public function getVirtualPageTemplate($page_id) { return isset($this->structureTemplateMapping['id:' . $page_id]) ? $this->structureTemplateMapping['id:' . $page_id] : false; } /** * 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) { static $current_theme_id = null; if ( !isset($current_theme_id) ) { $current_theme_id = $this->Application->GetVar('m_theme'); } if ( !isset($theme_id) ) { $theme_id = $current_theme_id; } if ( array_key_exists($template . ':' . $theme_id, $this->structureTemplateMapping) ) { // structure template corresponding to given physical template return $this->structureTemplateMapping[$template . ':' . $theme_id]; } return $template; } /** * Loads template mapping for Front-End * * @return void * @access public */ public function LoadStructureTemplateMapping() { if (!$this->Application->isAdmin) { $category_helper = $this->Application->recallObject('CategoryHelper'); /* @var $category_helper CategoryHelper */ $this->structureTemplateMapping = $category_helper->getTemplateMapping(); } } /** * Removes tpl part from template name + resolved template ID to name * * @param string $default_template * @return string * @access public */ public function getTemplateName($default_template = '') { if ( $this->Application->GetVarDirect('t', 'Get') !== false ) { // template name is passed directly in url (GET method) $t = $this->Application->GetVarDirect('t', 'Get'); } elseif ( $this->Application->GetVar('env') && $this->Application->RewriteURLs() && $this->Application->GetVar('t') ) { // if t was set through env, even in mod_rewrite mode! $t = $this->Application->GetVar('t'); } else { $t = trim($default_template ? $default_template : 'index', '/'); } return trim(preg_replace('/\.tpl$/', '', $t), '/'); } /** - * Prepares case, when requested page wasn't found - * - * @param int $theme_id - * @return array - */ - public function prepare404($theme_id = null) - { - if ( !isset($theme_id) ) { - $theme_id = $this->Application->GetVar('m_theme'); - } - - $not_found = $this->Application->ConfigValue('ErrorTemplate'); - $vars['t'] = $not_found ? $not_found : 'error_notfound'; - - $themes_helper = $this->Application->recallObject('ThemesHelper'); - /* @var $themes_helper kThemesHelper */ - - $vars['m_cat_id'] = $themes_helper->getPageByTemplate($vars['t'], $theme_id); - - header('HTTP/1.0 404 Not Found'); - - return $vars; - } - - /** * Show 404 page and exit * * @return void * @access public */ public function show404() { - $vars = $this->prepare404(); + header('HTTP/1.0 404 Not Found'); - foreach ($vars as $var_name => $var_value) { - $this->Application->SetVar($var_name, $var_value); - } + $not_found = $this->Application->ConfigValue('ErrorTemplate'); + $template = $not_found ? $not_found : 'error_notfound'; - // ensure parser is available (e.g. 404 page requested from event) - $this->Application->QuickRun(); + $this->Application->QuickRun($template); $this->Application->Done(); exit; } -} \ No newline at end of file +} Index: branches/5.3.x/core/kernel/managers/cache_manager.php =================================================================== --- branches/5.3.x/core/kernel/managers/cache_manager.php (revision 16394) +++ branches/5.3.x/core/kernel/managers/cache_manager.php (revision 16395) @@ -1,852 +1,853 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2011 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class kCacheManager extends kBase implements kiCacheable { /** * Used variables from SystemSettings table * * @var Array * @access protected */ protected $configVariables = Array(); /** * Used variables from SystemSettings table retrieved from unit cache * * @var Array * @access protected */ protected $originalConfigVariables = Array (); /** * IDs of config variables used in current run (for caching) * * @var Array * @access protected */ protected $configIDs = Array (); /** * IDs of config variables retrieved from unit cache * * @var Array * @access protected */ protected $originalConfigIDs = Array (); /** * Object of memory caching class * * @var kCache * @access protected */ protected $cacheHandler = null; protected $temporaryCache = Array ( 'registerAggregateTag' => Array (), 'registerScheduledTask' => Array (), 'registerHook' => Array (), 'registerBuildEvent' => Array (), 'registerAggregateTag' => Array (), ); /** * Name of database table, where configuration settings are stored * * @var string * @access protected */ protected $settingTableName = ''; /** * Set's references to kApplication and DBConnection interface class instances * * @access public */ public function __construct() { parent::__construct(); $this->settingTableName = TABLE_PREFIX . 'SystemSettings'; if ( defined('IS_INSTALL') && IS_INSTALL ) { // table substitution required, so "root" can perform login to upgrade to 5.2.0, where setting table was renamed if ( !$this->Application->TableFound(TABLE_PREFIX . 'SystemSettings') ) { $this->settingTableName = TABLE_PREFIX . 'ConfigurationValues'; } } } /** * Creates caching manager instance * * @access public */ public function InitCache() { $this->cacheHandler = $this->Application->makeClass('kCache'); } /** * Returns cache key, used to cache phrase and configuration variable IDs used on current page * * @return string * @access protected */ protected function getCacheKey() { // TODO: maybe language part isn't required, since same phrase from different languages have one ID now return $this->Application->GetVar('t') . $this->Application->GetVar('m_theme') . $this->Application->GetVar('m_lang') . $this->Application->isAdmin; } /** * Loads phrases and configuration variables, that were used on this template last time * * @access public */ public function LoadApplicationCache() { $phrase_ids = $config_ids = Array (); $sql = 'SELECT PhraseList, ConfigVariables FROM ' . TABLE_PREFIX . 'PhraseCache WHERE Template = ' . $this->Conn->qstr( md5($this->getCacheKey()) ); $res = $this->Conn->GetRow($sql); if ($res) { if ( $res['PhraseList'] ) { $phrase_ids = explode(',', $res['PhraseList']); } if ( $res['ConfigVariables'] ) { $config_ids = array_diff( explode(',', $res['ConfigVariables']), $this->originalConfigIDs); } } $this->Application->Phrases->Init('phrases', '', null, $phrase_ids); $this->configIDs = $this->originalConfigIDs = $config_ids; $this->InitConfig(); } /** * Updates phrases and configuration variables, that were used on this template * * @access public */ public function UpdateApplicationCache() { $update = false; //something changed $update = $update || $this->Application->Phrases->NeedsCacheUpdate(); $update = $update || (count($this->configIDs) && $this->configIDs != $this->originalConfigIDs); if ($update) { $fields_hash = Array ( 'PhraseList' => implode(',', $this->Application->Phrases->Ids), 'CacheDate' => time(), 'Template' => md5( $this->getCacheKey() ), 'ConfigVariables' => implode(',', array_unique($this->configIDs)), ); $this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'PhraseCache', 'REPLACE'); } } /** * Loads configuration variables, that were used on this template last time * * @access protected */ protected function InitConfig() { if (!$this->originalConfigIDs) { return ; } $sql = 'SELECT VariableValue, VariableName FROM ' . $this->settingTableName . ' WHERE VariableId IN (' . implode(',', $this->originalConfigIDs) . ')'; $config_variables = $this->Conn->GetCol($sql, 'VariableName'); $this->configVariables = array_merge($this->configVariables, $config_variables); } /** * Returns configuration option value by name * * @param string $name * @return string * @access public */ public function ConfigValue($name) { $site_domain_override = Array ( 'DefaultEmailSender' => 'AdminEmail', 'DefaultEmailRecipients' => 'DefaultEmailRecipients', ); if ( isset($site_domain_override[$name]) ) { $res = $this->Application->siteDomainField($site_domain_override[$name]); if ( $res ) { return $res; } } if ( array_key_exists($name, $this->configVariables) ) { return $this->configVariables[$name]; } if ( defined('IS_INSTALL') && IS_INSTALL && !$this->Application->TableFound($this->settingTableName, true) ) { return false; } $this->Conn->nextQueryCachable = true; $sql = 'SELECT VariableId, VariableValue FROM ' . $this->settingTableName . ' WHERE VariableName = ' . $this->Conn->qstr($name); $res = $this->Conn->GetRow($sql); if ( $res !== false ) { $this->configIDs[] = $res['VariableId']; $this->configVariables[$name] = $res['VariableValue']; return $res['VariableValue']; } trigger_error('Usage of undefined configuration variable "<strong>' . $name . '</strong>"', E_USER_NOTICE); return false; } /** * Changes value of individual configuration variable (+resets cache, when needed) * * @param string $name * @param string $value * @param bool $local_cache_only * @return string * @access public */ public function SetConfigValue($name, $value, $local_cache_only = false) { $this->configVariables[$name] = $value; if ( $local_cache_only ) { return; } $fields_hash = Array ('VariableValue' => $value); $this->Conn->doUpdate($fields_hash, $this->settingTableName, 'VariableName = ' . $this->Conn->qstr($name)); if ( array_key_exists($name, $this->originalConfigVariables) && $value != $this->originalConfigVariables[$name] ) { $this->DeleteUnitCache(); } } /** * Loads data, that was cached during unit config parsing * * @return bool * @access public */ public function LoadUnitCache() { if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) { $data = $this->Application->getCache('master:configs_parsed', false, CacheSettings::$unitCacheRebuildTime); } else { $data = $this->Application->getDBCache('configs_parsed', CacheSettings::$unitCacheRebuildTime); } if ( $data ) { $cache = unserialize($data); // 126 KB all modules unset($data); $this->Application->InitManagers(); $this->Application->setFromCache($cache); $aggregator = $this->Application->recallObject('TagsAggregator', 'kArray'); /* @var $aggregator kArray */ $aggregator->setFromCache($cache); $this->setFromCache($cache); unset($cache); return true; } return false; } /** * Empties factory and event manager cache (without storing changes) */ public function EmptyUnitCache() { // maybe discover keys automatically from corresponding classes $cache_keys = Array ( 'Factory.Files', 'Factory.ClassInfo', 'Factory.ClassTree', 'Factory.Namespaces', 'Factory.realClasses', 'ConfigReader.prefixFiles', 'ConfigCloner.clones', 'EventManager.beforeHooks', 'EventManager.afterHooks', 'EventManager.scheduledTasks', 'EventManager.buildEvents', 'Application.ReplacementTemplates', 'Application.Routers', 'Application.ModuleInfo', 'Application.ConfigHash', 'Application.ConfigCacheIds', ); $empty_cache = Array (); foreach ($cache_keys as $cache_key) { $empty_cache[$cache_key] = Array (); } $this->Application->setFromCache($empty_cache); $this->setFromCache($empty_cache); // Otherwise kModulesHelper indirectly used from includeConfigFiles won't work. $this->Application->RegisterDefaultClasses(); + $this->Application->RegisterDefaultBuildEvents(); } /** * Updates data, that was parsed from unit configs this time * * @access public */ public function UpdateUnitCache() { $aggregator = $this->Application->recallObject('TagsAggregator', 'kArray'); /* @var $aggregator kArray */ $this->preloadConfigVars(); // preloading will put to cache $cache = array_merge( $this->Application->getToCache(), $aggregator->getToCache(), $this->getToCache() ); $cache_rebuild_by = SERVER_NAME . ' (' . $this->Application->getClientIp() . ') - ' . date('d/m/Y H:i:s'); if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) { $this->Application->setCache('master:configs_parsed', serialize($cache)); $this->Application->setCache('master:last_cache_rebuild', $cache_rebuild_by); } else { $this->Application->setDBCache('configs_parsed', serialize($cache)); $this->Application->setDBCache('last_cache_rebuild', $cache_rebuild_by); } } public function delayUnitProcessing($method, $params) { if ($this->Application->InitDone) { // init already done -> call immediately (happens during installation) $function = Array (&$this->Application, $method); call_user_func_array($function, $params); return ; } $this->temporaryCache[$method][] = $params; } public function applyDelayedUnitProcessing() { foreach ($this->temporaryCache as $method => $method_calls) { $function = Array (&$this->Application, $method); foreach ($method_calls as $method_call) { call_user_func_array($function, $method_call); } $this->temporaryCache[$method] = Array (); } } /** * Deletes all data, that was cached during unit config parsing (excluding unit config locations) * * @param Array $config_variables * @access public */ public function DeleteUnitCache($config_variables = null) { if ( isset($config_variables) && !array_intersect(array_keys($this->originalConfigVariables), $config_variables) ) { // prevent cache reset, when given config variables are not in unit cache return; } if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) { $this->Application->rebuildCache('master:configs_parsed', kCache::REBUILD_LATER, CacheSettings::$unitCacheRebuildTime); } else { $this->rebuildDBCache('configs_parsed', kCache::REBUILD_LATER, CacheSettings::$unitCacheRebuildTime); } } /** * Deletes cached section tree, used during permission checking and admin console tree display * * @return void * @access public */ public function DeleteSectionCache() { if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) { $this->Application->rebuildCache('master:sections_parsed', kCache::REBUILD_LATER, CacheSettings::$sectionsParsedRebuildTime); } else { $this->rebuildDBCache('sections_parsed', kCache::REBUILD_LATER, CacheSettings::$sectionsParsedRebuildTime); } } /** * Preloads 21 widely used configuration variables, so they will get to cache for sure * * @access protected */ protected function preloadConfigVars() { $config_vars = Array ( // session related 'SessionTimeout', 'SessionCookieName', 'SessionCookieDomains', 'SessionBrowserSignatureCheck', 'SessionIPAddressCheck', 'CookieSessions', 'KeepSessionOnBrowserClose', 'User_GuestGroup', 'User_LoggedInGroup', 'RegistrationUsernameRequired', // output related 'UseModRewrite', 'UseContentLanguageNegotiation', 'UseOutputCompression', 'OutputCompressionLevel', 'Config_Site_Time', 'SystemTagCache', 'DefaultGridPerPage', // tracking related 'UseChangeLog', 'UseVisitorTracking', 'ModRewriteUrlEnding', 'ForceModRewriteUrlEnding', 'RunScheduledTasksFromCron', ); $escaped_config_vars = $this->Conn->qstrArray($config_vars); $sql = 'SELECT VariableId, VariableName, VariableValue FROM ' . $this->settingTableName . ' WHERE VariableName IN (' . implode(',', $escaped_config_vars) . ')'; $data = $this->Conn->Query($sql, 'VariableId'); foreach ($data as $variable_id => $variable_info) { $this->configIDs[] = $variable_id; $this->configVariables[ $variable_info['VariableName'] ] = $variable_info['VariableValue']; } } /** * Sets data from cache to object * * Used for cases, when ConfigValue is called before LoadApplicationCache method (e.g. session init, url engine init) * * @param Array $data * @access public */ public function setFromCache(&$data) { $this->configVariables = $this->originalConfigVariables = $data['Application.ConfigHash']; $this->configIDs = $this->originalConfigIDs = $data['Application.ConfigCacheIds']; } /** * Gets object data for caching * The following caches should be reset based on admin interaction (adjusting config, enabling modules etc) * * @access public * @return Array */ public function getToCache() { return Array ( 'Application.ConfigHash' => $this->configVariables, 'Application.ConfigCacheIds' => $this->configIDs, // not in use, since it only represents template specific values, not global ones // 'Application.Caches.ConfigVariables' => $this->originalConfigIDs, ); } /** * Returns caching type (none, memory, temporary) * * @param int $caching_type * @return bool * @access public */ public function isCachingType($caching_type) { return $this->cacheHandler->getCachingType() == $caching_type; } /** * Returns cached $key value from cache named $cache_name * * @param int $key key name from cache * @param bool $store_locally store data locally after retrieved * @param int $max_rebuild_seconds * @return mixed * @access public */ public function getCache($key, $store_locally = true, $max_rebuild_seconds = 0) { return $this->cacheHandler->getCache($key, $store_locally, $max_rebuild_seconds); } /** * 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->cacheHandler->setCache($key, $value, $expiration); } /** * Stores new $value in cache with $key name (only if not there already) * * @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->cacheHandler->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->cacheHandler->rebuildCache($name, $mode, $max_rebuilding_time); } /** * Deletes key from cache * * @param string $key * @return void * @access public */ public function deleteCache($key) { $this->cacheHandler->delete($key); } /** * Reset's all memory cache at once * * @return void * @access public */ public function resetCache() { $this->cacheHandler->reset(); } /** * Returns value from database cache * * @param string $name key name * @param int $max_rebuild_seconds * @return mixed * @access public */ public function getDBCache($name, $max_rebuild_seconds = 0) { // no serials in cache key OR cache is outdated $rebuilding = false; $wait_seconds = $max_rebuild_seconds; while (true) { $cached_data = $this->_getDBCache(Array ($name, $name . '_rebuilding', $name . '_rebuild')); if ( $cached_data[$name . '_rebuild'] ) { // cache rebuild requested -> rebuild now $this->deleteDBCache($name . '_rebuild'); if ( $this->rebuildDBCache($name, kCache::REBUILD_NOW, $max_rebuild_seconds, '[M1]') ) { return false; } } $cache = $cached_data[$name]; $rebuilding = $cached_data[$name . '_rebuilding']; if ( ($cache === false) && (!$rebuilding || $wait_seconds == 0) ) { // cache missing and nobody rebuilding it -> rebuild; enough waiting for cache to be ready $this->rebuildDBCache($name, kCache::REBUILD_NOW, $max_rebuild_seconds, '[M2' . ($rebuilding ? 'R' : '!R') . ',WS=' . $wait_seconds . ']'); return false; } elseif ( $cache !== false ) { // cache present -> return it $this->cacheHandler->storeStatistics($name, $rebuilding ? 'h' : 'H'); return $cache; } $wait_seconds -= kCache::WAIT_STEP; sleep(kCache::WAIT_STEP); } $this->rebuildDBCache($name, kCache::REBUILD_NOW, $max_rebuild_seconds, '[M3' . ($rebuilding ? 'R' : '!R') . ',WS=' . $wait_seconds . ']'); return false; } /** * Returns value from database cache * * @param string|Array $names key name * @return mixed * @access protected */ protected function _getDBCache($names) { $res = Array (); $names = (array)$names; $this->Conn->nextQueryCachable = true; $sql = 'SELECT Data, Cached, LifeTime, VarName FROM ' . TABLE_PREFIX . 'SystemCache WHERE VarName IN (' . implode(',', $this->Conn->qstrArray($names)) . ')'; $cached_data = $this->Conn->Query($sql, 'VarName'); foreach ($names as $name) { if ( !isset($cached_data[$name]) ) { $res[$name] = false; continue; } $lifetime = (int)$cached_data[$name]['LifeTime']; // in seconds if ( ($lifetime > 0) && ($cached_data[$name]['Cached'] + $lifetime < time()) ) { // delete expired $this->Conn->nextQueryCachable = true; $sql = 'DELETE FROM ' . TABLE_PREFIX . 'SystemCache WHERE VarName = ' . $this->Conn->qstr($name); $this->Conn->Query($sql); $res[$name] = false; continue; } $res[$name] = $cached_data[$name]['Data']; } return count($res) == 1 ? array_pop($res) : $res; } /** * 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->cacheHandler->storeStatistics($name, 'WU'); $this->deleteDBCache($name . '_rebuilding'); $this->_setDBCache($name, $value, $expiration); } /** * Sets value to database cache * * @param string $name * @param mixed $value * @param int|bool $expiration * @param string $insert_type * @return bool * @access protected */ protected function _setDBCache($name, $value, $expiration = false, $insert_type = 'REPLACE') { if ( (int)$expiration <= 0 ) { $expiration = -1; } $fields_hash = Array ( 'VarName' => $name, 'Data' => &$value, 'Cached' => time(), 'LifeTime' => (int)$expiration, ); $this->Conn->nextQueryCachable = true; return $this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'SystemCache', $insert_type); } /** * Sets value to database cache * * @param string $name * @param mixed $value * @param int|bool $expiration * @return bool * @access protected */ protected function _addDBCache($name, $value, $expiration = false) { return $this->_setDBCache($name, $value, $expiration, 'INSERT'); } /** * Sets rebuilding mode for given cache * * @param string $name * @param int $mode * @param int $max_rebuilding_time * @param string $miss_type * @return bool * @access public */ public function rebuildDBCache($name, $mode = null, $max_rebuilding_time = 0, $miss_type = 'M') { if ( !isset($mode) || $mode == kCache::REBUILD_NOW ) { $this->cacheHandler->storeStatistics($name, $miss_type); if ( !$max_rebuilding_time ) { return true; } if ( !$this->_addDBCache($name . '_rebuilding', 1, $max_rebuilding_time) ) { $this->cacheHandler->storeStatistics($name, 'l'); return false; } $this->deleteDBCache($name . '_rebuild'); $this->cacheHandler->storeStatistics($name, 'L'); } elseif ( $mode == kCache::REBUILD_LATER ) { $this->_setDBCache($name . '_rebuild', 1, 0); $this->deleteDBCache($name . '_rebuilding'); } return true; } /** * Deletes key from database cache * * @param string $name * @return void * @access public */ public function deleteDBCache($name) { $sql = 'DELETE FROM ' . TABLE_PREFIX . 'SystemCache WHERE VarName = ' . $this->Conn->qstr($name); $this->Conn->Query($sql); } /** * Increments serial based on prefix and it's ID (optional) * * @param string $prefix * @param int $id ID (value of IDField) or ForeignKeyField:ID * @param bool $increment * @return string * @access public */ public function incrementCacheSerial($prefix, $id = null, $increment = true) { $pascal_case_prefix = implode('', array_map('ucfirst', explode('-', $prefix))); $serial_name = $pascal_case_prefix . (isset($id) ? 'IDSerial:' . $id : 'Serial'); if ($increment) { if (defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode()) { $this->Application->Debugger->appendHTML('Incrementing serial: <strong>' . $serial_name . '</strong>.'); } $this->setCache($serial_name, (int)$this->getCache($serial_name) + 1); if (!defined('IS_INSTALL') || !IS_INSTALL) { // delete cached mod-rewrite urls related to given prefix and id $delete_clause = isset($id) ? $prefix . ':' . $id : $prefix; $sql = 'DELETE FROM ' . TABLE_PREFIX . 'CachedUrls WHERE Prefixes LIKE ' . $this->Conn->qstr('%|' . $delete_clause . '|%'); $this->Conn->Query($sql); } } return $serial_name; } /** * Returns cached category informaton by given cache name. All given category * information is recached, when at least one of 4 caches is missing. * * @param int $category_id * @param string $name cache name = {filenames, category_designs, category_tree} * @return string * @access public */ public function getCategoryCache($category_id, $name) { $serial_name = '[%CIDSerial:' . $category_id . '%]'; $cache_key = $name . $serial_name; $ret = $this->getCache($cache_key); if ($ret === false) { if (!$category_id) { // don't query database for "Home" category (ID = 0), because it doesn't exist in database return false; } // this allows to save 2 sql queries for each category $this->Conn->nextQueryCachable = true; $sql = 'SELECT NamedParentPath, CachedTemplate, TreeLeft, TreeRight FROM ' . TABLE_PREFIX . 'Categories WHERE CategoryId = ' . (int)$category_id; $category_data = $this->Conn->GetRow($sql); if ($category_data !== false) { // only direct links to category pages work (symlinks, container pages and so on won't work) $this->setCache('filenames' . $serial_name, $category_data['NamedParentPath']); $this->setCache('category_designs' . $serial_name, ltrim($category_data['CachedTemplate'], '/')); $this->setCache('category_tree' . $serial_name, $category_data['TreeLeft'] . ';' . $category_data['TreeRight']); } } return $this->getCache($cache_key); } } Index: branches/5.3.x/core/kernel/managers/rewrite_url_processor.php =================================================================== --- branches/5.3.x/core/kernel/managers/rewrite_url_processor.php (revision 16394) +++ branches/5.3.x/core/kernel/managers/rewrite_url_processor.php (revision 16395) @@ -1,1060 +1,1069 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2011 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class kRewriteUrlProcessor extends kUrlProcessor { /** * Holds a reference to httpquery * * @var kHttpQuery * @access protected */ protected $HTTPQuery = null; /** * Urls parts, that needs to be matched by routers. * * @var array */ protected $_partsToParse = array(); /** * Category item prefix, that was found * * @var string|boolean * @access protected */ protected $modulePrefix = false; /** * Template aliases for current theme * * @var Array * @access protected */ protected $_templateAliases = null; /** * Domain-based primary language id * * @var int * @access public */ public $primaryLanguageId = false; /** * Domain-based primary theme id * * @var int * @access public */ public $primaryThemeId = false; /** * Possible url endings from ModRewriteUrlEnding configuration variable * * @var Array * @access protected */ protected $_urlEndings = Array ('.html', '/', ''); /** * Factory storage sub-set, containing routers, used during url building and parsing. * * @var array */ protected $routers = array(); /** * Constructor of kRewriteUrlProcessor class * * @param $manager * @return kRewriteUrlProcessor */ public function __construct(&$manager) { parent::__construct($manager); $this->HTTPQuery = $this->Application->recallObject('kHTTPQuery'); // domain based primary language $this->primaryLanguageId = $this->Application->siteDomainField('PrimaryLanguageId'); if (!$this->primaryLanguageId) { // when domain-based language not found -> use site-wide language $this->primaryLanguageId = $this->Application->GetDefaultLanguageId(); } // domain based primary theme $this->primaryThemeId = $this->Application->siteDomainField('PrimaryThemeId'); if (!$this->primaryThemeId) { // when domain-based theme not found -> use site-wide theme $this->primaryThemeId = $this->Application->GetDefaultThemeId(true); } $this->initRouters(); } /** * Sets module prefix. * * @param string $prefix Unit config prefix. * * @return void */ public function setModulePrefix($prefix) { $this->modulePrefix = $prefix; } /** * Parses url * * @return void */ public function parseRewriteURL() { $url = $this->Application->GetVar('_mod_rw_url_'); if ( $url ) { $this->_redirectToDefaultUrlEnding($url); $url = $this->_removeUrlEnding($url); } $cached = $this->_getCachedUrl($url); if ( $cached !== false ) { $vars = $cached['vars']; $passed = $cached['passed']; } else { $vars = $this->parse($url); $passed = $vars['pass']; // also used in bottom of this method unset($vars['pass']); if ( !$this->_partsToParse ) { // don't cache 404 Not Found $this->_setCachedUrl($url, Array ('vars' => $vars, 'passed' => $passed)); } + else { + header('HTTP/1.0 404 Not Found'); + } if ( $this->Application->GetVarDirect('t', 'Post') ) { // template from POST overrides template from URL. $vars['t'] = $this->Application->GetVarDirect('t', 'Post'); if ( isset($vars['is_virtual']) && $vars['is_virtual'] ) { $vars['m_cat_id'] = 0; // this is virtual template category (for Proj-CMS) } } unset($vars['is_virtual']); } foreach ($vars as $name => $value) { $this->HTTPQuery->Set($name, $value); } $this->_initAll(); // also will use parsed language to load phrases from it $this->HTTPQuery->finalizeParsing($passed); } /** * Detects url ending of given url * * @param string $url * @return string * @access protected */ protected function _findUrlEnding($url) { if ( !$url ) { return ''; } foreach ($this->_urlEndings as $url_ending) { if ( mb_substr($url, mb_strlen($url) - mb_strlen($url_ending)) == $url_ending ) { return $url_ending; } } return ''; } /** * Removes url ending from url * * @param string $url * @return string * @access protected */ protected function _removeUrlEnding($url) { $url_ending = $this->_findUrlEnding($url); if ( !$url_ending ) { return $url; } return mb_substr($url, 0, mb_strlen($url) - mb_strlen($url_ending)); } /** * Redirects user to page with default url ending, where needed * * @param string $url * @return void * @access protected */ protected function _redirectToDefaultUrlEnding($url) { $default_ending = $this->Application->ConfigValue('ModRewriteUrlEnding'); if ( $this->_findUrlEnding($url) == $default_ending || !$this->Application->ConfigValue('ForceModRewriteUrlEnding') ) { return; } // user manually typed url with different url ending -> redirect to same url with default url ending $target_url = $this->Application->BaseURL() . $this->_removeUrlEnding($url) . $default_ending; trigger_error('Mod-rewrite url "<strong>' . $_SERVER['REQUEST_URI'] . '</strong>" without "<strong>' . $default_ending . '</strong>" line ending used', E_USER_NOTICE); $this->Application->Redirect('external:' . $target_url, Array ('response_code' => 301)); } /** * Returns url parsing result from cache or false, when not yet parsed * * @param $url * @return Array|bool * @access protected */ protected function _getCachedUrl($url) { if ( !$url || (defined('DBG_CACHE_URLS') && !DBG_CACHE_URLS) ) { return false; } $sql = 'SELECT * FROM ' . TABLE_PREFIX . 'CachedUrls WHERE Hash = ' . kUtil::crc32($url) . ' AND DomainId = ' . (int)$this->Application->siteDomainField('DomainId'); $data = $this->Conn->GetRow($sql); if ( $data ) { $lifetime = (int)$data['LifeTime']; // in seconds if ( ($lifetime > 0) && ($data['Cached'] + $lifetime < TIMENOW) ) { // delete expired $sql = 'DELETE FROM ' . TABLE_PREFIX . 'CachedUrls WHERE UrlId = ' . $data['UrlId']; $this->Conn->Query($sql); return false; } return unserialize($data['ParsedVars']); } return false; } /** * Caches url * * @param string $url * @param Array $data * @return void * @access protected */ protected function _setCachedUrl($url, $data) { if ( !$url || (defined('DBG_CACHE_URLS') && !DBG_CACHE_URLS) ) { return; } $vars = $data['vars']; $passed = $data['passed']; sort($passed); // get expiration if ( $vars['m_cat_id'] > 0 ) { $sql = 'SELECT PageExpiration FROM ' . TABLE_PREFIX . 'Categories WHERE CategoryId = ' . $vars['m_cat_id']; $expiration = $this->Conn->GetOne($sql); } // get prefixes $prefixes = Array (); $m_index = array_search('m', $passed); if ( $m_index !== false ) { unset($passed[$m_index]); if ( $vars['m_cat_id'] > 0 ) { $prefixes[] = 'c:' . $vars['m_cat_id']; } $prefixes[] = 'lang:' . $vars['m_lang']; $prefixes[] = 'theme:' . $vars['m_theme']; } foreach ($passed as $prefix) { if ( array_key_exists($prefix . '_id', $vars) && is_numeric($vars[$prefix . '_id']) ) { $prefixes[] = $prefix . ':' . $vars[$prefix . '_id']; } else { $prefixes[] = $prefix; } } $fields_hash = Array ( 'Url' => $url, 'Hash' => kUtil::crc32($url), 'DomainId' => (int)$this->Application->siteDomainField('DomainId'), 'Prefixes' => $prefixes ? '|' . implode('|', $prefixes) . '|' : '', 'ParsedVars' => serialize($data), 'Cached' => time(), 'LifeTime' => isset($expiration) && is_numeric($expiration) ? $expiration : -1 ); $this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'CachedUrls'); } /** * Loads all registered routers, so they could be quickly accessed later. * * @return void */ protected function initRouters() { static $init_done = false; // Not initialized OR mod-rewrite url with missing config cache. if ( $init_done || count($this->Application->routers) == 0 ) { return; } foreach ( $this->Application->routers as $prefix => $router_data ) { $this->routers[$prefix] = $this->Application->makeClass($router_data['class']); } define('MOD_REWRITE_URL_ENDING', $this->Application->ConfigValue('ModRewriteUrlEnding')); $init_done = true; } /** * Parses given string into a set of variables (url in this case) * * @param string $string * @param string $pass_name * @return Array * @access public */ public function parse($string, $pass_name = 'pass') { // external url (could be back this website as well) if ( preg_match('/external:(.*)/', $string, $regs) ) { $string = $regs[1]; } $vars = Array (); $url_components = parse_url($string); if ( isset($url_components['query']) ) { parse_str(html_entity_decode($url_components['query']), $url_params); if ( isset($url_params[ENV_VAR_NAME]) ) { $url_params = array_merge($url_params, $this->manager->plain->parse($url_params[ENV_VAR_NAME], $pass_name)); unset($url_params[ENV_VAR_NAME]); } $vars = array_merge($vars, $url_params); } $this->_fixPass($vars, $pass_name); if ( isset($url_components['path']) ) { if ( BASE_PATH ) { $string = preg_replace('/^' . preg_quote(BASE_PATH, '/') . '/', '', $url_components['path'], 1); } else { $string = $url_components['path']; } $string = $this->_removeUrlEnding(trim($string, '/')); } else { $string = ''; } $url_parts = $string ? explode('/', mb_strtolower($string)) : Array (); $this->setModulePrefix(false); $this->_partsToParse = $url_parts; if ( ($this->HTTPQuery->Get('rewrite') == 'on') || !$url_parts ) { $this->_setDefaultValues($vars); } if ( !$url_parts ) { $this->_initAll(); $vars['t'] = $this->Application->UrlManager->getTemplateName(); return $vars; } $this->_parseLanguage($url_parts, $vars); $this->_parseTheme($url_parts, $vars); // http://site-url/<language>/<theme>/<category>[_<category_page>]/<template>/<module_page> // http://site-url/<language>/<theme>/<category>[_<category_page>]/<module_page> (category-based section template) // http://site-url/<language>/<theme>/<category>[_<category_page>]/<template>/<module_item> // http://site-url/<language>/<theme>/<category>[_<category_page>]/<module_item> (category-based detail template) // http://site-url/<language>/<theme>/<rl_injections>/<category>[_<category_page>]/<rl_part> (customized url) if ( !$this->processRouters($url_parts, $vars) ) { // Routers weren't able to determine template. $this->_parsePhysicalTemplate($url_parts, $vars); if ( ($this->modulePrefix === false) && $vars['m_cat_id'] && !$this->_partsToParse ) { // no category item found, but category found and all url matched -> module index page return $vars; } } if ( $this->_partsToParse ) { - $vars = array_merge($vars, $this->manager->prepare404($vars['m_theme'])); + /** @var kThemesHelper $themes_helper */ + $themes_helper = $this->Application->recallObject('ThemesHelper'); + + $not_found = $this->Application->ConfigValue('ErrorTemplate'); + $vars['t'] = $not_found ? $not_found : 'error_notfound'; + $vars['m_cat_id'] = $themes_helper->getPageByTemplate($vars['t'], $vars['m_theme']); + $vars['pass'] = array('m'); } return $vars; } /** * Ensures, that "m" is always in "pass" variable * * @param Array $vars * @param string $pass_name * @return void * @access protected */ protected function _fixPass(&$vars, $pass_name) { if ( isset($vars[$pass_name]) ) { $vars[$pass_name] = array_unique(explode(',', 'm,' . $vars[$pass_name])); } else { $vars[$pass_name] = Array ('m'); } } /** * Initializes theme & language based on parse results * * @return void * @access protected */ protected function _initAll() { $this->Application->VerifyThemeId(); $this->Application->VerifyLanguageId(); // No need, since we don't have any cached phrase IDs + nobody will use kPhraseCache::LanguageId soon. // $this->Application->Phrases->Init('phrases'); } /** * Sets default parsed values before actual url parsing (only, for empty url) * * @param Array $vars * @access protected */ protected function _setDefaultValues(&$vars) { $defaults = Array ( 'm_cat_id' => 0, // no category 'm_cat_page' => 1, // first category page 'm_opener' => 's', // stay on same page 't' => 'index' // main site page ); if ($this->primaryLanguageId) { // domain-based primary language $defaults['m_lang'] = $this->primaryLanguageId; } if ($this->primaryThemeId) { // domain-based primary theme $defaults['m_theme'] = $this->primaryThemeId; } foreach ($defaults as $default_key => $default_value) { if ($this->HTTPQuery->Get($default_key) === false) { $vars[$default_key] = $default_value; } } } /** * Processes url using routers. * * Pattern: Chain of Command * * @param array $url_parts Url parts. * @param array $vars Vars. * * @return boolean */ protected function processRouters(array &$url_parts, array &$vars) { $this->initRouters(); $page_number = $this->_parsePage($url_parts, $vars); foreach ( $this->routers as $prefix => $router ) { if ( $page_number ) { // Page given in url - use it. $vars[$prefix . '_id'] = 0; $vars[$prefix . '_Page'] = $page_number; } if ( $router->parse($url_parts, $vars) === false ) { // Will not proceed to other methods. return true; } } // Will proceed to other methods. return false; } /** * Set's page (when found) to all modules * * @param Array $url_parts * @param Array $vars * @return string * @access protected * * @todo Should find a way, how to determine what router page is it. */ protected function _parsePage(&$url_parts, &$vars) { if (!$url_parts) { return false; } $page_number = end($url_parts); if (!is_numeric($page_number)) { return false; } array_pop($url_parts); $this->partParsed($page_number, 'rtl'); return $page_number; } /** * Gets language part from url * * @param Array $url_parts * @param Array $vars * @return bool * @access protected */ protected function _parseLanguage(&$url_parts, &$vars) { if (!$url_parts) { return false; } $url_part = reset($url_parts); $sql = 'SELECT LanguageId, IF(LOWER(PackName) = ' . $this->Conn->qstr($url_part) . ', 2, PrimaryLang) AS SortKey FROM ' . TABLE_PREFIX . 'Languages WHERE Enabled = 1 ORDER BY SortKey DESC'; $language_info = $this->Conn->GetRow($sql); if ($language_info && $language_info['LanguageId'] && $language_info['SortKey']) { // primary language will be selected in case, when $url_part doesn't match to other's language pack name // don't use next enabled language, when primary language is disabled $vars['m_lang'] = $language_info['LanguageId']; if ($language_info['SortKey'] == 2) { // language was found by pack name array_shift($url_parts); $this->partParsed($url_part); } elseif ($this->primaryLanguageId) { // use domain-based primary language instead of site-wide primary language $vars['m_lang'] = $this->primaryLanguageId; } return true; } return false; } /** * Gets theme part from url * * @param Array $url_parts * @param Array $vars * @return bool */ protected function _parseTheme(&$url_parts, &$vars) { if (!$url_parts) { return false; } $url_part = reset($url_parts); $sql = 'SELECT ThemeId, IF(LOWER(Name) = ' . $this->Conn->qstr($url_part) . ', 2, PrimaryTheme) AS SortKey, TemplateAliases FROM ' . TABLE_PREFIX . 'Themes WHERE Enabled = 1 ORDER BY SortKey DESC'; $theme_info = $this->Conn->GetRow($sql); if ($theme_info && $theme_info['ThemeId'] && $theme_info['SortKey']) { // primary theme will be selected in case, when $url_part doesn't match to other's theme name // don't use next enabled theme, when primary theme is disabled $vars['m_theme'] = $theme_info['ThemeId']; if ($theme_info['TemplateAliases']) { $this->_templateAliases = unserialize($theme_info['TemplateAliases']); } else { $this->_templateAliases = Array (); } if ($theme_info['SortKey'] == 2) { // theme was found by name array_shift($url_parts); $this->partParsed($url_part); } elseif ($this->primaryThemeId) { // use domain-based primary theme instead of site-wide primary theme $vars['m_theme'] = $this->primaryThemeId; } return true; } $vars['m_theme'] = 0; // required, because used later for category/template detection return false; } /** * Parses real template name from url * * @param Array $url_parts * @param Array $vars * @return bool */ protected function _parsePhysicalTemplate($url_parts, &$vars) { if ( !$url_parts ) { return false; } $themes_helper = $this->Application->recallObject('ThemesHelper'); /* @var $themes_helper kThemesHelper */ do { $index_added = false; $template_path = implode('/', $url_parts); $template_found = $themes_helper->getTemplateId($template_path, $vars['m_theme']); if ( !$template_found ) { $index_added = true; $template_found = $themes_helper->getTemplateId($template_path . '/index', $vars['m_theme']); } if ( !$template_found ) { array_shift($url_parts); } } while ( !$template_found && $url_parts ); if ( $template_found ) { $template_parts = explode('/', $template_path); $vars['t'] = $template_path . ($index_added ? '/index' : ''); while ( $template_parts ) { $this->partParsed(array_pop($template_parts), 'rtl'); } // 1. will damage actual category during category item review add process // 2. will use "use_section" parameter of "m_Link" tag to gain same effect // $vars['m_cat_id'] = $themes_helper->getPageByTemplate($template_path, $vars['m_theme']); return true; } return false; } /** * Returns environment variable values for given prefix (uses directly given params, when available) * * @param string $prefix_special * @param Array $params * @param bool $keep_events * @return Array * @access public */ public function getProcessedParams($prefix_special, &$params, $keep_events) { list ($prefix) = explode('.', $prefix_special); $query_vars = $this->Application->getUnitConfig($prefix)->getQueryString(Array ()); if ( !$query_vars ) { // given prefix doesn't use "env" variable to pass it's data return false; } $event_key = array_search('event', $query_vars); if ( $event_key ) { // pass through event of this prefix unset($query_vars[$event_key]); } if ( array_key_exists($prefix_special . '_event', $params) && !$params[$prefix_special . '_event'] ) { // if empty event, then remove it from url unset($params[$prefix_special . '_event']); } // if pass events is off and event is not implicity passed if ( !$keep_events && !array_key_exists($prefix_special . '_event', $params) ) { unset($params[$prefix_special . '_event']); // remove event from url if requested //otherwise it will use value from get_var } $processed_params = Array (); foreach ($query_vars as $var_name) { // if value passed in params use it, otherwise use current from application $var_name = $prefix_special . '_' . $var_name; $processed_params[$var_name] = array_key_exists($var_name, $params) ? $params[$var_name] : $this->Application->GetVar($var_name); if ( array_key_exists($var_name, $params) ) { unset($params[$var_name]); } } return $processed_params; } /** * Returns module item details template specified in given category custom field for given module prefix * * @param int|Array $category * @param string $module_prefix * @param int $theme_id * @return string * @access public * @todo Move to kPlainUrlProcessor */ public function GetItemTemplate($category, $module_prefix, $theme_id = null) { if ( !isset($theme_id) ) { $theme_id = $this->Application->GetVar('m_theme'); } $category_id = is_array($category) ? $category['CategoryId'] : $category; $cache_key = __CLASS__ . '::' . __FUNCTION__ . '[%CIDSerial:' . $category_id . '%][%ThemeIDSerial:' . $theme_id . '%]' . $module_prefix; $cached_value = $this->Application->getCache($cache_key); if ( $cached_value !== false ) { return $cached_value; } if ( !is_array($category) ) { if ( $category == 0 ) { $category = $this->Application->findModule('Var', $module_prefix, 'RootCat'); } $sql = 'SELECT c.ParentPath, c.CategoryId FROM ' . TABLE_PREFIX . 'Categories AS c WHERE c.CategoryId = ' . $category; $category = $this->Conn->GetRow($sql); } $parent_path = implode(',', explode('|', substr($category['ParentPath'], 1, -1))); // item template is stored in module' system custom field - need to get that field Id $primary_lang = $this->Application->GetDefaultLanguageId(); $item_template_field_id = $this->getItemTemplateCustomField($module_prefix); // looking for item template through cats hierarchy sorted by parent path $query = ' SELECT ccd.l' . $primary_lang . '_cust_' . $item_template_field_id . ', FIND_IN_SET(c.CategoryId, ' . $this->Conn->qstr($parent_path) . ') AS Ord1, c.CategoryId, c.Name, ccd.l' . $primary_lang . '_cust_' . $item_template_field_id . ' FROM ' . TABLE_PREFIX . 'Categories AS c LEFT JOIN ' . TABLE_PREFIX . 'CategoryCustomData AS ccd ON ccd.ResourceId = c.ResourceId WHERE c.CategoryId IN (' . $parent_path . ') AND ccd.l' . $primary_lang . '_cust_' . $item_template_field_id . ' != \'\' ORDER BY FIND_IN_SET(c.CategoryId, ' . $this->Conn->qstr($parent_path) . ') DESC'; $item_template = $this->Conn->GetOne($query); if ( !isset($this->_templateAliases) ) { // when empty url OR mod-rewrite disabled $themes_helper = $this->Application->recallObject('ThemesHelper'); /* @var $themes_helper kThemesHelper */ $sql = 'SELECT TemplateAliases FROM ' . TABLE_PREFIX . 'Themes WHERE ThemeId = ' . (int)$themes_helper->getCurrentThemeId(); $template_aliases = $this->Conn->GetOne($sql); $this->_templateAliases = $template_aliases ? unserialize($template_aliases) : Array (); } if ( substr($item_template, 0, 1) == '#' ) { // it's template alias + "#" isn't allowed in filenames $item_template = (string)getArrayValue($this->_templateAliases, $item_template); } $this->Application->setCache($cache_key, $item_template); return $item_template; } /** * Returns category custom field id, where given module prefix item template name is stored * * @param string $module_prefix * @return int * @access public * @todo Move to kPlainUrlProcessor; decrease visibility, since used only during upgrade */ public function getItemTemplateCustomField($module_prefix) { $cache_key = __CLASS__ . '::' . __FUNCTION__ . '[%CfSerial%]:' . $module_prefix; $cached_value = $this->Application->getCache($cache_key); if ($cached_value !== false) { return $cached_value; } $sql = 'SELECT CustomFieldId FROM ' . TABLE_PREFIX . 'CustomFields WHERE FieldName = ' . $this->Conn->qstr($module_prefix . '_ItemTemplate'); $item_template_field_id = $this->Conn->GetOne($sql); $this->Application->setCache($cache_key, $item_template_field_id); return $item_template_field_id; } /** * Marks url part as parsed * * @param string $url_part * @param string $parse_direction * @access public */ public function partParsed($url_part, $parse_direction = 'ltr') { if ( !$this->_partsToParse ) { return ; } if ( $parse_direction == 'ltr' ) { $expected_url_part = reset($this->_partsToParse); if ( $url_part == $expected_url_part ) { array_shift($this->_partsToParse); } } else { $expected_url_part = end($this->_partsToParse); if ( $url_part == $expected_url_part ) { array_pop($this->_partsToParse); } } if ( $url_part != $expected_url_part ) { trigger_error('partParsed: expected URL part "<strong>' . $expected_url_part . '</strong>", received URL part "<strong>' . $url_part . '</strong>"', E_USER_NOTICE); } } /** * Determines if there is more to parse in url * * @return bool * @access public */ public function moreToParse() { return count($this->_partsToParse) > 0; } /** * Builds url * * @param string $t * @param Array $params * @param string $pass * @param bool $pass_events * @param bool $env_var * @return string * @access public */ public function build($t, $params, $pass = 'all', $pass_events = false, $env_var = false) { if ( $this->Application->GetVar('admin') || (array_key_exists('admin', $params) && $params['admin']) ) { $params['admin'] = 1; if ( !array_key_exists('editing_mode', $params) ) { $params['editing_mode'] = EDITING_MODE; } } $ret = ''; $env = ''; if ( isset($params['__SSL__']) ) { unset($params['__SSL__']); } $catalog_item_found = false; $pass_info = $this->getPassInfo($pass); if ( $pass_info ) { if ( $pass_info[0] == 'm' ) { array_shift($pass_info); } $inject_parts = array(); // Url parts for beginning of url. $params['t'] = $t; // Make template available for routers. $params['pass_template'] = true; // By default we keep given template in resulting url. if ( !array_key_exists('pass_category', $params) ) { $params['pass_category'] = false; // by default we don't keep categories in url } foreach ($pass_info as $pass_index => $pass_element) { list ($prefix) = explode('.', $pass_element); $catalog_item = $this->Application->findModule('Var', $prefix) && $this->Application->getUnitConfig($prefix)->getCatalogItem(); if ( array_key_exists($prefix, $this->routers) ) { // if next prefix is same as current, but with special => exclude current prefix from url $next_prefix = array_key_exists($pass_index + 1, $pass_info) ? $pass_info[$pass_index + 1] : false; if ( $next_prefix ) { $next_prefix = substr($next_prefix, 0, strlen($prefix) + 1); if ( $prefix . '.' == $next_prefix ) { continue; } } // rewritten url part $url_part = $this->BuildModuleEnv($pass_element, $params, $pass_events); if ( is_string($url_part) && $url_part ) { $ret .= $url_part . '/'; if ( $catalog_item ) { // pass category later only for catalog items $catalog_item_found = true; } } elseif ( is_array($url_part) ) { // Router want to insert something at the beginning of url too. if ( $url_part[0] ) { $inject_parts[] = $url_part[0]; } if ( $url_part[1] ) { $ret .= $url_part[1] . '/'; } if ( $catalog_item ) { // pass category later only for catalog items $catalog_item_found = true; } } elseif ( $url_part === false ) { // Router decided not to rewrite given $pass_element. $env .= ':' . $this->manager->plain->BuildModuleEnv($pass_element, $params, $pass_events); } } else { $env .= ':' . $this->manager->plain->BuildModuleEnv($pass_element, $params, $pass_events); } } if ( $catalog_item_found || preg_match('/c\.[-\d]*/', implode(',', $pass_info)) ) { // "c" prefix is present -> keep category $params['pass_category'] = true; } $params['inject_parts'] = $inject_parts; $ret = $this->BuildModuleEnv('m', $params, $pass_events) . '/' . $ret; $cat_processed = array_key_exists('category_processed', $params) && $params['category_processed']; // Remove temporary parameters used by routers. unset($params['t'], $params['inject_parts'], $params['pass_template'], $params['pass_category'], $params['category_processed']); $ret = trim($ret, '/'); if ( isset($params['url_ending']) ) { if ( $ret ) { $ret .= $params['url_ending']; } unset($params['url_ending']); } elseif ( $ret ) { $ret .= MOD_REWRITE_URL_ENDING; } if ( $env ) { $params[ENV_VAR_NAME] = ltrim($env, ':'); } } unset($params['pass'], $params['opener'], $params['m_event']); // TODO: why? // $ret = str_replace('%2F', '/', urlencode($ret)); if ( $params ) { $params_str = http_build_query($params); $ret .= '?' . str_replace('%23', '#', $params_str); } return $ret; } /** * Builds env part that corresponds prefix passed * * @param string $prefix_special item's prefix & [special] * @param Array $params url params * @param bool $pass_events * @return string * @access protected */ protected function BuildModuleEnv($prefix_special, &$params, $pass_events = false) { list ($prefix) = explode('.', $prefix_special); return $this->routers[$prefix]->buildWrapper($prefix_special, $params, $pass_events); } } Index: branches/5.3.x/core/kernel/constants.php =================================================================== --- branches/5.3.x/core/kernel/constants.php (revision 16394) +++ branches/5.3.x/core/kernel/constants.php (revision 16395) @@ -1,235 +1,237 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); // item statuses define('STATUS_DISABLED', 0); define('STATUS_ACTIVE', 1); define('STATUS_PENDING', 2); define('STATUS_PENDING_EDITING', -2); // sections define('stTREE', 1); define('stTAB', 2); define('smHIDE', 0); // always hide section from tree define('smNORMAL', 1); // show section even, if they were marked as smDEBUG or smSUPER_ADMIN before define('smDEBUG', 2); // show section in debug mode only define('smSUPER_ADMIN', 4); // show section in super admin & debug mode // permission types define('ptCATEGORY', 0); define('ptSYSTEM', 1); define('EDIT_MARK', '&|edit|&'); // replace this sequence inside filters to SID[_main_wid] $application =& kApplication::Instance(); $spacer_url = $application->BaseURL().'core/admin_templates/img/spacer.gif'; define('SPACER_URL', $spacer_url); if (!$application->isAdmin) { // don't show debugger buttons on front (if not overridden in "debug.php") kUtil::safeDefine('DBG_TOOLBAR_BUTTONS', 0); } // common usage regular expressions define('REGEX_EMAIL_USER', '[-a-zA-Z0-9!\#$%&*+\/=?^_`{|}~.]+'); define('REGEX_EMAIL_DOMAIN', '[a-zA-Z0-9]{1}[-.a-zA-Z0-9_]*\.[a-zA-Z]{2,6}'); define('ALLOW_DEFAULT_SETTINGS', '_USE_DEFAULT_USER_DATA_'); //Allow persistent vars to take data from default user's persistent data class ChangeLog { const CREATE = 1; const UPDATE = 2; const DELETE = 3; } // Separator for ValueList fields define('VALUE_LIST_SEPARATOR', '||'); // template editing modes define('EDITING_MODE_BROWSE', 1); // no changes, front-end as users see it define('EDITING_MODE_CONTENT', 2); // content blocks + phrase editing define('EDITING_MODE_DESIGN', 3); // all other blocks class ScheduledTask { const TYPE_USER = 1; const TYPE_SYSTEM = 2; const LAST_RUN_SUCCEEDED = 1; const LAST_RUN_FAILED = 0; const LAST_RUN_RUNNING = 2; } // place for product file uploads (sort of "/system/images" but for all other files) define('ITEM_FILES_PATH', WRITEBALE_BASE . '/downloads/'); + define('THUMBS_PATH', WRITEBALE_BASE . '/thumbs'); + class MailingList { const NOT_PROCESSED = 1; const PARTIALLY_PROCESSED = 2; const PROCESSED = 3; const CANCELLED = 4; } // theme file statuses (related to structure creation process) define('SMS_MODE_AUTO', 1); define('SMS_MODE_FORCE', 2); // Means, that actual category Template field value should inherited from parent category define('CATEGORY_TEMPLATE_INHERIT', '#inherit#'); define('REWRITE_MODE_BUILD', 1); define('REWRITE_MODE_PARSE', 2); define('SESSION_LOG_ACTIVE', 0); define('SESSION_LOG_LOGGED_OUT', 1); define('SESSION_LOG_EXPIRED', 2); class LoginResult { const OK = 0; const INVALID_LOGIN = 1; const INVALID_PASSWORD = 2; const BANNED = 3; const NO_PERMISSION = 4; } define('DESTINATION_TYPE_COUNTRY', 1); define('DESTINATION_TYPE_STATE', 2); class SubmissionFormField { const VISIBILITY_EVERYONE = 1; const VISIBILITY_UNREGISTERED = 2; const COMMUNICATION_ROLE_NAME = 1; const COMMUNICATION_ROLE_EMAIL = 2; const COMMUNICATION_ROLE_SUBJECT = 3; const COMMUNICATION_ROLE_BODY = 4; } // form submission statuses define('SUBMISSION_REPLIED', 1); // submission was replied by admin define('SUBMISSION_NOT_REPLIED', 2); // submission has no client replies (no messages at all) define('SUBMISSION_NEW_EMAIL', 3); // submission have new reply/email from client define('SUBMISSION_BOUNCE', 4); // submission have bounce from client // submission log statuses define('SUBMISSION_LOG_SENT', 1); define('SUBMISSION_LOG_BOUNCE', 2); define('SUBMISSION_LOG_REPLIED', 1); define('SUBMISSION_LOG_ATTACHMENT_PATH', WRITEBALE_BASE . '/user_files/submission_log/'); define('TIMENOW', time()); // for faster message processing define('USER_TITLE_FIELD', 'IF(Email = "", Username, Email)'); // site domains define('SITE_DOMAIN_REDIRECT_CURRENT', 1); define('SITE_DOMAIN_REDIRECT_EXTERNAL', 2); class EmailTemplate { const TEMPLATE_TYPE_FRONTEND = 0; const TEMPLATE_TYPE_ADMIN = 1; const ADDRESS_TYPE_EMAIL = 1; const ADDRESS_TYPE_USER = 2; const ADDRESS_TYPE_GROUP = 3; const RECIPIENT_TYPE_TO = 1; const RECIPIENT_TYPE_CC = 2; const RECIPIENT_TYPE_BCC = 3; } define('PAGE_TYPE_VIRTUAL', 1); define('PAGE_TYPE_TEMPLATE', 2); define('CONTENT_LASTAUTOSAVE_UPDATE_INTERVAL', 5 * 60); // 5 minutes define('CONTENT_LASTAUTOSAVE_REFRESH_INTERVAL', 20); // 20 seconds define('hBEFORE', 1); define('hAFTER', 2); class UserType { const USER = 0; const ADMIN = 1; } class PasswordHashingMethod { const NONE = 0; const MD5 = 1; const MD5_AND_PHPPASS = 2; const PHPPASS = 3; } // selectors define('STYLE_BASE', 1); define('STYLE_BLOCK', 2); class Language { const SYNCHRONIZE_TO_OTHERS = 1; const SYNCHRONIZE_FROM_OTHERS = 2; const SYNCHRONIZE_DEFAULT = '|1|2|'; const PHRASE_TYPE_FRONT = 0; const PHRASE_TYPE_ADMIN = 1; const PHRASE_TYPE_COMMON = 2; } class PromoBlockType { const INTERNAL = 1; const EXTERNAL = 2; } class StorageEngine { const HASH = 1; const TIMESTAMP = 2; const PS_DATE_TIME = 'DATE-TIME'; const PS_PREFIX = 'PREFIX'; const PS_USER = 'USER'; } class CategoryPermissionRebuild { const MANUAL = 1; const SILENT = 2; const AUTOMATIC = 3; } class EmailDelivery { const QUEUE = 1; const IMMEDIATE = 2; } class EmailLogStatus { const SENT = 1; const ERROR = 2; } class TranslationSaveMode { const SYNC_WITH_PRIMARY = 1; const MAKE_PRIMARY = 2; } class ModuleDeploymentLog { const MODE_AUTOMATIC = 1; const MODE_MANUAL = 2; const STATUS_SUCCESS = 1; const STATUS_ERROR = 2; const STATUS_SKIPPED = 3; } Index: branches/5.3.x/core/kernel/processors/main_processor.php =================================================================== --- branches/5.3.x/core/kernel/processors/main_processor.php (revision 16394) +++ branches/5.3.x/core/kernel/processors/main_processor.php (revision 16395) @@ -1,1292 +1,1307 @@ <?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 kMainTagProcessor extends kTagProcessor { public function __construct() { parent::__construct(); $actions = $this->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 <base href ..> 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 '<base href="' . $base_href . '/" />'; } /** * 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)); + // 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']); + + // Pass "m" prefix, instead of "all", that is by default on Front-End. + if ( !array_key_exists('pass', $params) ) { + $params['pass'] = 'm'; + } + + $this->Application->Redirect($template, $params, $prefix); 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<br>"; } else echo "Object $name does not exist in the appliaction<br>"; }*/ /** * 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]<br>"; $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()); kUtil::array_unshift_ref($function_params, $ret); $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 $this->Application->isAdmin ? $ret : kUtil::unescape($ret, kUtil::ESCAPE_HTML); } 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 .= "<input type='hidden' name='$name' id='$name' value='$val'>\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 = '<input type="hidden" name="%1$s" id="%1$s" value="%2$s"/>' . "\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 = \'<tr>\'; $o.= \'<td>a\'.$params[\'param1\'].\'</td>\'; $o.= \'<td>a\'.$params[\'param2\'].\'</td>\'; $o.= \'<td>a\'.$params[\'param3\'].\'</td>\'; $o.= \'<td>a\'.$params[\'param4\'].\'</td>\'; $o.= \'</tr>\'; 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('kHTTPQuery'); /* @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) ) { + if ( file_exists($path) && is_file($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) { + if ( is_object($param_value) && !method_exists($param_value, '__toString') ) { + $param_value = 'ClassName: ' . get_class($param_value); + } + $current_params[$param_name] = $param_name . ' = "' . $param_value . '"'; } return '<pre>' . implode("\n", $current_params) . '</pre>'; } /** * 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 = '<input type="hidden" name="main_list" value="1"/>'; 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 .= '<input type="hidden" name="' . $var_name . '" value="' . $var_value . '"/>'; } } 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/email_send.php =================================================================== --- branches/5.3.x/core/kernel/utility/email_send.php (revision 16394) +++ branches/5.3.x/core/kernel/utility/email_send.php (revision 16395) @@ -1,2094 +1,2180 @@ <?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 used to compose email message (using MIME standarts) and send it via mail function or via SMTP server * */ class kEmailSendingHelper extends kHelper { /** * headers of main header part * * @var Array */ var $headers = Array (); /** * Tells if all message parts were combined together * * @var int */ var $bodyPartNumber = false; /** * Composed message parts * * @var Array */ var $parts = Array(); /** * Lines separator by MIME standart * * @var string */ var $line_break = "\n"; /** * Charset used for message composing * * @var string */ var $charset = 'utf-8'; /** * Name of mailer program (X-Mailer header) * * @var string */ var $mailerName = ''; /** * Options used for message content-type & structure guessing * * @var Array */ var $guessOptions = Array (); /** * Send messages using selected method * * @var string */ var $sendMethod = 'Mail'; /** * Parameters used to initiate SMTP server connection * * @var Array */ var $smtpParams = Array (); /** * List of supported authentication methods, in preferential order. * @var array * @access public */ var $smtpAuthMethods = Array('CRAM-MD5', 'LOGIN', 'PLAIN'); /** * The socket resource being used to connect to the SMTP server. * @var kSocket * @access private */ var $smtpSocket = null; /** * The most recent server response code. * @var int * @access private */ var $smtpResponceCode = -1; /** * The most recent server response arguments. * @var array * @access private */ var $smtpRespoceArguments = Array(); /** * Stores detected features of the SMTP server. * @var array * @access private */ var $smtpFeatures = Array(); /** * Stores log data * * @var Array * @access protected */ protected $_logData = Array (); public function __construct() { parent::__construct(); // set default guess options $this->guessOptions = Array ( 'attachments' => Array (), 'inline_attachments' => Array (), 'text_part' => false, 'html_part' => false, ); // read SMTP server connection params from config $smtp_mapping = Array ('server' => 'Smtp_Server', 'port' => 'Smtp_Port'); if ($this->Application->ConfigValue('Smtp_Authenticate')) { $smtp_mapping['username'] = 'Smtp_User'; $smtp_mapping['password'] = 'Smtp_Pass'; } foreach ($smtp_mapping as $smtp_name => $config_name) { $this->smtpParams[$smtp_name] = $this->Application->ConfigValue($config_name); } $this->smtpParams['use_auth'] = isset($this->smtpParams['username']) ? true : false; $this->smtpParams['localhost'] = 'localhost'; // The value to give when sending EHLO or HELO. $this->sendMethod = $this->smtpParams['server'] && $this->smtpParams['port'] ? 'SMTP' : 'Mail'; if ($this->sendMethod == 'SMTP') { // create connection object if we will use SMTP $this->smtpSocket = $this->Application->makeClass('kSocket'); } $this->SetCharset(null, true); } /** * Returns new message id header by sender's email address * * @param string $email_address email address * @return string */ function GenerateMessageID($email_address) { list ($micros, $seconds) = explode(' ', microtime()); list ($user, $domain) = explode('@', $email_address, 2); $message_id = strftime('%Y%m%d%H%M%S', $seconds).substr($micros, 1, 5).'.'.preg_replace('/[^A-Za-z]+/', '-', $user).'@'.$domain; $this->SetHeader('Message-ID', '<'.$message_id.'>'); } /** * Returns extension of given filename * * @param string $filename * @return string */ function GetFilenameExtension($filename) { $last_dot = mb_strrpos($filename, '.'); return $last_dot !== false ? mb_substr($filename, $last_dot + 1) : ''; } /** * Creates boundary for part by number (only if it's missing) * * @param int $part_number * */ function CreatePartBoundary($part_number) { $part =& $this->parts[$part_number]; if (!isset($part['BOUNDARY'])) { $part['BOUNDARY'] = md5(uniqid($part_number.time())); } } /** * Returns ready to use headers associative array of any message part by it's number * * @param int $part_number * @return Array */ function GetPartHeaders($part_number) { $part =& $this->parts[$part_number]; if (!isset($part['Content-Type'])) { return $this->SetError('MISSING_CONTENT_TYPE'); } $full_type = strtolower($part['Content-Type']); list ($type, $sub_type) = explode('/', $full_type); $headers['Content-Type'] = $full_type; switch ($type) { case 'text': case 'image': case 'audio': case 'video': case 'application': case 'message': // 1. update content-type header if (isset($part['CHARSET'])) { $headers['Content-Type'] .= '; charset='.$part['CHARSET']; } if (isset($part['NAME'])) { $headers['Content-Type'] .= '; name="'.$part['NAME'].'"'; } // 2. set content-transfer-encoding header if (isset($part['Content-Transfer-Encoding'])) { $headers['Content-Transfer-Encoding'] = $part['Content-Transfer-Encoding']; } // 3. set content-disposition header if (isset($part['DISPOSITION']) && $part['DISPOSITION']) { $headers['Content-Disposition'] = $part['DISPOSITION']; if (isset($part['NAME'])) { $headers['Content-Disposition'] .= '; filename="'.$part['NAME'].'"'; } } break; case 'multipart': switch ($sub_type) { case 'alternative': case 'related': case 'mixed': case 'parallel': $this->CreatePartBoundary($part_number); $headers['Content-Type'] .= '; boundary="'.$part['BOUNDARY'].'"'; break; default: return $this->SetError('INVALID_MULTIPART_SUBTYPE', Array($sub_type)); } break; default: return $this->SetError('INVALID_CONTENT_TYPE', Array($full_type)); } // set content-id if any if (isset($part['Content-ID'])) { $headers['Content-ID'] = '<'.$part['Content-ID'].'>'; } return $headers; } function GetPartBody($part_number) { $part =& $this->parts[$part_number]; if (!isset($part['Content-Type'])) { return $this->SetError('MISSING_CONTENT_TYPE'); } $full_type = strtolower($part['Content-Type']); list ($type, $sub_type) = explode('/', $full_type); $body = ''; switch ($type) { // compose text/binary content case 'text': case 'image': case 'audio': case 'video': case 'application': case 'message': // 1. get content of part if (isset($part['FILENAME'])) { // content provided via absolute path to content containing file $filename = $part['FILENAME']; $file_size = filesize($filename); $body = file_get_contents($filename); if ($body === false) { return $this->SetError('FILE_PART_OPEN_ERROR', Array($filename)); } $actual_size = strlen($body); if (($file_size === false || $actual_size > $file_size) && get_magic_quotes_runtime()) { $body = stripslashes($body); } if ($file_size !== false && $actual_size != $file_size) { return $this->SetError('FILE_PART_DATA_ERROR', Array($filename)); } } else { // content provided directly as one of part keys if (!isset($part['DATA'])) { return $this->SetError('FILE_PART_DATA_MISSING'); } $body =& $part['DATA']; } // 2. get part transfer encoding $encoding = isset($part['Content-Transfer-Encoding']) ? strtolower($part['Content-Transfer-Encoding']) : ''; if (!in_array($encoding, Array ('', 'base64', 'quoted-printable', '7bit'))) { return $this->SetError('INVALID_ENCODING', Array($encoding)); } if ($encoding == 'base64') { // split base64 encoded text by 76 symbols at line (MIME requirement) $body = chunk_split( base64_encode($body) ); } break; case 'multipart': // compose multipart message switch ($sub_type) { case 'alternative': case 'related': case 'mixed': case 'parallel': $this->CreatePartBoundary($part_number); $boundary = $this->line_break.'--'.$part['BOUNDARY']; foreach ($part['PARTS'] as $multipart_number) { $body .= $boundary.$this->line_break; $part_headers = $this->GetPartHeaders($multipart_number); if ($part_headers === false) { // some of sub-part headers were invalid return false; } foreach ($part_headers as $header_name => $header_value) { $body .= $header_name.': '.$header_value.$this->line_break; } $part_body = $this->GetPartBody($multipart_number); if ($part_body === false) { // part body was invalid return false; } $body .= $this->line_break.$part_body; } $body .= $boundary.'--'.$this->line_break; break; default: return $this->SetError('INVALID_MULTIPART_SUBTYPE', Array($sub_type)); } break; default: return $this->SetError('INVALID_CONTENT_TYPE', Array($full_type)); } return $body; } /** * Applies quoted-printable encoding to specified text * * @param string $text * @param string $header_charset * @param int $break_lines * @return unknown */ function QuotedPrintableEncode($text, $header_charset = '', $break_lines = 1) { $ln = strlen($text); $h = strlen($header_charset) > 0; if ($h) { $s = Array ( '=' => 1, '?' => 1, '_' => 1, '(' => 1, ')' => 1, '<' => 1, '>' => 1, '@' => 1, ',' => 1, ';' => 1, '"' => 1, '\\' => 1, /* '/' => 1, '[' => 1, ']' => 1, ':' => 1, '.' => 1, */ ); $b = $space = $break_lines = 0; for ($i = 0; $i < $ln; $i++) { if (isset($s[$text[$i]])) { $b = 1; break; } switch ($o = ord($text[$i])) { case 9: case 32: $space = $i + 1; $b = 1; break 2; case 10: case 13: break 2; default: if ($o < 32 || $o > 127) { $b = 1; break 2; } } } if($i == $ln) { return $text; } if ($space > 0) { return substr($text, 0, $space).($space < $ln ? $this->QuotedPrintableEncode(substr($text, $space), $header_charset, 0) : ''); } } for ($w = $e = '', $n = 0, $l = 0, $i = 0; $i < $ln; $i++) { $c = $text[$i]; $o = ord($c); $en = 0; switch ($o) { case 9: case 32: if (!$h) { $w = $c; $c = ''; } else { if ($b) { if ($o == 32) { $c = '_'; } else { $en = 1; } } } break; case 10: case 13: if (strlen($w)) { if ($break_lines && $l + 3 > 75) { $e .= '='.$this->line_break; $l = 0; } $e .= sprintf('=%02X', ord($w)); $l += 3; $w = ''; } $e .= $c; if ($h) { $e .= "\t"; } $l = 0; continue 2; case 46: case 70: case 102: $en = (!$h && ($l == 0 || $l + 1 > 75)); break; default: if ($o > 127 || $o < 32 || !strcmp($c, '=')) { $en = 1; } elseif ($h && isset($s[$c])) { $en = 1; } break; } if (strlen($w)) { if ($break_lines && $l + 1 > 75) { $e .= '='.$this->line_break; $l = 0; } $e .= $w; $l++; $w = ''; } if (strlen($c)) { if ($en) { $c = sprintf('=%02X', $o); $el = 3; $n = 1; $b = 1; } else { $el = 1; } if ($break_lines && $l + $el > 75) { $e .= '='.$this->line_break; $l = 0; } $e .= $c; $l += $el; } } if (strlen($w)) { if ($break_lines && $l + 3 > 75) { $e .= '='.$this->line_break; } $e .= sprintf('=%02X', ord($w)); } return $h && $n ? '=?'.$header_charset.'?q?'.$e.'?=' : $e; } /** * Sets message header + encodes is by quoted-printable using charset specified * * @param string $name * @param string $value * @param string $encoding_charset */ function SetHeader($name, $value, $encoding_charset = '') { if ($encoding_charset) { // actually for headers base64 method may give shorter result $value = $this->QuotedPrintableEncode($value, $encoding_charset); } $this->headers[$name] = $value; } /** * Sets header + automatically encodes it using default charset * * @param string $name * @param string $value */ function SetEncodedHeader($name, $value) { $this->SetHeader($name, $value, $this->charset); } /** * Sets header which value is email and username +autoencode * * @param string $header * @param string $address * @param string $name */ function SetEncodedEmailHeader($header, $address, $name) { $this->SetHeader($header, $this->QuotedPrintableEncode($name, $this->charset) . ' <' . $address . '>'); } function SetMultipleEncodedEmailHeader($header, $addresses) { $value = ''; foreach ($addresses as $name => $address) { $value .= $this->QuotedPrintableEncode($name, $this->charset) . ' <' . $address . '>, '; } $this->SetHeader($header, substr($value, 0, -2)); } /** * Adds new part to message and returns it's number * * @param Array $part_definition * @param int|bool $part_number number of new part * @return int */ function AddPart(&$part_definition, $part_number = false) { $part_number = $part_number !== false ? $part_number : count($this->parts); $this->parts[$part_number] =& $part_definition; return $part_number; } /** + * Detects if such part already was added. + * + * @param array $part_definition Part definition. + * + * @return boolean + */ + protected function HasPart(array $part_definition) + { + foreach ( $this->parts as $part ) { + if ( $part === $part_definition ) { + return true; + } + } + + return false; + } + + /** * Returns text version of HTML document * * @param string $html * @param bool $keep_inp_tags * @return string */ function ConvertToText($html, $keep_inp_tags = false) { if ( $keep_inp_tags && preg_match_all('/(<[\\/]?)inp2:([^>]*?)([\\/]?>)/s', $html, $regs) ) { $found_tags = Array (); foreach ($regs[0] as $index => $tag) { $tag_placeholder = '%' . md5($index . ':' . $tag) . '%'; $found_tags[$tag_placeholder] = $tag; // we can have duplicate tags -> replace only 1st occurrence (str_replace can't do that) $html = preg_replace('/' . preg_quote($tag, '/') . '/', $tag_placeholder, $html, 1); } $html = $this->_convertToText($html); foreach ($found_tags as $tag_placeholder => $tag) { $html = str_replace($tag_placeholder, $tag, $html); } return $html; } return $this->_convertToText($html); } /** * Returns text version of HTML document * * @param string $html * @return string */ protected function _convertToText($html) { $search = Array ( "'(<\/td>.*)[\r\n]+(.*<td)'i",//formating text in tables "'(<br[ ]?[\/]?>[\r\n]{0,2})|(<\/p>)|(<\/div>)|(<\/tr>)'i", "'<head>(.*?)</head>'si", "'<style(.*?)</style>'si", "'<title>(.*?)</title>'si", "'<script(.*?)</script>'si", // "'^[\s\n\r\t]+'", //strip all spacers & newlines in the begin of document // "'[\s\n\r\t]+$'", //strip all spacers & newlines in the end of document "'&(quot|#34);'i", "'&(amp|#38);'i", "'&(lt|#60);'i", "'&(gt|#62);'i", "'&(nbsp|#160);'i", "'&(iexcl|#161);'i", "'&(cent|#162);'i", "'&(pound|#163);'i", "'&(copy|#169);'i", "'&#(\d+);'e" ); $replace = Array ( "\\1\t\\2", "\n", "", "", "", "", // "", // "", "\"", "&", "<", ">", " ", $this->_safeCharEncode(161), $this->_safeCharEncode(162), $this->_safeCharEncode(163), $this->_safeCharEncode(169), "\$this->_safeCharEncode(\\1)" ); return strip_tags( preg_replace ($search, $replace, $html) ); } /** * Returns symbols, that corresponds given ASCII code and also encodes it into proper charset * * @param int $ascii * @return string * @access protected */ protected function _safeCharEncode($ascii) { return mb_convert_encoding(chr($ascii), $this->charset, 'ISO-8859-1'); } /** * Add text OR html part to message (optionally encoded) * * @param string $text part's text * @param bool $is_html this html part or not * @param bool $encode encode message using quoted-printable encoding * * @return int number of created part */ function CreateTextHtmlPart($text, $is_html = false, $encode = true) { if ($is_html) { // if adding HTML part, then create plain-text part too $this->CreateTextHtmlPart($this->ConvertToText($text)); } // in case if text is from $_REQUEST, then line endings are "\r\n", but we need "\n" here $text = str_replace("\r\n", "\n", $text); // possible case $text = str_replace("\r", "\n", $text); // impossible case, but just in case replace this too $definition = Array ( 'Content-Type' => $is_html ? 'text/html' : 'text/plain', 'CHARSET' => $this->charset, 'DATA' => $encode ? $this->QuotedPrintableEncode($text) : $text, ); if ($encode) { $definition['Content-Transfer-Encoding'] = 'quoted-printable'; } $guess_name = $is_html ? 'html_part' : 'text_part'; $part_number = $this->guessOptions[$guess_name] !== false ? $this->guessOptions[$guess_name] : false; $part_number = $this->AddPart($definition, $part_number); $this->guessOptions[$guess_name] = $part_number; return $part_number; } /** * Adds attachment part to message * * @param string $file name of the file with attachment body * @param string $attach_name name for attachment (name of file is used, when not specified) * @param string $content_type content type for attachment * @param string $content body of file to be attached * @param bool $inline is attachment inline or not * * @return int number of created part */ function AddAttachment($file = '', $attach_name = '', $content_type = '', $content = '', $inline = false) { $definition = Array ( 'Disposition' => $inline ? 'inline' : 'attachment', 'Content-Type' => $content_type ? $content_type : 'automatic/name', ); if ($file) { // filename of attachment given $definition['FileName'] = $file; } if ($attach_name) { // name of attachment given $definition['Name'] = $attach_name; } if ($content) { // attachment data is given $definition['Data'] = $content; } $definition =& $this->GetFileDefinition($definition); + + if ( $this->HasPart($definition) ) { + return false; + } + $part_number = $this->AddPart($definition); if ($inline) { // it's inline attachment and needs content-id to be addressed by in message $this->parts[$part_number]['Content-ID'] = md5(uniqid($part_number.time())).'.'.$this->GetFilenameExtension($attach_name ? $attach_name : $file); } $this->guessOptions[$inline ? 'inline_attachments' : 'attachments'][] = $part_number; return $part_number; } /** * Adds another MIME message as attachment to message being composed * * @param string $file name of the file with attachment body * @param string $attach_name name for attachment (name of file is used, when not specified) * @param string $content body of file to be attached * * @return int number of created part */ function AddMessageAttachment($file = '', $attach_name = '', $content = '') { $part_number = $this->AddAttachment($file, $attach_name, 'message/rfc822', $content, true); unset($this->parts[$part_number]['Content-ID']); // messages don't have content-id, but have inline disposition return $part_number; } /** * Creates multipart of specified type and returns it's number * * @param Array $part_numbers * @param string $multipart_type = {alternative,related,mixed,paralell} * @return int */ function CreateMultipart($part_numbers, $multipart_type) { $types = Array ('alternative', 'related' , 'mixed', 'paralell'); if (!in_array($multipart_type, $types)) { return $this->SetError('INVALID_MULTIPART_SUBTYPE', Array($multipart_type)); } $definition = Array ( 'Content-Type' => 'multipart/'.$multipart_type, 'PARTS' => $part_numbers, ); return $this->AddPart($definition); } /** * Creates missing content-id header for inline attachments * * @param int $part_number */ function CreateContentID($part_number) { $part =& $this->parts[$part_number]; if (!isset($part['Content-ID']) && $part['DISPOSITION'] == 'inline') { $part['Content-ID'] = md5(uniqid($part_number.time())).'.'.$this->GetFilenameExtension($part['NAME']); } } /** * Returns attachment part based on file used in attachment * * @param Array $file * @return Array */ function &GetFileDefinition ($file) { $name = ''; if (isset($file['Name'])) { // if name is given directly, then use it $name = $file['Name']; } else { // auto-guess attachment name based on source filename $name = isset($file['FileName']) ? basename($file['FileName']) : ''; } if (!$name || (!isset($file['FileName']) && !isset($file['Data']))) { // filename not specified || no filename + no direct file content return $this->SetError('MISSING_FILE_DATA'); } $encoding = 'base64'; if (isset($file['Content-Type'])) { $content_type = $file['Content-Type']; list ($type, $sub_type) = explode('/', $content_type); switch ($type) { case 'text': case 'image': case 'audio': case 'video': case 'application': break; case 'message': $encoding = '7bit'; break; case 'automatic': if (!$name) { return $this->SetError('MISSING_FILE_NAME'); } $this->guessContentType($name, $content_type, $encoding); break; default: return $this->SetError('INVALID_CONTENT_TYPE', Array($content_type)); } } else { // encoding not passed in file part, then assume, that it's binary $content_type = 'application/octet-stream'; } $definition = Array ( 'Content-Type' => $content_type, 'Content-Transfer-Encoding' => $encoding, 'NAME' => $name, // attachment name ); if (isset($file['Disposition'])) { $disposition = strtolower($file['Disposition']); if ($disposition == 'inline' || $disposition == 'attachment') { // valid disposition header value $definition['DISPOSITION'] = $file['Disposition']; } else { return $this->SetError('INVALID_DISPOSITION', Array($file['Disposition'])); } } if (isset($file['FileName'])) { $definition['FILENAME'] = $file['FileName']; } elseif (isset($file['Data'])) { $definition['DATA'] =& $file['Data']; } return $definition; } /** * Returns content-type based on filename extension * * @param string $filename * @param string $content_type * @param string $encoding * * @todo Regular expression used is not completely finished, that's why if extension used for * comparing in some other extension (from list) part, that partial match extension will be returned. * Because of two extension that begins with same 2 letters always belong to same content type * this unfinished regular expression still gives correct result in any case. */ function guessContentType($filename, &$content_type, &$encoding) { $content_type = kUtil::mimeContentTypeByExtension($filename); if ( mb_strtolower($this->GetFilenameExtension($filename)) == 'eml' ) { $encoding = '7bit'; } } /** * Using guess options combines all added parts together and returns combined part number * * @return int */ function PrepareMessageBody() { if ($this->bodyPartNumber === false) { $part_number = false; // number of generated body part // 1. set text content of message if ($this->guessOptions['text_part'] !== false && $this->guessOptions['html_part'] !== false) { // text & html parts present -> compose into alternative part $parts = Array ( $this->guessOptions['text_part'], $this->guessOptions['html_part'], ); $part_number = $this->CreateMultipart($parts, 'alternative'); } elseif ($this->guessOptions['text_part'] !== false) { // only text part is defined, then leave as is $part_number = $this->guessOptions['text_part']; } if ($part_number === false) { return $this->SetError('MESSAGE_TEXT_MISSING'); } // 2. if inline attachments found, then create related multipart from text & inline attachments if ($this->guessOptions['inline_attachments']) { $parts = array_merge(Array($part_number), $this->guessOptions['inline_attachments']); $part_number = $this->CreateMultipart($parts, 'related'); } // 3. if normal attachments found, then create mixed multipart from text & attachments if ($this->guessOptions['attachments']) { $parts = array_merge(Array($part_number), $this->guessOptions['attachments']); $part_number = $this->CreateMultipart($parts, 'mixed'); } $this->bodyPartNumber = $part_number; } return $this->bodyPartNumber; } /** * Returns message headers and body part (by reference in parameters) * * @param Array $message_headers * @param string $message_body * @return bool */ function GetHeadersAndBody(&$message_headers, &$message_body) { $part_number = $this->PrepareMessageBody(); if ($part_number === false) { return $this->SetError('MESSAGE_COMPOSE_ERROR'); } $message_headers = $this->GetPartHeaders($part_number); // join message headers and body headers $message_headers = array_merge($this->headers, $message_headers); $message_headers['MIME-Version'] = '1.0'; if ($this->mailerName) { $message_headers['X-Mailer'] = $this->mailerName; } $this->GenerateMessageID($message_headers['From']); $valid_headers = $this->ValidateHeaders($message_headers); if ($valid_headers) { // set missing headers from existing $from_headers = Array ('Reply-To', 'Errors-To'); foreach ($from_headers as $header_name) { if (!isset($message_headers[$header_name])) { $message_headers[$header_name] = $message_headers['From']; } } $message_body = $this->GetPartBody($part_number); return true; } return false; } /** * Checks that all required headers are set and not empty * * @param Array $message_headers * @return bool */ function ValidateHeaders($message_headers) { $from = isset($message_headers['From']) ? $message_headers['From'] : ''; if (!$from) { return $this->SetError('HEADER_MISSING', Array('From')); } if (!isset($message_headers['To'])) { return $this->SetError('HEADER_MISSING', Array('To')); } if (!isset($message_headers['Subject'])) { return $this->SetError('HEADER_MISSING', Array('Subject')); } return true; } /** * Returns full message source (headers + body) for sending to SMTP server * * @return string */ function GetMessage() { $composed = $this->GetHeadersAndBody($message_headers, $message_body); if ($composed) { // add headers to resulting message $message = ''; foreach ($message_headers as $header_name => $header_value) { $message .= $header_name.': '.$header_value.$this->line_break; } // add message body $message .= $this->line_break.$message_body; return $message; } return false; } /** * Sets just happened error code * * @param string $code * @param Array $params additional error params * @param bool $fatal * @return bool */ function SetError($code, $params = null, $fatal = true) { $error_msgs = Array ( 'MAIL_NOT_FOUND' => 'the mail() function is not available in this PHP installation', 'MISSING_CONTENT_TYPE' => 'it was added a part without Content-Type: defined', 'INVALID_CONTENT_TYPE' => 'Content-Type: %s not yet supported', 'INVALID_MULTIPART_SUBTYPE' => 'multipart Content-Type sub_type %s not yet supported', 'FILE_PART_OPEN_ERROR' => 'could not open part file %s', 'FILE_PART_DATA_ERROR' => 'the length of the file that was read does not match the size of the part file %s due to possible data corruption', 'FILE_PART_DATA_MISSING' => 'it was added a part without a body PART', 'INVALID_ENCODING' => '%s is not yet a supported encoding type', 'MISSING_FILE_DATA' => 'file part data is missing', 'MISSING_FILE_NAME' => 'it is not possible to determine content type from the name', 'INVALID_DISPOSITION' => '%s is not a supported message part content disposition', 'MESSAGE_TEXT_MISSING' => 'text part of message was not defined', 'MESSAGE_COMPOSE_ERROR' => 'unknown message composing error', 'HEADER_MISSING' => 'header %s is required', // SMTP errors 'INVALID_COMMAND' => 'Commands cannot contain newlines', 'CONNECTION_TERMINATED' => 'Connection was unexpectedly closed', 'HELO_ERROR' => 'HELO was not accepted: %s', 'AUTH_METHOD_NOT_SUPPORTED' => '%s is not a supported authentication method', 'AUTH_METHOD_NOT_IMPLEMENTED' => '%s is not a implemented authentication method', ); if (!is_array($params)) { $params = Array (); } $error_msg = 'mail error: ' . vsprintf($error_msgs[$code], $params); if ( $this->_logData ) { $this->_logData['Status'] = EmailLogStatus::ERROR; $this->_logData['ErrorMessage'] = $error_msg; $this->Conn->doInsert($this->_logData, TABLE_PREFIX . 'EmailLog'); } else { if ($fatal) { throw new Exception($error_msg); } else { if ( $this->Application->isDebugMode() ) { $this->Application->Debugger->appendTrace(); } trigger_error($error_msg, E_USER_WARNING); } } return false; } /** * Simple method of message sending * * @param string $from_email * @param string $to_email * @param string $subject * @param string $from_name * @param string $to_name */ function Send($from_email, $to_email, $subject, $from_name = '', $to_name = '') { $this->SetSubject($subject); $this->SetFrom($from_email, trim($from_name) ? trim($from_name) : $from_email); if (!isset($this->headers['Return-Path'])) { $this->SetReturnPath($from_email); } $this->SetTo($to_email, $to_name ? $to_name : $to_email); return $this->Deliver(); } /** * Prepares class for sending another message * */ function Clear() { $this->headers = Array (); $this->bodyPartNumber = false; $this->parts = Array (); $this->guessOptions = Array ( 'attachments' => Array (), 'inline_attachments' => Array (), 'text_part' => false, 'html_part' => false, ); $this->SetCharset(null, true); $this->_logData = Array (); } /** * Sends message via php mail function * * @param Array $message_headers * @param string $body * * @return bool */ function SendMail($message_headers, &$body) { if (!function_exists('mail')) { return $this->SetError('MAIL_NOT_FOUND'); } $to = $message_headers['To']; $subject = $message_headers['Subject']; $return_path = $message_headers['Return-Path']; unset($message_headers['To'], $message_headers['Subject']); $headers = ''; $header_separator = $this->Application->ConfigValue('MailFunctionHeaderSeparator') == 1 ? "\n" : "\r\n"; foreach ($message_headers as $header_name => $header_value) { $headers .= $header_name.': '.$header_value.$header_separator; } if ($return_path) { if (kUtil::constOn('SAFE_MODE') || (defined('PHP_OS') && substr(PHP_OS, 0, 3) == 'WIN')) { // safe mode restriction OR is windows $return_path = ''; } } return mail($to, $subject, $body, $headers, $return_path ? '-f'.$return_path : null); } /** * Sends message via SMTP server * * @param Array $message_headers * @param string $message_body * @return bool */ function SendSMTP($message_headers, &$message_body) { if (!$this->SmtpConnect()) { return false; } $from = $this->ExtractRecipientEmail($message_headers['From']); if (!$this->SmtpSetFrom($from)) { return false; } $recipients = ''; $recipient_headers = Array ('To', 'Cc', 'Bcc'); foreach ($recipient_headers as $recipient_header) { if (isset($message_headers[$recipient_header])) { $recipients .= ' '.$message_headers[$recipient_header]; } } $recipients_accepted = 0; $recipients = $this->ExtractRecipientEmail($recipients, true); foreach ($recipients as $recipient) { if ($this->SmtpAddTo($recipient)) { $recipients_accepted++; } } if ($recipients_accepted == 0) { // none of recipients were accepted return false; } $headers = ''; foreach ($message_headers as $header_name => $header_value) { $headers .= $header_name.': '.$header_value.$this->line_break; } if (!$this->SmtpSendMessage($headers . "\r\n" . $message_body)) { return false; } $this->SmtpDisconnect(); return true; } /** * Send a command to the server with an optional string of * arguments. A carriage return / linefeed (CRLF) sequence will * be appended to each command string before it is sent to the * SMTP server. * * @param string $command The SMTP command to send to the server. * @param string $args A string of optional arguments to append to the command. * * @return bool * */ function SmtpSendCommand($command, $args = '') { if (!empty($args)) { $command .= ' ' . $args; } if (strcspn($command, "\r\n") !== strlen($command)) { return $this->SetError('INVALID_COMMAND'); } return $this->smtpSocket->write($command . "\r\n") === false ? false : true; } /** * Read a reply from the SMTP server. The reply consists of a response code and a response message. * * @param mixed $valid The set of valid response codes. These may be specified as an array of integer values or as a single integer value. * * @return bool * */ function SmtpParseResponse($valid) { $this->smtpResponceCode = -1; $this->smtpRespoceArguments = array(); while ($line = $this->smtpSocket->readLine()) { // If we receive an empty line, the connection has been closed. if (empty($line)) { $this->SmtpDisconnect(); return $this->SetError('CONNECTION_TERMINATED', null, false); } // Read the code and store the rest in the arguments array. $code = substr($line, 0, 3); $this->smtpRespoceArguments[] = trim(substr($line, 4)); // Check the syntax of the response code. if (is_numeric($code)) { $this->smtpResponceCode = (int)$code; } else { $this->smtpResponceCode = -1; break; } // If this is not a multiline response, we're done. if (substr($line, 3, 1) != '-') { break; } } // Compare the server's response code with the valid code. if (is_int($valid) && ($this->smtpResponceCode === $valid)) { return true; } // If we were given an array of valid response codes, check each one. if (is_array($valid)) { foreach ($valid as $valid_code) { if ($this->smtpResponceCode === $valid_code) { return true; } } } return false; } /** * Attempt to connect to the SMTP server. * * @param int $timeout The timeout value (in seconds) for the socket connection. * @param bool $persistent Should a persistent socket connection be used ? * * @return bool * */ function SmtpConnect($timeout = null, $persistent = false) { $result = $this->smtpSocket->connect($this->smtpParams['server'], $this->smtpParams['port'], $persistent, $timeout); if (!$result) { return false; } if ($this->SmtpParseResponse(220) === false) { return false; } elseif ($this->SmtpNegotiate() === false) { return false; } if ($this->smtpParams['use_auth']) { $result = $this->SmtpAuthentificate($this->smtpParams['username'], $this->smtpParams['password']); if (!$result) { // authentification failed return false; } } return true; } /** * Attempt to disconnect from the SMTP server. * * @return bool */ function SmtpDisconnect() { if ($this->SmtpSendCommand('QUIT') === false) { return false; } elseif ($this->SmtpParseResponse(221) === false) { return false; } return $this->smtpSocket->disconnect(); } /** * Attempt to send the EHLO command and obtain a list of ESMTP * extensions available, and failing that just send HELO. * * @return bool */ function SmtpNegotiate() { if (!$this->SmtpSendCommand('EHLO', $this->smtpParams['localhost'])) { return false; } if (!$this->SmtpParseResponse(250)) { // If we receive a 503 response, we're already authenticated. if ($this->smtpResponceCode === 503) { return true; } // If the EHLO failed, try the simpler HELO command. if (!$this->SmtpSendCommand('HELO', $this->smtpParams['localhost'])) { return false; } if (!$this->SmtpParseResponse(250)) { return $this->SetError('HELO_ERROR', Array($this->smtpResponceCode), false); } return true; } foreach ($this->smtpRespoceArguments as $argument) { $verb = strtok($argument, ' '); $arguments = substr($argument, strlen($verb) + 1, strlen($argument) - strlen($verb) - 1); $this->smtpFeatures[$verb] = $arguments; } return true; } /** * Attempt to do SMTP authentication. * * @param string $uid The userid to authenticate as. * @param string $pwd The password to authenticate with. * @param string $method The requested authentication method. If none is specified, the best supported method will be used. * * @return bool */ function SmtpAuthentificate($uid, $pwd , $method = '') { if (empty($this->smtpFeatures['AUTH'])) { // server doesn't understand AUTH command, then don't authentificate return true; } $available_methods = explode(' ', $this->smtpFeatures['AUTH']); // methods supported by SMTP server if (empty($method)) { foreach ($this->smtpAuthMethods as $supported_method) { // check if server supports methods, that we have implemented if (in_array($supported_method, $available_methods)) { $method = $supported_method; break; } } } else { $method = strtoupper($method); } if (!in_array($method, $available_methods)) { // coosen method is not supported by server return $this->SetError('AUTH_METHOD_NOT_SUPPORTED', Array($method)); } switch ($method) { case 'CRAM-MD5': $result = $this->_authCRAM_MD5($uid, $pwd); break; case 'LOGIN': $result = $this->_authLogin($uid, $pwd); break; case 'PLAIN': $result = $this->_authPlain($uid, $pwd); break; default: return $this->SetError('AUTH_METHOD_NOT_IMPLEMENTED', Array($method)); break; } return $result; } /** * Function which implements HMAC MD5 digest * * @param string $key The secret key * @param string $data The data to protect * @return string The HMAC MD5 digest */ function _HMAC_MD5($key, $data) { if (strlen($key) > 64) { $key = pack('H32', md5($key)); } if (strlen($key) < 64) { $key = str_pad($key, 64, chr(0)); } $k_ipad = substr($key, 0, 64) ^ str_repeat(chr(0x36), 64); $k_opad = substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64); $inner = pack('H32', md5($k_ipad . $data)); $digest = md5($k_opad . $inner); return $digest; } /** * Authenticates the user using the CRAM-MD5 method. * * @param string $uid The userid to authenticate as. * @param string $pwd The password to authenticate with. * * @return bool */ function _authCRAM_MD5($uid, $pwd) { if (!$this->SmtpSendCommand('AUTH', 'CRAM-MD5')) { return false; } // 334: Continue authentication request if (!$this->SmtpParseResponse(334)) { // 503: Error: already authenticated return $this->smtpResponceCode === 503 ? true : false; } $challenge = base64_decode($this->smtpRespoceArguments[0]); $auth_str = base64_encode($uid . ' ' . $this->_HMAC_MD5($pwd, $challenge)); if (!$this->SmtpSendCommand($auth_str)) { return false; } // 235: Authentication successful if (!$this->SmtpParseResponse(235)) { return false; } return true; } /** * Authenticates the user using the LOGIN method. * * @param string $uid The userid to authenticate as. * @param string $pwd The password to authenticate with. * * @return bool */ function _authLogin($uid, $pwd) { if (!$this->SmtpSendCommand('AUTH', 'LOGIN')) { return false; } // 334: Continue authentication request if (!$this->SmtpParseResponse(334)) { // 503: Error: already authenticated return $this->smtpResponceCode === 503 ? true : false; } if (!$this->SmtpSendCommand(base64_encode($uid))) { return false; } // 334: Continue authentication request if (!$this->SmtpParseResponse(334)) { return false; } if (!$this->SmtpSendCommand(base64_encode($pwd))) { return false; } // 235: Authentication successful if (!$this->SmtpParseResponse(235)) { return false; } return true; } /** * Authenticates the user using the PLAIN method. * * @param string $uid The userid to authenticate as. * @param string $pwd The password to authenticate with. * * @return bool */ function _authPlain($uid, $pwd) { if (!$this->SmtpSendCommand('AUTH', 'PLAIN')) { return false; } // 334: Continue authentication request if (!$this->SmtpParseResponse(334)) { // 503: Error: already authenticated return $this->smtpResponceCode === 503 ? true : false; } $auth_str = base64_encode(chr(0) . $uid . chr(0) . $pwd); if (!$this->SmtpSendCommand($auth_str)) { return false; } // 235: Authentication successful if (!$this->SmtpParseResponse(235)) { return false; } return true; } /** * Send the MAIL FROM: command. * * @param string $sender The sender (reverse path) to set. * @param string $params String containing additional MAIL parameters, such as the NOTIFY flags defined by RFC 1891 or the VERP protocol. * * @return bool */ function SmtpSetFrom($sender, $params = null) { $args = "FROM:<$sender>"; if (is_string($params)) { $args .= ' ' . $params; } if (!$this->SmtpSendCommand('MAIL', $args)) { return false; } if (!$this->SmtpParseResponse(250)) { return false; } return true; } /** * Send the RCPT TO: command. * * @param string $recipient The recipient (forward path) to add. * @param string $params String containing additional RCPT parameters, such as the NOTIFY flags defined by RFC 1891. * * @return bool */ function SmtpAddTo($recipient, $params = null) { $args = "TO:<$recipient>"; if (is_string($params)) { $args .= ' ' . $params; } if (!$this->SmtpSendCommand('RCPT', $args)) { return false; } if (!$this->SmtpParseResponse(array(250, 251))) { return false; } return true; } /** * Send the DATA command. * * @param string $data The message body to send. * * @return bool */ function SmtpSendMessage($data) { /* RFC 1870, section 3, subsection 3 states "a value of zero * indicates that no fixed maximum message size is in force". * Furthermore, it says that if "the parameter is omitted no * information is conveyed about the server's fixed maximum * message size". */ if (isset($this->smtpFeatures['SIZE']) && ($this->smtpFeatures['SIZE'] > 0)) { if (strlen($data) >= $this->smtpFeatures['SIZE']) { $this->SmtpDisconnect(); return $this->SetError('Message size excedes the server limit', null, false); } } // Quote the data based on the SMTP standards // Change Unix (\n) and Mac (\r) linefeeds into Internet-standard CRLF (\r\n) linefeeds. $data = preg_replace(Array('/(?<!\r)\n/','/\r(?!\n)/'), "\r\n", $data); // Because a single leading period (.) signifies an end to the data, // legitimate leading periods need to be "doubled" (e.g. '..') $data = str_replace("\n.", "\n..", $data); if (!$this->SmtpSendCommand('DATA')) { return false; } if (!$this->SmtpParseResponse(354)) { return false; } if ($this->smtpSocket->write($data . "\r\n.\r\n") === false) { return false; } if (!$this->SmtpParseResponse(250)) { return false; } return true; } /** * Sets global charset for every message part * * @param string $charset * @param bool $is_system set charset to default for current language */ function SetCharset($charset, $is_system = false) { $this->charset = $is_system ? CHARSET : $charset; } /** * Allows to extract recipient's name from text by specifying it's email * * @param string $text * @param string $email * @return string */ function ExtractRecipientName($text, $email = '') { $lastspace = mb_strrpos($text, ' '); $name = trim(mb_substr($text, 0, $lastspace - mb_strlen($text)), " \r\n\t\0\x0b\"'"); if (empty($name)) { $name = $email; } return $name; } /** * Takes $text and returns an email address from it * Set $multiple to true to retrieve all found addresses * Returns false if no addresses were found * * @param string $text * @param bool $multiple * @param bool $allow_only_domain * @return Array|bool * @access public */ public function ExtractRecipientEmail($text, $multiple = false, $allow_only_domain = false) { if ( $allow_only_domain ) { $pattern = '/((' . REGEX_EMAIL_USER . '@)?' . REGEX_EMAIL_DOMAIN . ')/i'; } else { $pattern = '/(' . REGEX_EMAIL_USER . '@' . REGEX_EMAIL_DOMAIN . ')/i'; } if ( $multiple ) { if ( preg_match_all($pattern, $text, $found_emails) >= 1 ) { return $found_emails[1]; } else { return false; } } else { if ( preg_match($pattern, $text, $found_emails) == 1 ) { return $found_emails[1]; } else { return false; } } } /** * Returns array of recipient names and emails * * @param string $list * @param string $separator * @return Array */ function GetRecipients($list, $separator = ';') { // by MIME specs recipients should be separated using "," symbol, // but users can write ";" too (like in OutLook) if (!trim($list)) { return false; } $list = explode(',', str_replace($separator, ',', $list)); $ret = Array (); foreach ($list as $recipient) { $email = $this->ExtractRecipientEmail($recipient); if (!$email) { // invalid email format -> error return false; } $name = $this->ExtractRecipientName($recipient, $email); $ret[] = Array('Name' => $name, 'Email' => $email); } return $ret; } /* methods for nice header setting */ /** * Sets "From" header. * * @param string $email * @param string $first_last_name FirstName and LastName or just FirstName * @param string $last_name LastName (if not specified in previous parameter) */ function SetFrom($email, $first_last_name, $last_name = '') { $name = rtrim($first_last_name.' '.$last_name, ' '); $this->SetEncodedEmailHeader('From', $email, $name ? $name : $email); if (!isset($this->headers['Return-Path'])) { $this->SetReturnPath($email); } } /** * Sets "To" header. * * @param string $email * @param string $first_last_name FirstName and LastName or just FirstName * @param string $last_name LastName (if not specified in previous parameter) */ function SetTo($email, $first_last_name, $last_name = '') { $name = rtrim($first_last_name.' '.$last_name, ' '); $email = $this->_replaceRecipientEmail($email); $this->SetEncodedEmailHeader('To', $email, $name ? $name : $email); } /** * Sets "Return-Path" header (useful for spammers) * * @param string $email */ function SetReturnPath($email) { $this->SetHeader('Return-Path', $email); } /** * Adds one more recipient into "To" header * * @param string $email * @param string $first_last_name FirstName and LastName or just FirstName * @param string $last_name LastName (if not specified in previous parameter) */ function AddTo($email, $first_last_name = '', $last_name = '') { $name = rtrim($first_last_name.' '.$last_name, ' '); $this->AddRecipient('To', $email, $name); } /** * Allows to replace recipient in all sent emails (used for debugging) * * @param string $email * @return string */ function _replaceRecipientEmail($email) { if ( defined('OVERRIDE_EMAIL_RECIPIENTS') && OVERRIDE_EMAIL_RECIPIENTS ) { if ( substr(OVERRIDE_EMAIL_RECIPIENTS, 0, 1) == '@' ) { // domain $email = str_replace('@', '_at_', $email) . OVERRIDE_EMAIL_RECIPIENTS; } else { $email = OVERRIDE_EMAIL_RECIPIENTS; } } return $email; } /** * Adds one more recipient into "Cc" header * * @param string $email * @param string $first_last_name FirstName and LastName or just FirstName * @param string $last_name LastName (if not specified in previous parameter) */ function AddCc($email, $first_last_name = '', $last_name = '') { $name = rtrim($first_last_name.' '.$last_name, ' '); $this->AddRecipient('Cc', $email, $name); } /** * Adds one more recipient into "Bcc" header * * @param string $email * @param string $first_last_name FirstName and LastName or just FirstName * @param string $last_name LastName (if not specified in previous parameter) */ function AddBcc($email, $first_last_name = '', $last_name = '') { $name = rtrim($first_last_name.' '.$last_name, ' '); $this->AddRecipient('Bcc', $email, $name); } /** * Adds one more recipient to specified header * * @param string $header_name * @param string $email * @param string $name */ function AddRecipient($header_name, $email, $name = '') { $email = $this->_replaceRecipientEmail($email); if (!$name) { $name = $email; } $value = isset($this->headers[$header_name]) ? $this->headers[$header_name] : ''; if ( $value ) { // not first recipient added - separate with comma $value .= ', '; } $value .= $this->QuotedPrintableEncode($name, $this->charset) . ' <' . $email . '>'; $this->SetHeader($header_name, $value); } /** + * Returns list of recipients from given header. + * + * @param string $header_name Header name. + * + * @return array + */ + public function GetRecipientsByHeader($header_name) + { + if ( !isset($this->headers[$header_name]) ) { + return array(); + } + + $decoded_header = $this->decodeHeader($this->headers[$header_name]); + $recipients = $this->GetRecipients($decoded_header); + + if ( $recipients === false ) { + return array(); + } + + return $recipients; + } + + /** + * Decodes header value. + * + * @param string $header_value Header value. + * + * @return string + */ + protected function decodeHeader($header_value) + { + while ( preg_match('/(=\?([^?]+)\?(Q|B)\?([^?]*)\?=)/i', $header_value, $matches) ) { + $encoded = $matches[1]; + $charset = $matches[2]; + $encoding = $matches[3]; + $text = $matches[4]; + + switch ( strtoupper($encoding) ) { + case 'B': + $text = base64_decode($text); + break; + + case 'Q': + $text = str_replace('_', ' ', $text); + preg_match_all('/=([a-f0-9]{2})/i', $text, $matches); + + foreach ( $matches[1] as $value ) { + $text = str_replace('=' . $value, chr(hexdec($value)), $text); + } + break; + } + + $header_value = mb_convert_encoding( + str_replace($encoded, $text, $header_value), + $this->charset, + $charset + ); + } + + return $header_value; + } + + /** * Sets "Subject" header. * * @param string $subject message subject */ function SetSubject($subject) { $this->setEncodedHeader('Subject', $subject); } /** * Sets HTML part of message * * @param string $html */ function SetHTML($html) { $this->CreateTextHtmlPart($html, true); } /** * Sets Plain-Text part of message * * @param string $plain_text */ function SetPlain($plain_text) { $this->CreateTextHtmlPart($plain_text); } /** * Sets HTML and optionally plain part of the message * * @param string $html * @param string $plain_text */ function SetBody($html, $plain_text = '') { $this->SetHTML($html); if ($plain_text) { $this->SetPlain($plain_text); } } /** * Performs mail delivery (supports delayed delivery) * * @param string $message message, if not given, then use composed one * @param bool $immediate_send send message now or MailingId * @param bool $immediate_clear clear message parts after message is sent * @return bool */ function Deliver($message = null, $immediate_send = true, $immediate_clear = true) { if (isset($message)) { // if message is given directly, then use it if (is_array($message)) { $message_headers =& $message[0]; $message_body =& $message[1]; } else { $message_headers = Array (); list ($headers, $message_body) = explode("\n\n", $message, 2); $headers = explode("\n", $headers); foreach ($headers as $header) { $header = explode(':', $header, 2); $message_headers[ trim($header[0]) ] = trim($header[1]); } } $composed = true; } else { // direct message not given, then assemble message from available parts $composed = $this->GetHeadersAndBody($message_headers, $message_body); } if ( $composed ) { if ( $immediate_send === true ) { $send_method = 'Send' . $this->sendMethod; $result = $this->$send_method($message_headers, $message_body); if ( $result && $this->_logData ) { // add e-mail log record $this->Conn->doInsert($this->_logData, TABLE_PREFIX . 'EmailLog'); } if ( $immediate_clear ) { $this->Clear(); } return $result; } else { $fields_hash = Array ( 'ToEmail' => $message_headers['To'], 'Subject' => $message_headers['Subject'], 'Queued' => time(), 'SendRetries' => 0, 'LastSendRetry' => 0, 'MailingId' => (int)$immediate_send, 'LogData' => serialize($this->_logData), // remember e-mail log record ); $fields_hash['MessageHeaders'] = serialize($message_headers); $fields_hash['MessageBody'] =& $message_body; $this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'EmailQueue'); if ( $immediate_clear ) { $this->Clear(); } } } // if not immediate send, then send result is positive :) return $immediate_send !== true ? true : false; } /** * Sets log data * * @param string $log_data * @return void * @access public */ public function setLogData($log_data) { $this->_logData = $log_data; } } Index: branches/5.3.x/core/kernel/utility/formatters/date_formatter.php =================================================================== --- branches/5.3.x/core/kernel/utility/formatters/date_formatter.php (revision 16394) +++ branches/5.3.x/core/kernel/utility/formatters/date_formatter.php (revision 16395) @@ -1,509 +1,524 @@ <?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 kDateFormatter extends kFormatter { /** * Current Language * * @var LanguagesItem */ var $language = null; /** * Create date formatter * * @access public */ public function __construct() { parent::__construct(); $this->language = $this->Application->recallObject('lang.current'); } /** * Sets mixed format (date + time) for field if not set directly * * @param Array $field_options options of field * @param Array $format separate formats for date & time * @param string $type destination key in field_options to store mixed format */ function SetMixedFormat(&$field_options, &$format, $type) { if (!isset($field_options[$type])) { // default value is date+separator+time $field_options[$type] = '_regional_DateTimeFormat'; } if ($field_options[$type] == '_regional_DateTimeFormat') { $field_options[$type] = $format['date'].$field_options['date_time_separator'].$format['time']; } else if(preg_match('/_regional_(.*)/', $field_options[$type], $regs)) { $field_options[$type] = $this->language->GetDBField($regs[1]); } $format['mixed'] = $field_options[$type]; } /** * Returns separate formats for date,time,combined for input & display formats * * @param Array $field_options options of field * @param string $type type of requested information = {mixed,date,time} * @return Array display & input formats */ function GetSeparateFormats(&$field_options, $type) { if ($type == 'mixed') { if (!isset($field_options['date_time_separator'])) $field_options['date_time_separator'] = ' '; $display_format = Array (); $input_format = Array (); list ($display_format['date'], $input_format['date']) = $this->GetSeparateFormats($field_options, 'date'); list ($display_format['time'], $input_format['time']) = $this->GetSeparateFormats($field_options, 'time'); $this->SetMixedFormat($field_options, $display_format, 'format'); $this->SetMixedFormat($field_options, $input_format, 'input_format'); return Array ($display_format, $input_format); } else { // 1. set display format if (isset($field_options[$type.'_format'])) { $format = $field_options[$type.'_format']; } else { $format = $this->language->GetDBField(ucfirst($type).'Format'); } // 2. set input format if (isset($field_options['input_'.$type.'_format'])) { $input_format = $field_options['input_'.$type.'_format']; } else { $input_format = $this->language->GetDBField('Input'.ucfirst($type).'Format'); } return Array ($format, $input_format); } } /** * 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) { list ($display_format, $input_format) = $this->GetSeparateFormats($field_options, 'mixed'); $field_options['sub_fields'] = Array('date' => $field_name.'_date', 'time' => $field_name.'_time'); if (!isset($field_options['use_timezone'])) { // apply timezone from server $field_options['use_timezone'] = true; } // 1. add field to indicate, that date is already combined into one field $add_fields = Array ( $field_name . '_combined' => Array ('type' => 'int', 'default' => 0), ); // 2. add DATE virtual field $opts = Array('master_field' => $field_name, 'formatter' => 'kDateFormatter', 'format' => $display_format['date'], 'input_format' => $input_format['date']); $copy_options = Array ('type', 'default', 'required', 'use_timezone', 'error_msgs'); foreach ($copy_options as $copy_option) { if ( array_key_exists($copy_option, $field_options) ) { $opts[$copy_option] = $field_options[$copy_option]; } } $add_fields[$field_name . '_date'] = $opts; // 3. add TIME virtual field $opts['format'] = $display_format['time']; $opts['input_format'] = $input_format['time']; $add_fields[$field_name . '_time'] = $opts; $filter_type = getArrayValue($field_options, 'filter_type'); if ( $filter_type == 'range' ) { $opts['format'] = $field_options['format']; $add_fields[$field_name . '_rangefrom'] = $opts; $add_fields[$field_name . '_rangeto'] = $opts; } if ( !$object->isVirtualField($field_name) ) { // adding calculated field to format date directly in the query $object->addCalculatedField($field_name . '_date', '%1$s.' . $field_name); $object->addCalculatedField($field_name . '_time', '%1$s.' . $field_name); } $virtual_fields = $object->getVirtualFields(); $add_fields = kUtil::array_merge_recursive($add_fields, $virtual_fields); $object->setVirtualFields($add_fields); } /** * 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 $object * @return void * @access public */ public function UpdateSubFields($field, $value, &$options, &$object) { $sub_fields = getArrayValue($options, 'sub_fields'); if ( !$sub_fields || !isset($value) || !$value ) { return ; } $object->SetDBField($sub_fields['date'], $value); $object->SetDBField($sub_fields['time'], $value); } /** * 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 $object */ function UpdateMasterFields($field, $value, &$options, &$object) { $sub_fields = getArrayValue($options, 'sub_fields'); $master_field = getArrayValue($options, 'master_field'); if ( $master_field ) { // when in one of sub_fields - call update for master_field to update its value from sub_fields [are you following ? :) ] $opt = $object->GetFieldOptions($master_field); $this->UpdateMasterFields($master_field, null, $opt, $object); } elseif ( $sub_fields && !$object->GetDBField($field . '_combined') ) { // when in master field - set own value from sub_fields if ( $object->GetDBField($sub_fields['date']) != '' && $object->GetDBField($sub_fields['time']) == '' ) { // when time is not supplied, then use "midnight" (or unit config override) $empty_time = getArrayValue($options, 'empty_time'); if ( $empty_time === false ) { $empty_time = mktime(0, 0, 0); } $object->SetDBField($sub_fields['time'], $empty_time); } elseif ( $object->GetDBField($sub_fields['time']) != '' && $object->GetDBField($sub_fields['date']) == '' ) { // when date is not supplied, then use "1970-01-01 00:00:00" instead (or unit config override) $empty_date = getArrayValue($options, 'empty_date'); if ( $empty_date === false ) { $empty_date = mktime(0, 0, 0, 1, 1, 1970); } $object->SetDBField($sub_fields['date'], $empty_date); } $input_format['date'] = $object->GetFieldOption($sub_fields['date'], 'input_format'); $input_format['time'] = $object->GetFieldOption($sub_fields['time'], 'input_format'); $object->SetField($field, $object->GetField($sub_fields['date'], $input_format['date']) . $options['date_time_separator'] . $object->GetField($sub_fields['time'], $input_format['time'])); } } /** * Formats value of a given field * - * @param string $value - * @param string $field_name - * @param kDBItem|kDBList $object - * @param string $format + * @param string $value Value. + * @param string $field_name Field name. + * @param kDBBase $object Object. + * @param string $format Format. + * * @return string */ - function Format($value, $field_name, &$object, $format=null) + public function Format($value, $field_name, &$object, $format = null) { - if ( is_null($value) ) return ''; + $options = $object->GetFieldOptions($field_name); + + if ( is_null($value) ) { + if ( $format != 'picker' || !isset($options['picker_default']) ) { + return ''; + } + + $value = strtotime($options['picker_default']); + } + + if ( $format == 'picker' ) { + $format = '_input_'; + } + if ( !is_numeric($value) ) { return $value; // for leaving badly formatted date on the form } + settype($value, 'int'); + if ( !is_int($value) ) { return $value; } - $options = $object->GetFieldOptions($field_name); if ( isset($format) ) { $options['format'] = $format; } - if (preg_match('/_regional_(.*)/', $options['format'], $regs)) { + if ( preg_match('/_regional_(.*)/', $options['format'], $regs) ) { // when such type of format is given directly to kDBBase::GetField $options['format'] = $this->language->GetDBField($regs[1]); } - if ($options['format'] == '_input_') { + if ( $options['format'] == '_input_' ) { // use input format instead of output format $options['format'] = $options['input_format']; } - if (!$options['use_timezone']) { + if ( !$options['use_timezone'] ) { return gmdate($options['format'], $value); } $format = defined($options['format']) ? constant($options['format']) : $options['format']; $dt_separator = getArrayValue($options, 'date_time_separator'); - if ($dt_separator) { + if ( $dt_separator ) { $format = trim($format, $dt_separator); } return date($format, $value); } function HumanFormat($format) { $patterns = Array('/m/', '/n/', '/d/', '/j/', '/y/', '/Y/', '/h|H/', '/g|G/', '/i/', '/s/', '/a|A/'); $replace = Array( 'mm', 'm', 'dd', 'd', 'yy', 'yyyy', 'hh', 'h', 'mm', 'ss', 'AM'); $res = preg_replace($patterns, $replace, $format); return $res; } function SQLFormat($format) { $mapping = Array( '/%/' => '%%', '/(?<!%)a/' => '%p', // Lowercase Ante meridiem and Post meridiem => MySQL provides only uppercase '/(?<!%)A/' => '%p', // Uppercase Ante meridiem and Post meridiem '/(?<!%)d/' => '%d', // Day of the month, 2 digits with leading zeros '/(?<!%)D/' => '%a', // A textual representation of a day, three letters '/(?<!%)F/' => '%M', // A full textual representation of a month, such as January or March '/(?<!%)g/' => '%l', // 12-hour format of an hour without leading zeros '/(?<!%)G/' => '%k', // 24-hour format of an hour without leading zeros '/(?<!%)h/' => '%h', // 12-hour format of an hour with leading zeros '/(?<!%)H/' => '%H', // 24-hour format of an hour with leading zeros '/(?<!%)i/' => '%i', // Minutes with leading zeros '/(?<!%)I/' => 'N/A', // Whether or not the date is in daylights savings time '/(?<!%)S/' => 'N/A', // English ordinal suffix for the day of the month, 2 characters, see below '/jS/' => '%D', // MySQL can't return separate suffix, but could return date with suffix '/(?<!%)j/' => '%e', // Day of the month without leading zeros '/(?<!%)l/' => '%W', // A full textual representation of the day of the week '/(?<!%)L/' => 'N/A', // Whether it's a leap year '/(?<!%)m/' => '%m', // Numeric representation of a month, with leading zeros '/(?<!%)M/' => '%b', // A short textual representation of a month, three letters '/(?<!%)n/' => '%c', // Numeric representation of a month, without leading zeros '/(?<!%)O/' => 'N/A', // Difference to Greenwich time (GMT) in hours '/(?<!%)r/' => 'N/A', // RFC 2822 formatted date '/(?<!%)s/' => '%s', // Seconds, with leading zeros // S and jS moved before j - see above '/(?<!%)t/' => 'N/A', // Number of days in the given month '/(?<!%)T/' => 'N/A', // Timezone setting of this machine '/(?<!%)U/' => 'N/A', // Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) '/(?<!%)w/' => '%w', // Numeric representation of the day of the week '/(?<!%)W/' => '%v', // ISO-8601 week number of year, weeks starting on Monday (added in PHP 4.1.0) '/(?<!%)Y/' => '%Y', // A full numeric representation of a year, 4 digits '/(?<!%)y/' => '%y', // A two digit representation of a year '/(?<!%)z/' => 'N/A', // The day of the year (starting from 0) => MySQL starts from 1 '/(?<!%)Z/' => 'N/A', // Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those east of UTC is always positive. ); $patterns = array_keys($mapping); $replacements = array_values($mapping); $res = preg_replace($patterns, $replacements, $format); return $res; } /** * Converts formatted date+time to timestamp and validates format * * @param mixed $value * @param string $field_name * @param kDBItem $object * @return mixed * @access public */ public function Parse($value, $field_name, &$object) { $options = $object->GetFieldOptions($field_name); $dt_separator = getArrayValue($options,'date_time_separator'); if($dt_separator) $value = trim($value, $dt_separator); if($value == '') return NULL; //return strtotime($value); $format = $options['input_format']; if ($dt_separator) $format = trim($format, $dt_separator); $error_params = Array( $this->HumanFormat($format), date($format), 'value' => $value ); $hour = 0; $minute = 0; $second = 0; $month = 1; $day = 1; $year = 1970; $patterns['n'] = '([0-9]{1,2})'; $patterns['m'] = '([0-9]{1,2})'; $patterns['d'] = '([0-9]{1,2})'; $patterns['j'] = '([0-9]{1,2})'; $patterns['Y'] = '([0-9]{4})'; $patterns['y'] = '([0-9]{2})'; $patterns['G'] = '([0-9]{1,2})'; $patterns['g'] = '([0-9]{1,2})'; $patterns['H'] = '([0-9]{2})'; $patterns['h'] = '([0-9]{2})'; $patterns['i'] = '([0-9]{2})'; $patterns['s'] = '([0-9]{2})'; $patterns['a'] = '(am|pm)'; $patterns['A'] = '(AM|PM)'; $holders_mask = '/' . preg_replace('/[a-zA-Z]{1}/i', '([a-zA-Z]{1})', preg_quote($format, '/')) . '/'; if (!preg_match($holders_mask, $format, $holders)) { $object->SetError($field_name, 'bad_date_format', null, $error_params); return $value; } $values_mask = '/^' . preg_quote($format, '/') . '$/'; foreach ($patterns as $key => $val) { $values_mask = str_replace($key, $val, $values_mask); } if (!preg_match($values_mask, $value, $values)) { $object->SetError($field_name, 'bad_date_format', null, $error_params); return $value; } for ($i = 1; $i < count($holders); $i++) { switch ($holders[$i]) { case 'n': case 'm': $month = $values[$i]; $month = preg_replace('/^0{1}/', '', $month); break; case 'd': $day = $values[$i]; $day = preg_replace('/^0{1}/', '', $day); break; case 'Y': $year = $values[$i]; break; case 'y': $year = $values[$i] >= 70 ? 1900 + $values[$i] : 2000 + $values[$i]; break; case 'H': case 'h': case 'G': case 'g': $hour = $values[$i]; $hour = preg_replace('/^0{1}/', '', $hour); break; case 'i': $minute = $values[$i]; $minute = preg_replace('/^0{1}/', '', $minute); break; case 's': $second = $values[$i]; $second = preg_replace('/^0{1}/', '', $second); break; case 'a': case 'A': if ($hour <= 12) { // if AM/PM used with 24-hour - could happen :) if ($values[$i] == 'pm' || $values[$i] == 'PM') { $hour += 12; if ($hour == 24) $hour = 12; } elseif ($values[$i] == 'am' || $values[$i] == 'AM') { if ($hour == 12) $hour = 0; } } break; } } //echo "day: $day, month: $month, year: $year, hour: $hour, minute: $minute<br>"; /*if (!($year >= 1970 && $year <= 2037)) { $object->SetError($field_name, 'bad_date_format', null, $error_params); return $value; }*/ if (!($month >= 1 && $month <= 12)) { $object->SetError($field_name, 'bad_date_format', null, $error_params); return $value; } $months_days = Array ( 1 => 31,2 => 28, 3 => 31, 4 => 30,5 => 31,6 => 30, 7 => 31, 8 => 31,9 => 30,10 => 31,11 => 30,12 => 31); if ($year % 4 == 0) $months_days[2] = 29; if (!($day >=1 && $day <= $months_days[$month])) { $object->SetError($field_name, 'bad_date_format', null, $error_params); return $value; } if (!($hour >=0 && $hour <= 23)) { $object->SetError($field_name, 'bad_date_format', null, $error_params); return $value; } if (!($minute >=0 && $minute <= 59)) { $object->SetError($field_name, 'bad_date_format', null, $error_params); return $value; } if (!($second >=0 && $second <= 59)) { $object->SetError($field_name, 'bad_date_format', null, $error_params); return $value; } if (!$options['use_timezone']) { return gmmktime($hour, $minute, $second, $month, $day, $year); } return mktime($hour, $minute, $second, $month, $day, $year); } function GetSample($field, &$options, &$object) { return $this->Format( time(), $field, $object, $options['input_format']); } -} \ 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 16394) +++ branches/5.3.x/core/kernel/utility/formatters/formatter.php (revision 16395) @@ -1,303 +1,309 @@ <?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 kFormatter extends kBase { /** * Reference to category helper * * @var CategoryHelper */ var $_categoryHelper = null; /** * Creates formatter instance * * @access public */ public function __construct() { parent::__construct(); $this->_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 <strong>' . $field_type . '</strong> (in TypeCast method), please use <strong>float</strong> instead', E_USER_NOTICE); $field_type = 'float'; } elseif ( $field_type == 'string' ) { 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) { + if ( substr($format, -1) === 'f' ) { + // High precision formats (e.g. '%01.20f') are not supported to keep code below as fast as possible. + $value = round($value, substr($format, -2, 1)); + } + $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); + // Remove trailing zeros in decimal part (including "." if any left at the end). + $value = preg_replace('/\.0+$/', '', $value); + $value = preg_replace('/(\.\d+?)0+$/', '$1', $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) { $options = $object->GetFieldOptions($field_name); if ($value == '') { return $options['type'] == 'string' ? $value : null; } $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'] : ''; } } Index: branches/5.3.x/core/kernel/utility/http_query.php =================================================================== --- branches/5.3.x/core/kernel/utility/http_query.php (revision 16394) +++ branches/5.3.x/core/kernel/utility/http_query.php (revision 16395) @@ -1,785 +1,792 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class kHTTPQuery extends Params { /** * Cache of QueryString parameters * from config, that are represented * in environment variable * * @var Array */ protected $discoveredUnits = Array (); /** * $_POST vars * * @var Array * @access private */ var $Post; /** * $_GET vars * * @var Array * @access private */ var $Get; /** * $_COOKIE vars * * @var Array * @access private */ var $Cookie; /** * $_SERVER vars * * @var Array * @access private */ var $Server; /** * $_ENV vars * * @var Array * @access private */ var $Env; /** * Order in what write * all vars together in * the same array * * @var string */ var $Order; /** * Uploaded files info * * @var Array * @access private */ var $Files; var $specialsToRemove = Array(); /** * SessionID is given via "sid" variable in query string * * @var bool */ var $_sidInQueryString = false; /** * Trust information, provided by proxy * * @var bool */ protected $_trustProxy = false; /** * Loads info from $_POST, $_GET and * related arrays into common place * * @param string $order * @access public */ public function __construct($order = 'CGPF') { parent::__construct(); $this->Order = $order; if ( isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') { // when AJAX request is made from jQuery, then create ajax variable, // so any logic based in it (like redirects) will not break down $_GET['ajax'] = 'yes'; } $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 "<strong>remove_specials[' . $prefix_special . ']</strong>" field (no special found)', E_USER_NOTICE); } } $this->_Params = $this->_removeSpecials($this->_Params); } } /** * Finishes initialization of kHTTPQuery class. * * @return void */ protected function AfterInit() { $rewrite_url = $this->Get('_mod_rw_url_'); if ( $this->Application->RewriteURLs() || $rewrite_url ) { // maybe call onafterconfigread here $this->Application->UrlManager->initRewrite(); if ( defined('DEBUG_MODE') && $this->Application->isDebugMode() ) { $this->Application->Debugger->profileStart('url_parsing', 'Parsing <b>MOD_REWRITE</b> url'); $this->Application->UrlManager->rewrite->parseRewriteURL(); $description = 'Parsing <b>MOD_REWRITE</b> url (template: <b>' . $this->Get('t') . '</b>)'; $this->Application->Debugger->profileFinish('url_parsing', $description); } else { $this->Application->UrlManager->rewrite->parseRewriteURL(); } if ( !$rewrite_url && $this->rewriteRedirectRequired() ) { // rewrite url is missing (e.g. not a script from tools folder) $url_params = $this->getRedirectParams(); // no idea about how to check, that given template require category to be passed with it, so pass anyway $url_params['pass_category'] = 1; $url_params['response_code'] = 301; // Moved Permanently trigger_error('Non mod-rewrite url "<strong>' . $_SERVER['REQUEST_URI'] . '</strong>" used', E_USER_NOTICE); $this->Application->Redirect('', $url_params); } } else { $this->Application->VerifyThemeId(); $this->Application->VerifyLanguageId(); } + $virtual_template = $this->Application->getVirtualPageTemplate($this->Get('m_cat_id')); + + if ( ($virtual_template !== false) && preg_match('/external:(.*)/', $virtual_template) ) { + trigger_error('URL of page, that has "External URL" set was accessed: "<strong>' . $_SERVER['REQUEST_URI'] . '</strong>"', E_USER_NOTICE); + $this->Application->Redirect($virtual_template); + } + 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 "<strong>' . $_SERVER['REQUEST_URI'] . '</strong>" 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; } } Index: branches/5.3.x/core/kernel/utility/factory.php =================================================================== --- branches/5.3.x/core/kernel/utility/factory.php (revision 16394) +++ branches/5.3.x/core/kernel/utility/factory.php (revision 16395) @@ -1,513 +1,531 @@ <?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. */ use InPortal\Core\kernel\utility\ClassDiscovery\ClassMapBuilder; defined('FULL_PATH') or die('restricted access!'); class kFactory extends kBase implements kiCacheable { const TYPE_CLASS = 1; const TYPE_INTERFACE = 2; const TYPE_TRAIT = 3; const MODIFIER_ABSTRACT = 1; const MODIFIER_FINAL = 2; /** * Mapping between class name and file name where class definition can be found. * * File path absolute! * * @var array */ protected $classMap = array(); /** * Class information. * * @var array */ protected $classInfo = array(); /** * Information about class usage among other classes. * * @var array */ protected $classTree = array(); /** * The PSR-4 compliant namespace-to-folder mapping. * * @var array */ protected $namespaceMap = array(); /** * Map class names to their pseudo class names, e.g. key=pseudo_class, value=real_class_name. * * @var array */ protected $realClasses = array(); /** * Where all created objects are stored. * * @var kBase[] */ protected $storage = array(); /** * Debug factory internal processing. * * @var boolean */ private $_debugFactory = false; /** * Profile memory usage during class file inclusion. * * @var boolean */ private $_profileMemory = false; /** * Creates factory instance. */ public function __construct() { parent::__construct(); spl_autoload_register(array($this, 'autoload'), true, true); if ( defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode() ) { $this->_debugFactory = defined('DBG_FACTORY') && DBG_FACTORY; $this->_profileMemory = defined('DBG_PROFILE_MEMORY') && DBG_PROFILE_MEMORY; } } /** * Configures module-based autoloader. * * @return void */ public function configureAutoloader() { if ( !$this->Application->ModuleInfo ) { $error_msg = 'Autoloader configuration can be only performed after module information is available'; throw new LogicException($error_msg); } $this->namespaceMap = array(); foreach ( $this->Application->ModuleInfo as $module_name => $module_info ) { if ( $module_name == 'In-Portal' ) { continue; } $this->namespaceMap[$module_info['ClassNamespace']] = rtrim($module_info['Path'], '/'); } // Don't let class map builder rely on auto-loader (especially during upgrade). require_once KERNEL_PATH . '/utility/ClassDiscovery/ClassMapBuilder.php'; if ( defined('IS_INSTALL') && IS_INSTALL ) { // During installation process all modules, because unit configs from all modules are scanned too. $class_map_builders = ClassMapBuilder::createBuilders(); } else { $class_map_builders = ClassMapBuilder::createBuilders($this->Application->ModuleInfo); } foreach ( $class_map_builders as $class_map_builder ) { list($class_map, $class_info) = $class_map_builder->get(); $class_names = array_keys($class_map); $this->classMap = array_merge($this->classMap, $class_map); $this->classInfo = array_merge($this->classInfo, $class_info); $this->realClasses = array_merge($this->realClasses, array_combine($class_names, $class_names)); foreach ( $class_info as $class => $class_data ) { if ( isset($class_data['extends']) ) { foreach ( $class_data['extends'] as $extends_class ) { if ( !isset($this->classTree[$extends_class]) ) { $this->classTree[$extends_class] = array(); } $this->classTree[$extends_class][] = $class; } } } } } /** * Sets data from cache to object. * * @param array $data Data. * * @return void */ public function setFromCache(&$data) { $this->classMap = $data['Factory.Files']; $this->classInfo = $data['Factory.ClassInfo']; $this->classTree = $data['Factory.ClassTree']; $this->namespaceMap = $data['Factory.Namespaces']; $this->realClasses = $data['Factory.realClasses']; } /** * Performs automatic loading of classes registered with the factory. * * @param string $class Class. * * @return boolean|null */ public function autoload($class) { $file = $this->findFile($class); if ( $file ) { kUtil::includeOnce(FULL_PATH . $file); return true; } return null; } /** + * Finds the absolute path to the file where the class is defined. + * + * @param string $class The name of the class. + * + * @return string|false + */ + public function findClassFile($class) + { + $file = $this->findFile($class); + + if ( $file ) { + return FULL_PATH . $file; + } + + return false; + } + + /** * Finds the path to the file where the class is defined. * * @param string $class The name of the class. * * @return string|boolean The path if found, false otherwise. */ protected function findFile($class) { if ( $class[0] == '\\' ) { $class = substr($class, 1); } if ( isset($this->classMap[$class]) ) { return $this->classMap[$class]; } $pos = strrpos($class, '\\'); if ( $pos !== false ) { // Namespaced class name. $class_path = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos)) . DIRECTORY_SEPARATOR; $class_name = substr($class, $pos + 1); } else { // PEAR-like class name. $class_path = null; $class_name = $class; } $class_path .= str_replace('_', DIRECTORY_SEPARATOR, $class_name) . '.php'; foreach ( $this->namespaceMap as $namespace_prefix => $namespace_path ) { if ( strpos($class, $namespace_prefix) === 0 ) { $test_class_path = str_replace( str_replace('\\', DIRECTORY_SEPARATOR, $namespace_prefix), $namespace_path, $class_path ); if ( file_exists(FULL_PATH . DIRECTORY_SEPARATOR . $test_class_path) ) { return DIRECTORY_SEPARATOR . $test_class_path; } } } return $this->classMap[$class] = false; } /** * Gets object data for caching. * * @return array */ public function getToCache() { ksort($this->classMap); ksort($this->classInfo); ksort($this->classTree); ksort($this->namespaceMap); ksort($this->realClasses); return array( 'Factory.Files' => $this->classMap, 'Factory.ClassInfo' => $this->classInfo, 'Factory.ClassTree' => $this->classTree, 'Factory.Namespaces' => $this->namespaceMap, 'Factory.realClasses' => $this->realClasses, ); } /** * Splits any mixing of prefix and special into correct ones. * * @param string $prefix_special Prefix-special. * * @return array */ public function processPrefix($prefix_special) { // Example: "l.pick", "l", "m.test_TagProcessor". $tmp = explode('_', $prefix_special, 2); $tmp[0] = explode('.', $tmp[0]); $prefix = $tmp[0][0]; $prefix_special = $prefix; if ( isset($tmp[1]) ) { $prefix .= '_' . $tmp[1]; } $special = isset($tmp[0][1]) ? $tmp[0][1] : ''; $prefix_special .= '.' . $special; return array('prefix' => $prefix, 'special' => $special, 'prefix_special' => $prefix_special); } /** * Returns object using params specified, creates it if is required. * * @param string $name Object name in factory. * @param string $pseudo_class Pseudo class. * @param array $event_params Event params. * @param array $arguments Constructor arguments. * * @return kBase */ public function getObject($name, $pseudo_class = '', array $event_params = array(), array $arguments = array()) { $name = rtrim($name, '.'); if ( isset($this->storage[$name]) ) { return $this->storage[$name]; } $ret = $this->processPrefix($name); if ( !$pseudo_class ) { $pseudo_class = $ret['prefix']; } if ( $this->_debugFactory ) { $this->Application->Debugger->appendHTML( '<strong>Creating object:</strong> Pseudo class: ' . $pseudo_class . ' Prefix: ' . $name ); $this->Application->Debugger->appendTrace(); } $this->storage[$name] = $this->makeClass($pseudo_class, $arguments); $this->storage[$name]->Init($ret['prefix'], $ret['special']); $this->Application->EventManager->runBuildEvent($ret['prefix_special'], $pseudo_class, $event_params); return $this->storage[$name]; } /** * Removes object from storage, so next time it could be created from scratch. * * @param string $name Object's name in the Storage. * * @return void */ public function DestroyObject($name) { unset($this->storage[$name]); } /** * Checks if object with prefix passes was already created in factory. * * @param string $name Object pseudo_class, prefix. * * @return boolean */ public function hasObject($name) { return isset($this->storage[$name]); } /** * Get's real class name for pseudo class, includes class file and creates class instance. * * Pattern: Factory Method * * @param string $pseudo_class Pseudo class. * @param array $arguments Constructor arguments. * * @return kBase * @throws kFactoryException When class not found. */ public function makeClass($pseudo_class, array $arguments = array()) { if ( !isset($this->realClasses[$pseudo_class]) ) { $error_msg = 'RealClass not defined for "<strong>' . $pseudo_class . '</strong>" pseudo_class.'; $error_msg .= ' Please use "<strong>in-portal classmap:rebuild</strong>" command to discover new classes.'; if ( $this->Application->isInstalled() ) { throw new kFactoryException($error_msg); } else { if ( $this->Application->isDebugMode() ) { $this->Application->Debugger->appendTrace(); } trigger_error($error_msg, E_USER_WARNING); } return false; } $real_class = $this->realClasses[$pseudo_class]; $mem_before = memory_get_usage(); $time_before = microtime(true); $arguments = (array)$arguments; if ( !$arguments ) { $object = new $real_class(); } else { $reflection = new ReflectionClass($real_class); $object = $reflection->newInstanceArgs($arguments); } if ( $this->_profileMemory ) { $mem_after = memory_get_usage(); $time_after = microtime(true); $mem_used = $mem_after - $mem_before; $mem_used_formatted = round($mem_used / 1024, 3); $time_used = round($time_after - $time_before, 5); $this->Application->Debugger->appendHTML( 'Factory created <b>' . $real_class . '</b> - used ' . $mem_used_formatted . 'Kb time: ' . $time_used ); $this->Application->Debugger->profilerAddTotal('objects', null, $mem_used); } return $object; } /** * Returns sub-classes of given ancestor class. * * @param string $ancestor_class Ancestor class. * @param boolean $concrete_only Return only non-abstract classes. * * @return array * @throws kFactoryException When ancestor class not found. */ public function getSubClasses($ancestor_class, $concrete_only = true) { if ( !isset($this->classMap[$ancestor_class]) ) { throw new kFactoryException( 'Class "<strong>' . $ancestor_class . '</strong>" is not registered in the Factory' ); } if ( !isset($this->classTree[$ancestor_class]) ) { return array(); } $all_sub_classes = array(); foreach ( $this->classTree[$ancestor_class] as $sub_class ) { $real_sub_class = $this->realClasses[$sub_class]; $all_sub_classes[$real_sub_class] = $sub_class; $all_sub_classes = array_merge($all_sub_classes, $this->getSubClasses($sub_class, false)); } if ( $concrete_only ) { $concrete_sub_classes = array(); foreach ( $all_sub_classes as $real_sub_class => $sub_class ) { if ( $this->classInfo[$sub_class]['type'] == self::TYPE_CLASS && !$this->classHasModifier($sub_class, self::MODIFIER_ABSTRACT) ) { $concrete_sub_classes[$real_sub_class] = $sub_class; } } return $concrete_sub_classes; } return $all_sub_classes; } /** * Determines of class has modifier. * * @param string $class Class. * @param integer $modifier Modifier. * * @return boolean */ protected function classHasModifier($class, $modifier) { return ($this->classInfo[$class]['modifiers'] & $modifier) == $modifier; } /** * 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 */ public function registerClass($real_class, $file, $pseudo_class = null) { if ( !isset($pseudo_class) ) { $pseudo_class = $real_class; } if ( !isset($this->classMap[$real_class]) ) { $this->classMap[$real_class] = preg_replace('/^' . preg_quote(FULL_PATH, '/') . '/', '', $file, 1); } $this->realClasses[$pseudo_class] = $real_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 */ public function unregisterClass($real_class, $pseudo_class = null) { unset($this->classMap[$real_class]); } } class kFactoryException extends Exception { } Index: branches/5.3.x/core/kernel/utility/system_config.php =================================================================== --- branches/5.3.x/core/kernel/utility/system_config.php (revision 16394) +++ branches/5.3.x/core/kernel/utility/system_config.php (revision 16395) @@ -1,283 +1,287 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2013 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 kSystemConfig { /** * Path to config file. * * @var string */ protected $file = ''; /** * Parsed configuration data. * * @var array */ protected $data = array(); /** * Tells, that config was changed. * * @var boolean */ protected $isChanged = false; /** * In strict mode an exception is thrown on any inconsistency within system config. * * @var boolean */ protected $strictMode = true; /** * Creates object to access system config. * * @param boolean $parse_section Whatever or not to parse sub-sections. * @param boolean $strict Strict mode. */ public function __construct($parse_section = false, $strict = true) { $this->parseSections = $parse_section; $this->strictMode = $strict; $this->file = FULL_PATH . DIRECTORY_SEPARATOR . 'system' . DIRECTORY_SEPARATOR . 'config.php'; } /** * Returns default config values. * * @return array */ protected function getDefaults() { $ret = array( 'AdminDirectory' => '/admin', 'AdminPresetsDirectory' => '/admin', 'ApplicationClass' => 'kApplication', 'ApplicationPath' => '/core/kernel/application.php', 'CacheHandler' => 'Fake', 'CmsMenuRebuildTime' => 10, 'DomainsParsedRebuildTime' => 2, 'EditorPath' => '/core/ckeditor/', 'EnableSystemLog' => '0', 'MemcacheServers' => 'localhost:11211', 'CompressionEngine' => '', 'RestrictedPath' => DIRECTORY_SEPARATOR . 'system' . DIRECTORY_SEPARATOR . '.restricted', 'SectionsParsedRebuildTime' => 5, 'StructureTreeRebuildTime' => 10, 'SystemLogMaxLevel' => 5, 'TemplateMappingRebuildTime' => 5, 'TrustProxy' => '0', 'UnitCacheRebuildTime' => 10, 'WebsiteCharset' => 'utf-8', 'WebsitePath' => rtrim(preg_replace('/'.preg_quote(rtrim(defined('REL_PATH') ? REL_PATH : '', '/'), '/').'$/', '', str_replace('\\', '/', dirname($_SERVER['PHP_SELF']))), '/'), 'WriteablePath' => DIRECTORY_SEPARATOR . 'system', ); return $this->parseSections ? array('Misc' => $ret) : $ret; } /** * Parses "/system/config.php" file and writes the result to the data variable * * @return array * @throws kSystemConfigException When something goes wrong. */ public function parse() { if ( !$this->exists() ) { if ( $this->strictMode ) { throw new kSystemConfigException(sprintf('System config at "%s" not found', $this->file)); } return array(); } elseif ( !is_readable($this->file) ) { throw new kSystemConfigException(sprintf('System config at "%s" could not be opened', $this->file)); } $contents = file($this->file); if ( $contents && $contents[0] == '<' . '?' . 'php die() ?' . ">\n" ) { // format of "config.php" file before 5.1.0 version array_shift($contents); return parse_ini_string(implode('', $contents), $this->parseSections); } $_CONFIG = array(); require($this->file); if ( $this->parseSections ) { if ( isset($_CONFIG['Database']['LoadBalancing']) && $_CONFIG['Database']['LoadBalancing'] ) { require FULL_PATH . DIRECTORY_SEPARATOR . 'system' . DIRECTORY_SEPARATOR . 'db_servers.php'; } return $_CONFIG; } $ret = array(); foreach ($_CONFIG as $section_variables) { $ret = array_merge($ret, $section_variables); } if ( !count($ret) && $this->strictMode ) { throw new kSystemConfigException(sprintf('System config at "%s" could is empty', $this->file)); } return $ret; } /** * Returns parsed variables from "config.php" file * * @return array */ public function getData() { if ( !$this->data ) { $this->data = array_replace_recursive($this->getDefaults(), $this->parse()); } return $this->data; } /** * Checks if given section is present in config. * * @param string $section * * @return boolean */ function sectionFound($section) { return $this->parseSections ? array_key_exists($section, $this->getData()) : false; } /** * Returns config value * * @param string $key Key name. * @param string $section Section name. * @param mixed $default Default value. * * @return string */ public function get($key, $section = null, $default = false) { $data = $this->getData(); if ( $this->parseSections ) { return isset($data[$section][$key]) ? $data[$section][$key] : $default; } return isset($data[$key]) ? $data[$key] : (isset($section) ? $section : $default); } /** * Checks, if a configuration file exists on disk. * * @return boolean */ public function exists() { return file_exists($this->file); } /** * Returns config status - is changed or not changed * * @return bool */ public function isChanged() { return $this->isChanged; } /** * Sets value to system config (yet saveConfig must be called to write it to file) * * @param string $key Key name. * @param string $section Section name. * @param mixed $value Value. * * @return void */ public function set($key, $section, $value = null) { $this->getData(); $this->isChanged = true; if ( isset($value) ) { // create section, when missing if ( !array_key_exists($section, $this->data) ) { $this->data[$section] = array(); } // create key in section $this->data[$section][$key] = $value; return; } unset($this->data[$section][$key]); } /** * Saves config data to the file * * @param boolean $silent * * @return void * @throws Exception */ public function save($silent = false) { if ( !is_writable($this->file) && !is_writable(dirname($this->file)) ) { $error_msg = 'Cannot write to "' . $this->file . '" file'; if ( $silent ) { trigger_error($error_msg, E_USER_WARNING); return; } throw new Exception($error_msg); } $fp = fopen($this->file, 'w'); fwrite($fp, '<' . '?' . 'php' . "\n\n"); foreach ( $this->getData() as $section_name => $section_data ) { foreach ( $section_data as $key => $value ) { fwrite($fp, '$_CONFIG[\'' . $section_name . '\'][\'' . $key . '\'] = \'' . addslashes($value) . '\';' . "\n"); } fwrite($fp, "\n"); } fclose($fp); + if ( function_exists('opcache_invalidate') ) { + opcache_invalidate($this->file); + } + $this->isChanged = false; } } class kSystemConfigException extends Exception { } Index: branches/5.3.x/core/kernel/utility/logger.php =================================================================== --- branches/5.3.x/core/kernel/utility/logger.php (revision 16394) +++ branches/5.3.x/core/kernel/utility/logger.php (revision 16395) @@ -1,1404 +1,1404 @@ <?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 for logging system activity */ class kLogger extends kBase { /** * Prefix of all database related errors */ const DB_ERROR_PREFIX = 'SQL Error:'; /** * Logger state: logging of errors and user-defined messages */ const STATE_ENABLED = 1; /** * Logger state: logging of user-defined messages only */ const STATE_USER_ONLY = 2; /** * Logger state: don't log anything */ const STATE_DISABLED = 0; /** * Log store: automatically determine where log should be written */ const LS_AUTOMATIC = 1; /** * Log store: always write log to database */ const LS_DATABASE = 2; /** * Log store: always write log to disk */ const LS_DISK = 3; /** * Log level: system is unusable */ const LL_EMERGENCY = 0; /** * Log level: action must be taken immediately */ const LL_ALERT = 1; /** * Log level: the system is in a critical condition */ const LL_CRITICAL = 2; /** * Log level: there is an error condition */ const LL_ERROR = 3; /** * Log level: there is a warning condition */ const LL_WARNING = 4; /** * Log level: a normal but significant condition */ const LL_NOTICE = 5; /** * Log level: a purely informational message */ const LL_INFO = 6; /** * Log level: messages generated to debug the application */ const LL_DEBUG = 7; /** * Log type: PHP related activity */ const LT_PHP = 1; /** * Log type: database related activity */ const LT_DATABASE = 2; /** * Log type: custom activity */ const LT_OTHER = 3; /** * Log interface: Front */ const LI_FRONT = 1; /** * Log interface: Admin */ const LI_ADMIN = 2; /** * Log interface: Cron (Front) */ const LI_CRON_FRONT = 3; /** * Log interface: Cron (Admin) */ const LI_CRON_ADMIN = 4; /** * Log interface: API */ const LI_API = 5; /** * Log notification status: disabled */ const LNS_DISABLED = 0; /** * Log notification status: pending */ const LNS_PENDING = 1; /** * Log notification status: sent */ const LNS_SENT = 2; /** * List of error/exception handlers * * @var Array * @access protected */ protected $_handlers = Array (); /** * Long messages are saved here, because "trigger_error" doesn't support error messages over 1KB in size * * @var Array * @access protected */ protected static $_longMessages = Array (); /** * Log record being worked on * * @var Array * @access protected */ protected $_logRecord = Array (); /** * Maximal level of a message, that can be logged * * @var int * @access protected */ protected $_maxLogLevel = self::LL_NOTICE; /** * State of the logger * * @var int * @access protected */ protected $_state = self::STATE_DISABLED; /** * Caches state of debug mode * * @var bool * @access protected */ protected $_debugMode = false; /** * Ignores backtrace record where following files are mentioned * * @var Array * @access protected */ protected $_ignoreInTrace = Array ('logger.php', 'db_connection.php', 'db_load_balancer.php'); /** * Create event log * * @param Array $methods_to_call List of invokable kLogger class method with their parameters (if any) * @access public */ public function __construct($methods_to_call = Array ()) { parent::__construct(); $system_config = kUtil::getSystemConfig(); $this->_debugMode = $this->Application->isDebugMode(); $this->setState($system_config->get('EnableSystemLog', self::STATE_DISABLED)); $this->_maxLogLevel = $system_config->get('SystemLogMaxLevel', self::LL_NOTICE); foreach ($methods_to_call as $method_to_call) { call_user_func_array(Array ($this, $method_to_call[0]), $method_to_call[1]); } if ( !kUtil::constOn('DBG_ZEND_PRESENT') && !$this->Application->isDebugMode() ) { // don't report error on screen if debug mode is turned off error_reporting(0); ini_set('display_errors', 0); } register_shutdown_function(Array ($this, 'catchLastError')); } /** * Sets state of the logged (enabled/user-only/disabled) * * @param $new_state * @return void * @access public */ public function setState($new_state = null) { if ( isset($new_state) ) { $this->_state = (int)$new_state; } if ( $this->_state === self::STATE_ENABLED ) { $this->_enableErrorHandling(); } elseif ( $this->_state === self::STATE_DISABLED ) { $this->_disableErrorHandling(); } } /** * Enable error/exception handling capabilities * * @return void * @access protected */ protected function _enableErrorHandling() { $this->_disableErrorHandling(); $this->_handlers[self::LL_ERROR] = new kErrorHandlerStack($this); $this->_handlers[self::LL_CRITICAL] = new kExceptionHandlerStack($this); } /** * Disables error/exception handling capabilities * * @return void * @access protected */ protected function _disableErrorHandling() { foreach ($this->_handlers as $index => $handler) { $this->_handlers[$index]->__destruct(); unset($this->_handlers[$index]); } } /** * Initializes new log record. Use "kLogger::write" to save to db/disk * * @param string $message * @param int $code * @return kLogger * @access public */ public function prepare($message = '', $code = null) { $this->_logRecord = Array ( 'LogUniqueId' => kUtil::generateId(), 'LogMessage' => $message, 'LogLevel' => self::LL_INFO, 'LogCode' => $code, 'LogType' => self::LT_OTHER, 'LogHostname' => $_SERVER['HTTP_HOST'], 'LogRequestSource' => php_sapi_name() == 'cli' ? 2 : 1, 'LogRequestURI' => php_sapi_name() == 'cli' ? implode(' ', $GLOBALS['argv']) : $_SERVER['REQUEST_URI'], 'LogUserId' => USER_GUEST, 'IpAddress' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '', 'LogSessionKey' => 0, 'LogProcessId' => getmypid(), 'LogUserData' => '', 'LogNotificationStatus' => self::LNS_DISABLED, ); if ( $this->Application->isAdmin ) { $this->_logRecord['LogInterface'] = defined('CRON') && CRON ? self::LI_CRON_ADMIN : self::LI_ADMIN; } else { $this->_logRecord['LogInterface'] = defined('CRON') && CRON ? self::LI_CRON_FRONT : self::LI_FRONT; } if ( $this->Application->InitDone ) { $this->_logRecord['LogUserId'] = $this->Application->RecallVar('user_id'); $this->_logRecord['LogSessionKey'] = $this->Application->GetSID(); $this->_logRecord['IpAddress'] = $this->Application->getClientIp(); } return $this; } /** * Sets one or more fields of log record * * @param string|Array $field_name * @param string|null $field_value * @return kLogger * @access public * @throws UnexpectedValueException */ public function setLogField($field_name, $field_value = null) { if ( isset($field_value) ) { $this->_logRecord[$field_name] = $field_value; } elseif ( is_array($field_name) ) { $this->_logRecord = array_merge($this->_logRecord, $field_name); } else { throw new UnexpectedValueException('Invalid arguments'); } return $this; } /** * Sets user data * * @param string $data * @param bool $as_array * @return kLogger * @access public */ public function setUserData($data, $as_array = false) { if ( $as_array ) { $data = serialize((array)$data); } return $this->setLogField('LogUserData', $data); } /** * Add user data * * @param string $data * @param bool $as_array * @return kLogger * @access public */ public function addUserData($data, $as_array = false) { $new_data = $this->_logRecord['LogUserData']; if ( $as_array ) { $new_data = $new_data ? unserialize($new_data) : Array (); $new_data[] = $data; $new_data = serialize($new_data); } else { $new_data .= ($new_data ? PHP_EOL : '') . $data; } return $this->setLogField('LogUserData', $new_data); } /** * Adds event to log record * * @param kEvent $event * @return kLogger * @access public */ public function addEvent(kEvent $event) { $this->_logRecord['LogEventName'] = (string)$event; return $this; } /** * Adds log source file & file to log record * * @param string|Array $file_or_trace file path * @param int $line file line * @return kLogger * @access public */ public function addSource($file_or_trace = '', $line = 0) { if ( is_array($file_or_trace) ) { $trace_info = $file_or_trace[0]; $this->_logRecord['LogSourceFilename'] = $trace_info['file']; $this->_logRecord['LogSourceFileLine'] = $trace_info['line']; } else { $this->_logRecord['LogSourceFilename'] = $file_or_trace; $this->_logRecord['LogSourceFileLine'] = $line; } return $this; } /** * Adds session contents to log record * * @param bool $include_optional Include optional session variables * @return kLogger * @access public */ public function addSessionData($include_optional = false) { if ( $this->Application->InitDone ) { $this->_logRecord['LogSessionData'] = serialize($this->Application->Session->getSessionData($include_optional)); } return $this; } /** * Adds user request information to log record * * @return kLogger * @access public */ public function addRequestData() { $request_data = array( 'Headers' => $this->Application->HttpQuery->getHeaders(), ); $request_variables = Array('_GET' => $_GET, '_POST' => $_POST, '_COOKIE' => $_COOKIE); foreach ( $request_variables as $title => $data ) { if ( !$data ) { continue; } $request_data[$title] = $data; } $this->_logRecord['LogRequestData'] = serialize($request_data); return $this; } /** * Adds trace to log record * * @param Array $trace * @param int $skip_levels * @param Array $skip_files * @return kLogger * @access public */ public function addTrace($trace = null, $skip_levels = 1, $skip_files = null) { $trace = $this->createTrace($trace, $skip_levels, $skip_files); foreach ($trace as $trace_index => $trace_info) { if ( isset($trace_info['args']) ) { $trace[$trace_index]['args'] = $this->_implodeObjects($trace_info['args']); } } $this->_logRecord['LogBacktrace'] = serialize($this->_removeObjectsFromTrace($trace)); return $this; } /** * Remove objects from trace, since before PHP 5.2.5 there wasn't possible to remove them initially * * @param Array $trace * @return Array * @access protected */ protected function _removeObjectsFromTrace($trace) { if ( version_compare(PHP_VERSION, '5.3', '>=') ) { return $trace; } $trace_indexes = array_keys($trace); foreach ($trace_indexes as $trace_index) { unset($trace[$trace_index]['object']); } return $trace; } /** * Implodes object to prevent memory leaks * * @param Array $array * @return Array * @access protected */ protected function _implodeObjects($array) { $ret = Array (); foreach ($array as $key => $value) { if ( is_array($value) ) { $ret[$key] = $this->_implodeObjects($value); } elseif ( is_object($value) ) { if ( $value instanceof kEvent ) { $ret[$key] = 'Event: ' . (string)$value; } elseif ( $value instanceof kBase ) { $ret[$key] = (string)$value; } else { $ret[$key] = 'Class: ' . get_class($value); } } elseif ( strlen($value) > 200 ) { $ret[$key] = substr($value, 0, 50) . ' ...'; } else { $ret[$key] = $value; } } return $ret; } /** * Removes first N levels from trace * * @param Array $trace * @param int $levels * @param Array $files * @return Array * @access public */ public function createTrace($trace = null, $levels = null, $files = null) { if ( !isset($trace) ) { $trace = debug_backtrace(false); } if ( !$trace ) { // no trace information return $trace; } if ( isset($levels) && is_numeric($levels) ) { for ($i = 0; $i < $levels; $i++) { array_shift($trace); } } if ( isset($files) && is_array($files) ) { while (true) { $trace_info = $trace[0]; $file = isset($trace_info['file']) ? basename($trace_info['file']) : ''; if ( !in_array($file, $files) ) { break; } array_shift($trace); } } return $trace; } /** * Adds PHP error to log record * * @param int $errno * @param string $errstr * @param string $errfile * @param int $errline * @return kLogger * @access public */ public function addError($errno, $errstr, $errfile = null, $errline = null) { $errstr = self::expandMessage($errstr, !$this->_debugMode); $this->_logRecord['LogLevel'] = $this->_getLogLevelByErrorNo($errno); if ( $this->isLogType(self::LT_DATABASE, $errstr) ) { list ($errno, $errstr, $sql) = self::parseDatabaseError($errstr); $this->_logRecord['LogType'] = self::LT_DATABASE; $this->_logRecord['LogUserData'] = $sql; $trace = $this->createTrace(null, 4, $this->_ignoreInTrace); $this->addSource($trace); $this->addTrace($trace, 0); } else { $this->_logRecord['LogType'] = self::LT_PHP; $this->addSource((string)$errfile, $errline); $this->addTrace(null, 4); } $this->_logRecord['LogCode'] = $errno; $this->_logRecord['LogMessage'] = $errstr; return $this; } /** * Adds PHP exception to log record * * @param Exception $exception * @return kLogger * @access public */ public function addException($exception) { $errstr = self::expandMessage($exception->getMessage(), !$this->_debugMode); $this->_logRecord['LogLevel'] = self::LL_CRITICAL; $exception_trace = $exception->getTrace(); array_unshift($exception_trace, array( 'function' => '', 'file' => $exception->getFile() !== null ? $exception->getFile() : 'n/a', 'line' => $exception->getLine() !== null ? $exception->getLine() : 'n/a', 'args' => array(), )); if ( $this->isLogType(self::LT_DATABASE, $errstr) ) { list ($errno, $errstr, $sql) = self::parseDatabaseError($errstr); $this->_logRecord['LogType'] = self::LT_DATABASE; $this->_logRecord['LogUserData'] = $sql; $trace = $this->createTrace($exception_trace, null, $this->_ignoreInTrace); $this->addSource($trace); $this->addTrace($trace, 0); } else { $this->_logRecord['LogType'] = self::LT_PHP; $errno = $exception->getCode(); $this->addSource((string)$exception->getFile(), $exception->getLine()); $this->addTrace($exception_trace, 0); } $this->_logRecord['LogCode'] = $errno; $this->_logRecord['LogMessage'] = $errstr; return $this; } /** * Allows to map PHP error numbers to syslog log level * * @param int $errno * @return int * @access protected */ protected function _getLogLevelByErrorNo($errno) { $error_number_mapping = Array ( self::LL_ERROR => Array (E_RECOVERABLE_ERROR, E_USER_ERROR, E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE), self::LL_WARNING => Array (E_WARNING, E_USER_WARNING, E_CORE_WARNING, E_COMPILE_WARNING), self::LL_NOTICE => Array (E_NOTICE, E_USER_NOTICE, E_STRICT), ); if ( version_compare(PHP_VERSION, '5.3.0', '>=') ) { $error_number_mapping[self::LL_NOTICE][] = E_DEPRECATED; $error_number_mapping[self::LL_NOTICE][] = E_USER_DEPRECATED; } foreach ($error_number_mapping as $log_level => $error_numbers) { if ( in_array($errno, $error_numbers) ) { return $log_level; } } return self::LL_ERROR; } /** * Changes log level of a log record * * @param int $log_level * @return kLogger * @access public */ public function setLogLevel($log_level) { $this->_logRecord['LogLevel'] = $log_level; return $this; } /** * Writes prepared log to database or disk, when database isn't available * * @param int $storage_medium * @return bool|int * @access public * @throws InvalidArgumentException */ public function write($storage_medium = self::LS_AUTOMATIC) { if ( !$this->_logRecord || $this->_logRecord['LogLevel'] > $this->_maxLogLevel || $this->_state == self::STATE_DISABLED ) { // nothing to save OR less detailed logging requested OR disabled return false; } $this->_logRecord['LogMemoryUsed'] = memory_get_usage(); $this->_logRecord['LogTimestamp'] = time(); $this->_logRecord['LogDate'] = date('Y-m-d H:i:s'); if ( $storage_medium == self::LS_AUTOMATIC ) { $storage_medium = $this->Conn->connectionOpened() ? self::LS_DATABASE : self::LS_DISK; } if ( $storage_medium == self::LS_DATABASE ) { $result = $this->Conn->doInsert($this->_logRecord, TABLE_PREFIX . 'SystemLog'); } elseif ( $storage_medium == self::LS_DISK ) { $result = $this->_saveToFile(RESTRICTED . '/system.log'); } else { throw new InvalidArgumentException('Unknown storage medium "' . $storage_medium . '"'); } $unique_id = $this->_logRecord['LogUniqueId']; if ( $this->_logRecord['LogNotificationStatus'] == self::LNS_SENT ) { $this->_sendNotification($unique_id); } $this->_logRecord = Array (); return $result ? $unique_id : false; } /** * Catches last error happened before script ended * * @return void * @access public */ public function catchLastError() { $this->write(); $last_error = error_get_last(); if ( !is_null($last_error) && isset($this->_handlers[self::LL_ERROR]) ) { $handler = $this->_handlers[self::LL_ERROR]; /* @var $handler kErrorHandlerStack */ $handler->handle($last_error['type'], $last_error['message'], $last_error['file'], $last_error['line']); } } /** * 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 delete($unique_id, $storage_medium = self::LS_AUTOMATIC) { if ( $storage_medium == self::LS_AUTOMATIC ) { - $storage_medium = $this->Conn->connectionOpened ? self::LS_DATABASE : self::LS_DISK; + $storage_medium = $this->Conn->connectionOpened() ? self::LS_DATABASE : self::LS_DISK; } if ( $storage_medium == self::LS_DATABASE ) { $sql = 'DELETE FROM ' . TABLE_PREFIX . 'SystemLog WHERE LogUniqueId = ' . $unique_id; $this->Conn->Query($sql); } elseif ( $storage_medium == self::LS_DISK ) { // TODO: no way to delete a line from a file } else { throw new InvalidArgumentException('Unknown storage medium "' . $storage_medium . '"'); } } /** * Send notification (delayed or instant) about log record to e-mail from configuration * * @param bool $instant * @return kLogger * @access public */ public function notify($instant = false) { $this->_logRecord['LogNotificationStatus'] = $instant ? self::LNS_SENT : self::LNS_PENDING; return $this; } /** * Sends notification e-mail about message with given $unique_id * * @param int $unique_id * @return void * @access protected */ protected function _sendNotification($unique_id) { $notification_email = $this->Application->ConfigValue('SystemLogNotificationEmail'); if ( !$notification_email ) { trigger_error('System Log notification E-mail not specified', E_USER_NOTICE); return; } $send_params = Array ( 'to_name' => $notification_email, 'to_email' => $notification_email, ); // initialize list outside of e-mail event with right settings $this->Application->recallObject('system-log.email', 'system-log_List', Array ('unique_id' => $unique_id)); $this->Application->emailAdmin('SYSTEM.LOG.NOTIFY', null, $send_params); $this->Application->removeObject('system-log.email'); } /** * Adds error/exception handler * * @param string|Array $handler * @param bool $is_exception * @return void * @access public */ public function addErrorHandler($handler, $is_exception = false) { $this->_handlers[$is_exception ? self::LL_CRITICAL : self::LL_ERROR]->add($handler); } /** * SQL Error Handler * * When not debug mode, then fatal database query won't break anything. * * @param int $code * @param string $msg * @param string $sql * @return bool * @access public * @throws RuntimeException */ public function handleSQLError($code, $msg, $sql) { $error_msg = self::shortenMessage(self::DB_ERROR_PREFIX . ' #' . $code . ' - ' . $msg . '. SQL: ' . trim($sql)); if ( isset($this->Application->Debugger) ) { if ( kUtil::constOn('DBG_SQL_FAILURE') && !defined('IS_INSTALL') ) { throw new RuntimeException($error_msg); } else { $this->Application->Debugger->appendTrace(); } } // next line also trigger attached error handlers trigger_error($error_msg, E_USER_WARNING); return true; } /** * Packs information about error into a single line * * @param string $errno * @param bool $strip_tags * @return string * @access public */ public function toString($errno = null, $strip_tags = false) { if ( !isset($errno) ) { $errno = $this->_logRecord['LogCode']; } $errstr = $this->_logRecord['LogMessage']; $errfile = $this->_logRecord['LogSourceFilename']; $errline = $this->_logRecord['LogSourceFileLine']; if ( PHP_SAPI === 'cli' ) { $result = sprintf(' [%s] ' . PHP_EOL . ' %s', $errno, $errstr); if ( $this->_logRecord['LogBacktrace'] ) { $result .= $this->printBacktrace(unserialize($this->_logRecord['LogBacktrace'])); } } else { $result = '<strong>' . $errno . ': </strong>' . "{$errstr} in {$errfile} on line {$errline}"; } return $strip_tags ? strip_tags($result) : $result; } /** * Prints backtrace result * * @param array $trace Trace. * * @return string */ protected function printBacktrace(array $trace) { if ( !$trace ) { return ''; } $ret = PHP_EOL . PHP_EOL . PHP_EOL . 'Exception trace:' . PHP_EOL; foreach ( $trace as $trace_info ) { $class = isset($trace_info['class']) ? $trace_info['class'] : ''; $type = isset($trace_info['type']) ? $trace_info['type'] : ''; $function = $trace_info['function']; $args = isset($trace_info['args']) && $trace_info['args'] ? '...' : ''; $file = isset($trace_info['file']) ? $trace_info['file'] : 'n/a'; $line = isset($trace_info['line']) ? $trace_info['line'] : 'n/a'; $ret .= sprintf(' %s%s%s(%s) at %s:%s' . PHP_EOL, $class, $type, $function, $args, $file, $line); } return $ret; } /** * Saves log to file (e.g. when not possible to save into database) * * @param $filename * @return bool * @access protected */ protected function _saveToFile($filename) { $time = date('Y-m-d H:i:s'); $log_file = new SplFileObject($filename, 'a'); return $log_file->fwrite('[' . $time . '] #' . $this->toString(null, true) . PHP_EOL) > 0; } /** * Checks if log type of current log record matches given one * * @param int $log_type * @param string $log_message * @return bool * @access public */ public function isLogType($log_type, $log_message = null) { if ( $this->_logRecord['LogType'] == $log_type ) { return true; } if ( $log_type == self::LT_DATABASE ) { if ( !isset($log_message) ) { $log_message = $this->_logRecord['LogMessage']; } return strpos($log_message, self::DB_ERROR_PREFIX) !== false; } return false; } /** * Shortens message * * @param string $message * @return string * @access public */ public static function shortenMessage($message) { $max_len = ini_get('log_errors_max_len'); if ( strlen($message) > $max_len ) { $long_key = kUtil::generateId(); self::$_longMessages[$long_key] = $message; return mb_substr($message, 0, $max_len - strlen($long_key) - 2) . ' #' . $long_key; } return $message; } /** * Expands shortened message * * @param string $message * @param bool $clear_cache Allow debugger to expand message after it's been expanded by kLogger * @return string * @access public */ public static function expandMessage($message, $clear_cache = true) { if ( preg_match('/(.*)#([\d]+)$/', $message, $regs) ) { $long_key = $regs[2]; if ( isset(self::$_longMessages[$long_key]) ) { $message = self::$_longMessages[$long_key]; if ( $clear_cache ) { unset(self::$_longMessages[$long_key]); } } } return $message; } /** * Parses database error message into error number, error message and sql that caused that error * * @static * @param string $message * @return Array * @access public */ public static function parseDatabaseError($message) { $regexp = '/' . preg_quote(self::DB_ERROR_PREFIX) . ' #(.*?) - (.*?)\. SQL: (.*?)$/s'; if ( preg_match($regexp, $message, $regs) ) { // errno, errstr, sql return Array ($regs[1], $regs[2], $regs[3]); } return Array (0, $message, ''); } } /** * Base class for error or exception handling */ abstract class kHandlerStack extends kBase { /** * List of added handlers * * @var Array * @access protected */ protected $_handlers = Array (); /** * Reference to event log, which created this object * * @var kLogger * @access protected */ protected $_logger; /** * Remembers if handler is activated * * @var bool * @access protected */ protected $_enabled = false; public function __construct(kLogger $logger) { parent::__construct(); $this->_logger = $logger; if ( !kUtil::constOn('DBG_ZEND_PRESENT') ) { $this->attach(); $this->_enabled = true; } } /** * Detaches from error handling routines on class destruction * * @return void * @access public */ public function __destruct() { if ( !$this->_enabled ) { return; } $this->detach(); $this->_enabled = false; } /** * Attach to error handling routines * * @abstract * @return void * @access protected */ abstract protected function attach(); /** * Detach from error handling routines * * @abstract * @return void * @access protected */ abstract protected function detach(); /** * Adds new handler to the stack * * @param callable $handler * @return void * @access public */ public function add($handler) { $this->_handlers[] = $handler; } protected function _handleFatalError($errno) { $debug_mode = defined('DEBUG_MODE') && DEBUG_MODE; $skip_reporting = defined('DBG_SKIP_REPORTING') && DBG_SKIP_REPORTING; if ( !$this->_handlers || ($debug_mode && $skip_reporting) ) { // when debugger absent OR it's present, but we actually can't see it's error report (e.g. during ajax request) if ( $this->_isFatalError($errno) ) { $this->_displayFatalError($errno); } if ( !$this->_handlers ) { return true; } } return null; } /** * Determines if given error is a fatal * * @abstract * @param Exception|int $errno * @return bool */ abstract protected function _isFatalError($errno); /** * Displays div with given error message * * @param string $errno * @return void * @access protected */ protected function _displayFatalError($errno) { $errno = $this->_getFatalErrorTitle($errno); $margin = $this->Application->isAdmin ? '8px' : 'auto'; $error_msg = $this->_logger->toString($errno, PHP_SAPI === 'cli'); if ( PHP_SAPI === 'cli' ) { echo $error_msg; } else { echo '<div style="background-color: #FEFFBF; margin: ' . $margin . '; padding: 10px; border: 2px solid red; text-align: center">' . $error_msg . '</div>'; } exit; } /** * Returns title to show for a fatal * * @abstract * @param Exception|int $errno * @return string */ abstract protected function _getFatalErrorTitle($errno); } /** * Class, that handles errors */ class kErrorHandlerStack extends kHandlerStack { /** * Attach to error handling routines * * @return void * @access protected */ protected function attach() { // set as error handler $error_handler = set_error_handler(Array ($this, 'handle')); if ( $error_handler ) { // wrap around previous error handler, if any was set $this->_handlers[] = $error_handler; } } /** * Detach from error handling routines * * @return void * @access protected */ protected function detach() { restore_error_handler(); } /** * Determines if given error is a fatal * * @param int $errno * @return bool * @access protected */ protected function _isFatalError($errno) { $fatal_errors = Array (E_USER_ERROR, E_RECOVERABLE_ERROR, E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE); return in_array($errno, $fatal_errors); } /** * Returns title to show for a fatal * * @param int $errno * @return string * @access protected */ protected function _getFatalErrorTitle($errno) { return 'Fatal Error'; } /** * Default error handler * * @param int $errno * @param string $errstr * @param string $errfile * @param int $errline * @param Array $errcontext * @return bool * @access public */ public function handle($errno, $errstr, $errfile = null, $errline = null, $errcontext = Array ()) { $log = $this->_logger->prepare()->addError($errno, $errstr, $errfile, $errline); if ( $this->_handleFatalError($errno) ) { $log->write(); return true; } $log->write(); $res = false; foreach ($this->_handlers as $handler) { $res = call_user_func($handler, $errno, $errstr, $errfile, $errline, $errcontext); } return $res; } } /** * Class, that handles exceptions */ class kExceptionHandlerStack extends kHandlerStack { /** * Attach to error handling routines * * @return void * @access protected */ protected function attach() { // set as exception handler $exception_handler = set_exception_handler(Array ($this, 'handle')); if ( $exception_handler ) { // wrap around previous exception handler, if any was set $this->_handlers[] = $exception_handler; } } /** * Detach from error handling routines * * @return void * @access protected */ protected function detach() { restore_exception_handler(); } /** * Determines if given error is a fatal * * @param Exception $errno * @return bool */ protected function _isFatalError($errno) { return true; } /** * Returns title to show for a fatal * * @param Exception $errno * @return string */ protected function _getFatalErrorTitle($errno) { return get_class($errno); } /** * Handles exception * * @param Exception $exception * @return bool * @access public */ public function handle($exception) { $log = $this->_logger->prepare()->addException($exception); if ( $exception instanceof kRedirectException ) { /* @var $exception kRedirectException */ $exception->run(); } if ( $this->_handleFatalError($exception) ) { $log->write(); return true; } $log->write(); $res = false; foreach ($this->_handlers as $handler) { $res = call_user_func($handler, $exception); } return $res; } } Index: branches/5.3.x/core/kernel/utility/event.php =================================================================== --- branches/5.3.x/core/kernel/utility/event.php (revision 16394) +++ branches/5.3.x/core/kernel/utility/event.php (revision 16395) @@ -1,453 +1,458 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); final class kEvent extends kBase { /** * Event finished working successfully. */ const erSUCCESS = 0; /** * Event finished working, but result is unsuccessful. */ const erFAIL = -1; /** * Event experienced FATAL error - no hooks should continue! */ const erFATAL = -2; /** * Event failed on internal permission checking (user has no permission). */ const erPERM_FAIL = -3; /** * Event requested to stop processing (don't parse templates). */ const erSTOP = -4; /** + * Flag, set as event parameter, that indicates that ID is coming from Web Request. + */ + const FLAG_ID_FROM_REQUEST = 'id_from_request'; + + /** * Reference to event, that created given event. * * @var self */ public $MasterEvent; /** * Event name. * * @var string */ public $Name; /** * Don't execute hooks, before event processing. * * @var boolean */ public $SkipBeforeHooks = false; /** * Don't execute hooks, after event processing. * * @var boolean */ public $SkipAfterHooks = false; /** * Perform redirect after event processing. * * Redirect after event processing allows to prevent same event being * present in resulting url. Also could contain template name, that * needs to be shown after redirect. * * @var mixed */ public $redirect = true; /** * Params, used during redirect url building after event successful processing. * * @var array */ private $_redirectParams = array(); /** * PHP file to redirect to. Defaults to "index.php". * * @var string */ public $redirectScript = null; /** * Event processing status. * * @var integer */ public $status = self::erSUCCESS; /** * Event parameters. * * Usually indicate, how particular event should be processed. * * @var array */ private $_specificParams = array(); /** * Pseudo class used, to create object, based on event contents. * * @var string */ private $_pseudoClass = ''; /** * Creates an event from array. * * @param array $params Event string in array format with 'prefix', 'special' (optional) and 'name' keys. * @param array $specific_params Specific params. * * @return self */ public static function fromArray(array $params, array $specific_params = array()) { $prefix = isset($params['prefix']) ? $params['prefix'] : false; $special = isset($params['special']) ? $params['special'] : false; $name = isset($params['name']) ? $params['name'] : ''; return new self($prefix . '.' . $special . ':' . $name, $specific_params); } /** * Create event from given prefix, special, name and specific params. * Parameter $params could be be an an array with following keys: "prefix", "special" (optional), "name". * Parameter $params could be a string in format: "prefix:name" or "prefix.special:name". * * @param mixed $event_string Params. * @param array $specific_params Event specific params (none by default). * * @throws InvalidArgumentException When incorrect event string given. */ public function __construct($event_string, array $specific_params = array()) { parent::__construct(); if ( preg_match('/([^.:]*)[.]{0,1}([^:]*):(.*)/', $event_string, $regs) ) { $prefix = $regs[1]; $special = $regs[2]; if ( $prefix ) { $this->Init($prefix, $special); } $this->Name = $regs[3]; $this->_specificParams = $specific_params; } else { $error_msg = 'Invalid event string: "<strong>' . $event_string . '</strong>". '; $error_msg .= 'Should be in "prefix[.special]:OnEvent" format'; throw new InvalidArgumentException($error_msg); } } /** * Returns joined prefix and special if any. * * @param boolean $from_submit If true, then joins prefix & special by "_", uses "." otherwise. * * @return string */ public function getPrefixSpecial($from_submit = false) { if ( !$from_submit ) { return parent::getPrefixSpecial(); } return rtrim($this->Prefix . '_' . $this->Special, '_'); } /** * Sets event parameter. * * @param string $name Name. * @param mixed $value Value. * * @return void */ public function setEventParam($name, $value) { $this->_specificParams[$name] = $value; } /** * Returns event parameter by name (supports digging). * * @param string $name Name. * * @return mixed */ public function getEventParam($name) { $args = func_get_args(); if ( count($args) > 1 ) { kUtil::array_unshift_ref($args, $this->_specificParams); return call_user_func_array('getArrayValue', $args); } return array_key_exists($name, $this->_specificParams) ? $this->_specificParams[$name] : false; } /** * Returns all event parameters. * * @return array */ public function getEventParams() { return $this->_specificParams; } /** * Set's pseudo class that differs from the one specified in $Prefix. * * @param string $appendix Appendix. * * @return void */ public function setPseudoClass($appendix) { $this->_pseudoClass = $this->Prefix . $appendix; } /** * Performs event initialization. * * Also sets pseudo class same $prefix. * * @param string $prefix Prefix. * @param string $special Special. * * @return void */ public function Init($prefix, $special) { $this->_pseudoClass = $prefix; parent::Init($prefix, $special); } /** * Returns object used in event. * * @param array $params Params. * * @return kDBBase */ public function getObject(array $params = array()) { if ( !$this->Application->hasObject($this->prefixSpecial) ) { $top_event = $this; // When OnSave calls OnPreSave in first line, then this would make sure OnSave is used. while ( is_object($top_event->MasterEvent) ) { $top_event = $top_event->MasterEvent; } $params['parent_event'] = $top_event; } return $this->Application->recallObject($this->prefixSpecial, $this->_pseudoClass, $params); } /** * Executes given event in context of current event. * * Sub-event gets this event in "kEvent::MasterEvent" attribute. * Sub-event execution results (status and redirect* properties) are copied back to current event. * * @param string $name Name of callable event (optionally could contain prefix_special as well). * * @return void * @see kEvent::MasterEvent * @todo Overwrites master event data with called event data, which makes 'parent_event' useless in most cases. */ public function CallSubEvent($name) { if ( strpos($name, ':') === false ) { // PrefixSpecial not specified -> use from current event. $name = $this->getPrefixSpecial() . ':' . $name; } $child_event = new kEvent($name); $child_event->copyFrom($this, true); $this->Application->HandleEvent($child_event); $this->copyFrom($child_event); $this->_specificParams = $child_event->_specificParams; } /** * Allows to copy data between events. * * @param kEvent $source_event Source event. * @param boolean $inherit Keep reference to source event in copied event. * * @return void */ public function copyFrom(kEvent $source_event, $inherit = false) { if ( $inherit ) { $this->MasterEvent = $source_event; } else { $this->status = $source_event->status; } $this->redirect = $source_event->redirect; $this->_redirectParams = $source_event->_redirectParams; $this->redirectScript = $source_event->redirectScript; $this->_specificParams = $source_event->_specificParams; } /** * Returns all redirect parameters. * * @return array */ public function getRedirectParams() { return $this->_redirectParams; } /** * Returns redirect parameter. * * @param string $name Name. * * @return mixed */ public function getRedirectParam($name) { return array_key_exists($name, $this->_redirectParams) ? $this->_redirectParams[$name] : false; } /** * Set's redirect param for event. * * @param string $name Name. * @param string $value Value. * * @return void */ public function SetRedirectParam($name, $value) { $this->_redirectParams[$name] = $value; } /** * Allows to merge passed redirect params hash with existing ones. * * @param array $params Params. * @param boolean $append Append to existing parameters. * * @return void */ public function setRedirectParams(array $params, $append = true) { if ( $append ) { $params = kUtil::array_merge_recursive($this->_redirectParams, $params); } $this->_redirectParams = $params; } /** * Allows to tell if this event was called some how (e.g. sub-event, hook) from event requested. * * @param string $event_key Event key in format: "[prefix[.special]:]event_name". * * @return boolean */ public function hasAncestor($event_key) { if ( strpos($event_key, ':') === false ) { $event_key = $this->getPrefixSpecial() . ':' . $event_key; } return $this->Application->EventManager->eventRunning($event_key); } /** * Returns permission section associated with event. * * @return string * @throws Exception When permission section not specified for event's unit. */ public function getSection() { $perm_section = $this->getEventParam('PermSection'); if ( $perm_section ) { return $perm_section; } // 1. get section by current top_prefix. $top_prefix = $this->getEventParam('top_prefix'); if ( $top_prefix == false ) { $top_prefix = $this->Application->GetTopmostPrefix($this->Prefix, true); $this->setEventParam('top_prefix', $top_prefix); } $section = $this->Application->getUnitConfig($top_prefix)->getPermSectionByName('main'); // 2. check if this section has perm_prefix mapping to other prefix. $sections_helper = $this->Application->recallObject('SectionsHelper'); /* @var $sections_helper kSectionsHelper */ $section_data =& $sections_helper->getSectionData($section); if ( $section_data && isset($section_data['perm_prefix']) && $section_data['perm_prefix'] != $top_prefix ) { $this->setEventParam('top_prefix', $section_data['perm_prefix']); $section = $this->Application->getUnitConfig($section_data['perm_prefix'])->getPermSectionByName('main'); } if ( !$section ) { $error_msg = 'Permission <strong>section</strong> not specified for'; $error_msg .= ' prefix <strong>' . $top_prefix . '</strong>'; throw new Exception($error_msg); } return $section; } /** * Casts object to string. * * @return string */ public function __toString() { return $this->getPrefixSpecial() . ':' . $this->Name; } } Index: branches/5.3.x/core/kernel/utility/debugger.php =================================================================== --- branches/5.3.x/core/kernel/utility/debugger.php (revision 16394) +++ branches/5.3.x/core/kernel/utility/debugger.php (revision 16395) @@ -1,2128 +1,2141 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); if( !class_exists('Debugger') ) { /** * Contains misc functions, used by debugger (mostly copied from kUtil class) */ class DebuggerUtil { /** * Trust information, provided by proxy * * @var bool */ public static $trustProxy = false; /** * Checks if constant is defined and has positive value * * @param string $const_name * @return bool */ public static function constOn($const_name) { return defined($const_name) && constant($const_name); } /** * Define constant if it was not already defined before * * @param string $const_name * @param string $const_value * @access public */ public static function safeDefine($const_name, $const_value) { if ( !defined($const_name) ) { define($const_name, $const_value); } } /** * Formats file/memory size in nice way * * @param int $bytes * @return string * @access public */ public static function formatSize($bytes) { if ($bytes >= 1099511627776) { $return = round($bytes / 1024 / 1024 / 1024 / 1024, 2); $suffix = "TB"; } elseif ($bytes >= 1073741824) { $return = round($bytes / 1024 / 1024 / 1024, 2); $suffix = "GB"; } elseif ($bytes >= 1048576) { $return = round($bytes / 1024 / 1024, 2); $suffix = "MB"; } elseif ($bytes >= 1024) { $return = round($bytes / 1024, 2); $suffix = "KB"; } else { $return = $bytes; $suffix = "Byte"; } $return .= ' '.$suffix; return $return; } /** * Checks, that user IP address is within allowed range * * @param string $ip_list semi-column (by default) separated ip address list * @param string $separator ip address separator (default ";") * * @return bool */ public static function ipMatch($ip_list, $separator = ';') { if ( php_sapi_name() == 'cli' ) { return false; } $ip_match = false; $ip_addresses = $ip_list ? explode($separator, $ip_list) : Array (); $client_ip = self::getClientIp(); foreach ($ip_addresses as $ip_address) { if ( self::netMatch($ip_address, $client_ip) ) { $ip_match = true; break; } } return $ip_match; } /** * Returns the client IP address. * * @return string The client IP address * @access public */ public static function getClientIp() { if ( self::$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 $_SERVER['REMOTE_ADDR']; } /** * Checks, that given ip belongs to given subnet * * @param string $network * @param string $ip * @return bool * @access public */ public static function netMatch($network, $ip) { $network = trim($network); $ip = trim($ip); if ( preg_replace('/[\d\.\/-]/', '', $network) != '' ) { $network = gethostbyname($network); } if ($network == $ip) { // comparing two ip addresses directly return true; } $d = strpos($network, '-'); if ($d !== false) { // ip address range specified $from = ip2long(trim(substr($network, 0, $d))); $to = ip2long(trim(substr($network, $d + 1))); $ip = ip2long($ip); return ($ip >= $from && $ip <= $to); } elseif (strpos($network, '/') !== false) { // single subnet specified $ip_arr = explode('/', $network); if (!preg_match("@\d*\.\d*\.\d*\.\d*@", $ip_arr[0], $matches)) { $ip_arr[0] .= '.0'; // Alternate form 194.1.4/24 } $network_long = ip2long($ip_arr[0]); $x = ip2long($ip_arr[1]); $mask = long2ip($x) == $ip_arr[1] ? $x : (0xffffffff << (32 - $ip_arr[1])); $ip_long = ip2long($ip); return ($ip_long & $mask) == ($network_long & $mask); } return false; } } /** * Main debugger class, that can be used with any In-Portal (or not) project */ class Debugger { const ROW_TYPE_ERROR = 'error'; const ROW_TYPE_WARNING = 'warning'; const ROW_TYPE_NOTICE = 'notice'; const ROW_TYPE_SQL = 'sql'; const ROW_TYPE_OTHER = 'other'; /** * Holds reference to global KernelApplication instance * * @var kApplication * @access private */ private $Application = null; /** * Stores last fatal error hash or 0, when no fatal error happened * * @var integer */ private $_fatalErrorHash = 0; private $_filterTypes = Array ('error', 'sql', 'other'); /** * Counts warnings on the page * * @var int * @access public */ public $WarningCount = 0; /** * Allows to track compile errors, like "stack-overflow" * * @var bool * @access private */ private $_compileError = false; /** * Debugger data for building report * * @var Array * @access private */ private $Data = Array (); /** * Holds information about each profiler record (start/end/description) * * @var Array * @access private */ private $ProfilerData = Array (); /** * Holds information about total execution time per profiler key (e.g. total sql time) * * @var Array * @access private */ private $ProfilerTotals = Array (); /** * Counts how much each of total types were called (e.g. total error count) * * @var Array * @access private */ private $ProfilerTotalCount = Array (); /** * Holds information about all profile points registered * * @var Array * @access private */ private $ProfilePoints = Array (); /** * Prevent recursion when processing debug_backtrace() function results * * @var Array * @access private */ private $RecursionStack = Array (); /** * Cross browser debugger report scrollbar width detection * * @var int * @access private */ private $scrollbarWidth = 0; /** * Remembers how much memory & time was spent on including files * * @var Array * @access public * @see kUtil::includeOnce */ public $IncludesData = Array (); /** * Remembers maximal include deep level * * @var int * @access public * @see kUtil::includeOnce */ public $IncludeLevel = 0; /** * Prevents report generation more then once * * @var bool * @access private */ private $_inReportPrinting = false; /** * Transparent spacer image used in case of none spacer image defined via SPACER_URL constant. * Used while drawing progress bars (memory usage, time usage, etc.) * * @var string * @access private */ private $dummyImage = ''; /** * Temporary files created by debugger will be stored here * * @var string * @access private */ private $tempFolder = ''; /** * Debug rows will be separated using this string before writing to debug file * * @var string * @access private */ private $rowSeparator = '@@'; /** * Base URL for debugger includes * * @var string * @access private */ private $baseURL = ''; /** * Sub-folder, where In-Portal is installed * * @var string * @access private */ private $basePath = ''; /** * Holds last recorded timestamp (for appendTimestamp) * * @var int * @access private */ private $LastMoment; /** * Determines, that current request is AJAX request * * @var bool * @access private */ private $_isAjax = false; /** * Data, parsed from the editor url. * * @var array */ protected $editorUrlData = array('url' => '', 'params' => array()); /** * Creates instance of debugger */ public function __construct() { global $start, $dbg_options; // check if user haven't defined DEBUG_MODE contant directly if ( defined('DEBUG_MODE') && DEBUG_MODE ) { die('error: constant DEBUG_MODE defined directly, please use <strong>$dbg_options</strong> array instead'); } if ( class_exists('kUtil') ) { DebuggerUtil::$trustProxy = kUtil::getSystemConfig()->get('TrustProxy'); } // check IP before enabling debug mode $ip_match = DebuggerUtil::ipMatch(isset($dbg_options['DBG_IP']) ? $dbg_options['DBG_IP'] : ''); if ( !$ip_match || (isset($_COOKIE['debug_off']) && $_COOKIE['debug_off']) ) { define('DEBUG_MODE', 0); return; } // debug is allowed for user, continue initialization $this->InitDebugger(); $this->profileStart('kernel4_startup', 'Startup and Initialization of kernel4', $start); $this->profileStart('script_runtime', 'Script runtime', $start); $this->LastMoment = $start; error_reporting(E_ALL & ~E_STRICT); // show errors on screen in case if not in Zend Studio debugging ini_set('display_errors', DebuggerUtil::constOn('DBG_ZEND_PRESENT') ? 0 : 1); // vertical scrollbar width differs in Firefox and other browsers $this->scrollbarWidth = $this->isGecko() ? 22 : 25; $this->appendRequest(); } /** * Set's default values to constants debugger uses * */ function InitDebugger() { global $dbg_options; unset($dbg_options['DBG_IP']); // Detect fact, that this session being debugged by Zend Studio foreach ($_COOKIE as $cookie_name => $cookie_value) { if (substr($cookie_name, 0, 6) == 'debug_') { DebuggerUtil::safeDefine('DBG_ZEND_PRESENT', 1); break; } } DebuggerUtil::safeDefine('DBG_ZEND_PRESENT', 0); // set this constant value to 0 (zero) to debug debugger using Zend Studio // set default values for debugger constants $dbg_constMap = Array ( 'DBG_USE_HIGHLIGHT' => 1, // highlight output same as php code using "highlight_string" function 'DBG_WINDOW_WIDTH' => 700, // set width of debugger window (in pixels) for better viewing large amount of debug data 'DBG_USE_SHUTDOWN_FUNC' => DBG_ZEND_PRESENT ? 0 : 1, // use shutdown function to include debugger code into output 'DBG_HANDLE_ERRORS' => DBG_ZEND_PRESENT ? 0 : 1, // handle all allowed by php (see php manual) errors instead of default handler 'DBG_DOMVIEWER' => '/temp/domviewer.html', // path to DOMViewer on website 'DOC_ROOT' => str_replace('\\', '/', realpath($_SERVER['DOCUMENT_ROOT']) ), // windows hack 'DBG_LOCAL_BASE_PATH' => 'w:', // replace DOC_ROOT in filenames (in errors) using this path 'DBG_EDITOR_URL' => 'file://%F:%L', 'DBG_SHORTCUT' => 'F12', // Defines debugger activation shortcut (any symbols or Ctrl/Alt/Shift are allowed, e.g. Ctrl+Alt+F12) ); // debugger is initialized before kHTTPQuery, so do jQuery headers check here too if (array_key_exists('HTTP_X_REQUESTED_WITH', $_SERVER) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') { $this->_isAjax = true; } elseif (array_key_exists('ajax', $_GET) && $_GET['ajax'] == 'yes') { $this->_isAjax = true; } // user defined options override debugger defaults $dbg_constMap = array_merge($dbg_constMap, $dbg_options); if ($this->_isAjax && array_key_exists('DBG_SKIP_AJAX', $dbg_constMap) && $dbg_constMap['DBG_SKIP_AJAX']) { $dbg_constMap['DBG_SKIP_REPORTING'] = 1; } // allows to validate unit configs via request variable if ( !array_key_exists('DBG_VALIDATE_CONFIGS', $dbg_constMap) ) { $dbg_constMap['DBG_VALIDATE_CONFIGS'] = array_key_exists('validate_configs', $_GET) ? (int)$_GET['validate_configs'] : 0; } // when validation configs, don't show sqls for better validation error displaying if ($dbg_constMap['DBG_VALIDATE_CONFIGS']) { $dbg_constMap['DBG_SQL_PROFILE'] = 0; } // when showing explain make shure, that debugger window is large enough if (array_key_exists('DBG_SQL_EXPLAIN', $dbg_constMap) && $dbg_constMap['DBG_SQL_EXPLAIN']) { $dbg_constMap['DBG_WINDOW_WIDTH'] = 1000; } foreach ($dbg_constMap as $dbg_constName => $dbg_constValue) { DebuggerUtil::safeDefine($dbg_constName, $dbg_constValue); } $this->parseEditorUrl(); } /** * Parses editor url. * * @return void */ protected function parseEditorUrl() { $components = parse_url(DBG_EDITOR_URL); $this->editorUrlData['url'] = $components['scheme'] . '://' . $components['host']; if ( isset($components['path']) ) { $this->editorUrlData['url'] .= $components['path']; } if ( isset($components['query']) ) { parse_str(html_entity_decode($components['query']), $this->editorUrlData['params']); } } /** * Performs debugger initialization * * @return void */ private function InitReport() { if ( !class_exists('kApplication') ) { return; } $application =& kApplication::Instance(); // string used to separate debugger records while in file (used in debugger dump filename too) $this->rowSeparator = '@' . (/*is_object($application->Factory) &&*/ $application->InitDone ? $application->GetSID() : 0) . '@'; // $this->rowSeparator = '@' . rand(0, 100000) . '@'; // include debugger files from this url $reg_exp = '/^' . preg_quote(FULL_PATH, '/') . '/'; $kernel_path = preg_replace($reg_exp, '', KERNEL_PATH, 1); $this->baseURL = PROTOCOL . SERVER_NAME . (defined('PORT') ? ':' . PORT : '') . rtrim(BASE_PATH, '/') . $kernel_path . '/utility/debugger'; // store debugger cookies at this path $this->basePath = rtrim(BASE_PATH, '/'); // save debug output in this folder $this->tempFolder = defined('RESTRICTED') ? RESTRICTED : WRITEABLE . '/cache'; } /** * Appends all passed variable values (without variable names) to debug output * * @return void * @access public */ public function dumpVars() { $dump_mode = 'var_dump'; $dumpVars = func_get_args(); if ( $dumpVars[count($dumpVars) - 1] === 'STRICT' ) { $dump_mode = 'strict_var_dump'; array_pop($dumpVars); } foreach ($dumpVars as $varValue) { $this->Data[] = Array ('value' => $varValue, 'debug_type' => $dump_mode); } } /** * Transforms collected data at given index into human-readable HTML to place in debugger report * * @param int $dataIndex * @return string * @access private */ private function prepareHTML($dataIndex) { static $errors_displayed = 0; $Data =& $this->Data[$dataIndex]; if ( $Data['debug_type'] == 'html' ) { return $Data['html']; } switch ($Data['debug_type']) { case 'error': $errors_displayed++; $fileLink = $this->getFileLink($Data['file'], $Data['line']); $ret = '<b class="debug_error">' . $this->getErrorNameByCode($Data['no']) . ' (#' . $errors_displayed . ')</b>: ' . $Data['str']; $ret .= ' in <b>' . $fileLink . '</b> on line <b>' . $Data['line'] . '</b>'; return $ret; break; case 'exception': $fileLink = $this->getFileLink($Data['file'], $Data['line']); $ret = '<b class="debug_error">' . $Data['exception_class'] . '</b>: ' . $Data['str']; $ret .= ' in <b>' . $fileLink . '</b> on line <b>' . $Data['line'] . '</b>'; return $ret; break; case 'var_dump': return $this->highlightString($this->print_r($Data['value'], true)); break; case 'strict_var_dump': return $this->highlightString(var_export($Data['value'], true)); break; case 'trace': ini_set('memory_limit', '500M'); $trace =& $Data['trace']; $i = 0; $traceCount = count($trace); $ret = ''; while ( $i < $traceCount ) { $traceRec =& $trace[$i]; $argsID = 'trace_args_' . $dataIndex . '_' . $i; $has_args = isset($traceRec['args']); if ( isset($traceRec['file']) ) { $func_name = isset($traceRec['class']) ? $traceRec['class'] . $traceRec['type'] . $traceRec['function'] : $traceRec['function']; $args_link = $has_args ? '<a href="javascript:$Debugger.ToggleTraceArgs(\'' . $argsID . '\');" title="Show/Hide Function Arguments"><b>Function</b></a>' : '<strong>Function</strong>'; $ret .= $args_link . ': ' . $this->getFileLink($traceRec['file'], $traceRec['line'], $func_name); $ret .= ' in <b>' . basename($traceRec['file']) . '</b> on line <b>' . $traceRec['line'] . '</b><br>'; } else { $ret .= 'no file information available'; } if ( $has_args ) { // if parameter value is longer then 200 symbols, then leave only first 50 $args = $this->highlightString($this->print_r($traceRec['args'], true)); $ret .= '<div id="' . $argsID . '" style="display: none;">' . $args . '</div>'; } $i++; } return $ret; break; case 'profiler': $profileKey = $Data['profile_key']; $Data =& $this->ProfilerData[$profileKey]; $runtime = ($Data['ends'] - $Data['begins']); // in seconds $totals_key = getArrayValue($Data, 'totalsKey'); if ( $totals_key ) { $total_before = $Data['totalsBefore']; $total = $this->ProfilerTotals[$totals_key]; $div_width = Array (); $total_width = ($this->getWindowWidth() - 10); $div_width['before'] = round(($total_before / $total) * $total_width); $div_width['current'] = round(($runtime / $total) * $total_width); $div_width['left'] = round((($total - $total_before - $runtime) / $total) * $total_width); $subtitle = array_key_exists('subtitle', $Data) ? ' (' . $Data['subtitle'] . ')' : ''; $ret = '<b>Name' . $subtitle . '</b>: ' . $Data['description'] . '<br />'; $additional = isset($Data['additional']) ? $Data['additional'] : Array (); if ( isset($Data['file']) ) { array_unshift($additional, Array ('name' => 'File', 'value' => $this->getFileLink($Data['file'], $Data['line'], basename($Data['file']) . ':' . $Data['line']))); } array_unshift($additional, Array ('name' => 'Runtime', 'value' => $runtime . 's')); $ret .= '<div>'; //FF 3.5 needs this! foreach ($additional as $mixed_param) { $ret .= '[<strong>' . $mixed_param['name'] . '</strong>: ' . $mixed_param['value'] . '] '; } /*if ( isset($Data['file']) ) { $ret .= '[<b>Runtime</b>: ' . $runtime . 's] [<b>File</b>: ' . $this->getFileLink($Data['file'], $Data['line'], basename($Data['file']) . ':' . $Data['line']) . ']<br />'; } else { $ret .= '<b>Runtime</b>: ' . $runtime . 's<br />'; }*/ $ret .= '</div>'; $ret .= '<div class="dbg_profiler" style="width: ' . $div_width['before'] . 'px; border-right: 0px; background-color: #298DDF;"><img src="' . $this->dummyImage . '" width="1" height="1"/></div>'; $ret .= '<div class="dbg_profiler" style="width: ' . $div_width['current'] . 'px; border-left: 0px; border-right: 0px; background-color: #EF4A4A;"><img src="' . $this->dummyImage . '" width="1" height="1"/></div>'; $ret .= '<div class="dbg_profiler" style="width: ' . $div_width['left'] . 'px; border-left: 0px; background-color: #DFDFDF;"><img src="' . $this->dummyImage . '" width="1" height="1"/></div>'; return $ret; } else { return '<b>Name</b>: ' . $Data['description'] . '<br><b>Runtime</b>: ' . $runtime . 's'; } break; default: return 'incorrect debug data'; break; } } /** * Returns row type for debugger row. * * @param integer $data_index Index of the row. * * @return string */ protected function getRowType($data_index) { $data = $this->Data[$data_index]; switch ($data['debug_type']) { case 'html': if ( strpos($data['html'], 'SQL Total time') !== false ) { return self::ROW_TYPE_SQL; } break; case 'error': $error_map = array( 'Fatal Error' => self::ROW_TYPE_ERROR, 'Warning' => self::ROW_TYPE_WARNING, 'Notice' => self::ROW_TYPE_NOTICE, ); return $error_map[$this->getErrorNameByCode($data['no'])]; break; case 'exception': return self::ROW_TYPE_ERROR; break; case 'profiler': if ( preg_match('/^sql_/', $data['profile_key']) ) { return self::ROW_TYPE_SQL; } break; } return self::ROW_TYPE_OTHER; } /** * Returns debugger report window width excluding scrollbar * * @return int * @access private */ private function getWindowWidth() { return DBG_WINDOW_WIDTH - $this->scrollbarWidth - 8; } /** * Tells debugger to skip objects that are heavy in plan of memory usage while printing debug_backtrace results * * @param Object $object * @return bool * @access private */ private function IsBigObject(&$object) { $skip_classes = Array( defined('APPLICATION_CLASS') ? APPLICATION_CLASS : 'kApplication', 'kFactory', 'kUnitConfigReader', 'NParser', ); foreach ($skip_classes as $class_name) { if ( strtolower(get_class($object)) == strtolower($class_name) ) { return true; } } return false; } /** - * Advanced version of print_r (for debugger only). Don't print objects recursively + * Advanced version of print_r (for debugger only). Don't print objects recursively. + * + * @param mixed $p_array Value to be printed. + * @param boolean $return_output Return output or print it out. + * @param integer $tab_count Offset in tabs. * - * @param Array $array - * @param bool $return_output return output or print it out - * @param int $tab_count offset in tabs * @return string - * @access private */ - private function print_r(&$array, $return_output = false, $tab_count = -1) + private function print_r(&$p_array, $return_output = false, $tab_count = -1) { static $first_line = true; // not an array at all - if ( !is_array($array) ) { - switch ( gettype($array) ) { + if ( !is_array($p_array) ) { + switch ( gettype($p_array) ) { case 'NULL': return 'NULL' . "\n"; break; case 'object': - return $this->processObject($array, $tab_count); + return $this->processObject($p_array, $tab_count); + break; + + case 'resource': + return (string)$p_array . "\n"; break; default: // number or string - if ( strlen($array) > 200 ) { - $array = substr($array, 0, 50) . ' ...'; + if ( strlen($p_array) > 200 ) { + $p_array = substr($p_array, 0, 50) . ' ...'; } - return $array . "\n"; + + return $p_array . "\n"; break; } } $output = ''; + if ( count($p_array) > 50 ) { + $array = array_slice($p_array, 0, 50); + $array[] = '...'; + } + else { + $array = $p_array; + } + $tab_count++; $output .= "Array\n" . str_repeat(' ', $tab_count) . "(\n"; $tab_count++; $tabsign = $tab_count ? str_repeat(' ', $tab_count) : ''; $array_keys = array_keys($array); foreach ($array_keys as $key) { switch ( gettype($array[$key]) ) { case 'array': $output .= $tabsign . '[' . $key . '] = ' . $this->print_r($array[$key], true, $tab_count); break; case 'boolean': $output .= $tabsign . '[' . $key . '] = ' . ($array[$key] ? 'true' : 'false') . "\n"; break; case 'integer': case 'double': case 'string': if ( strlen($array[$key]) > 200 ) { $array[$key] = substr($array[$key], 0, 50) . ' ...'; } $output .= $tabsign . '[' . $key . '] = ' . $array[$key] . "\n"; break; case 'NULL': $output .= $tabsign . '[' . $key . "] = NULL\n"; break; case 'object': $output .= $tabsign . '[' . $key . "] = "; $output .= "Object (" . get_class($array[$key]) . ") = \n" . str_repeat(' ', $tab_count + 1) . "(\n"; $output .= $this->processObject($array[$key], $tab_count + 2); $output .= str_repeat(' ', $tab_count + 1) . ")\n"; break; default: $output .= $tabsign . '[' . $key . '] unknown = ' . gettype($array[$key]) . "\n"; break; } } $tab_count--; $output .= str_repeat(' ', $tab_count) . ")\n"; if ( $first_line ) { $first_line = false; $output .= "\n"; } $tab_count--; if ( $return_output ) { return $output; } else { echo $output; } return true; } /** * Returns string representation of given object (more like print_r, but with recursion prevention check) * * @param Object $object * @param int $tab_count * @return string * @access private */ private function processObject(&$object, $tab_count) { $object_class = get_class($object); if ( !in_array($object_class, $this->RecursionStack) ) { if ( $this->IsBigObject($object) ) { return 'SKIPPED (class: ' . $object_class . ")\n"; } $attribute_names = get_class_vars($object_class); if ( !$attribute_names ) { return "NO_ATTRIBUTES\n"; } else { $output = ''; array_push($this->RecursionStack, $object_class); $tabsign = $tab_count ? str_repeat(' ', $tab_count) : ''; foreach ($attribute_names as $attribute_name => $attribute_value) { if ( is_object($object->$attribute_name) ) { // it is object $output .= $tabsign . '[' . $attribute_name . '] = ' . $this->processObject($object->$attribute_name, $tab_count + 1); } else { $output .= $tabsign . '[' . $attribute_name . '] = ' . $this->print_r($object->$attribute_name, true, $tab_count); } } array_pop($this->RecursionStack); return $output; } } else { // object [in recursion stack] return '*** RECURSION *** (class: ' . $object_class . ")\n"; } } /** * Format SQL Query using predefined formatting * and highlighting techniques * * @param string $sql * @return string * @access public */ public function formatSQL($sql) { $sql = trim(preg_replace('/(\n|\t| )+/is', ' ', $sql)); // whitespace in the beginning of the regex is to avoid splitting inside words, for example "FROM int_ConfigurationValues" into "FROM intConfiguration\n\tValues" $formatted_sql = preg_replace('/\s(CREATE TABLE|DROP TABLE|SELECT|UPDATE|SET|REPLACE|INSERT|DELETE|VALUES|FROM|LEFT JOIN|INNER JOIN|LIMIT|WHERE|HAVING|GROUP BY|ORDER BY)\s/is', "\n\t$1 ", ' ' . $sql); $formatted_sql = $this->highlightString($formatted_sql); if ( defined('DBG_SQL_EXPLAIN') && DBG_SQL_EXPLAIN ) { if ( substr($sql, 0, 6) == 'SELECT' ) { $formatted_sql .= '<br/>' . '<strong>Explain</strong>:<br /><br />'; $explain_result = $this->Application->Conn->Query('EXPLAIN ' . $sql, null, true); $explain_table = ''; foreach ($explain_result as $explain_row) { if ( !$explain_table ) { // first row -> draw header $explain_table .= '<tr class="explain_header"><td>' . implode('</td><td>', array_keys($explain_row)) . '</td></tr>'; } $explain_table .= '<tr><td>' . implode('</td><td>', $explain_row) . '</td></tr>'; } $formatted_sql .= '<table class="dbg_explain_table">' . $explain_table . '</table>'; } } return $formatted_sql; } /** * Highlights given string using "highlight_string" method * * @param string $string * @return string * @access public */ public function highlightString($string) { if ( !(defined('DBG_USE_HIGHLIGHT') && DBG_USE_HIGHLIGHT) || $this->_compileError ) { return nl2br($string); } $string = str_replace(Array ('\\', '/'), Array ('_no_match_string_', '_n_m_s_'), $string); $this->_compileError = true; // next line is possible cause of compile error $string = highlight_string('<?php ' . $string . ' ?>', true); $this->_compileError = false; $string = str_replace(Array ('_no_match_string_', '_n_m_s_'), Array ('\\', '/'), $string); if ( strlen($string) >= 65536 ) { // preg_replace will fail, when string is longer, then 65KB return str_replace(Array ('<?php ', '?>'), '', $string); } return preg_replace('/<\?(.*)php (.*)\?>/Us', '\\2', $string); } /** * Determine by php type of browser used to show debugger * * @return bool * @access private */ private function isGecko() { // we need isset because we may run scripts from shell with no user_agent at all return isset($_SERVER['HTTP_USER_AGENT']) && strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'firefox') !== false; } /** * Returns link for editing php file (from error) in external editor * * @param string $file filename with path from root folder * @param int $line_number line number in file where error is found * @param string $title text to show on file edit link * @return string * @access public */ public function getFileLink($file, $line_number = 1, $title = '') { if ( !$title ) { $title = str_replace('/', '\\', $this->getLocalFile($file)); } $local_file = $this->getLocalFile($file); $url_params = $this->editorUrlData['params']; foreach ( $url_params as $param_name => $param_value ) { $url_params[$param_name] = str_replace( array('%F', '%L'), array($local_file, $line_number), $param_value ); } $url = $this->editorUrlData['url'] . '?' . http_build_query($url_params); return '<a href="' . $url . '">' . $title . '</a>'; } /** * Converts filepath on server to filepath in mapped DocumentRoot on developer pc * * @param string $remoteFile * @return string * @access private */ private function getLocalFile($remoteFile) { return preg_replace('/^' . preg_quote(DOC_ROOT, '/') . '/', DBG_LOCAL_BASE_PATH, $remoteFile, 1); } /** * Appends call trace till this method call * * @param int $levels_to_shift * @return void * @access public */ public function appendTrace($levels_to_shift = 1) { $levels_shifted = 0; $trace = debug_backtrace(); while ( $levels_shifted < $levels_to_shift ) { array_shift($trace); $levels_shifted++; } $this->Data[] = Array ('trace' => $trace, 'debug_type' => 'trace'); } /** * Appends call trace till this method call * * @param Exception $exception * @return void * @access private */ private function appendExceptionTrace(&$exception) { $trace = $exception->getTrace(); $this->Data[] = Array('trace' => $trace, 'debug_type' => 'trace'); } /** * Adds memory usage statistics * * @param string $msg * @param int $used * @return void * @access public */ public function appendMemoryUsage($msg, $used = null) { if ( !isset($used) ) { $used = round(memory_get_usage() / 1024); } $this->appendHTML('<b>Memory usage</b> ' . $msg . ' ' . $used . 'Kb'); } /** * Appends HTML code without transformations * * @param string $html * @return void * @access public */ public function appendHTML($html) { $this->Data[] = Array ('html' => $html, 'debug_type' => 'html'); } /** * Returns instance of FirePHP class * * @return FirePHP * @link http://www.firephp.org/HQ/Use.htm */ function firePHP() { require_once('FirePHPCore/FirePHP.class.php'); return FirePHP::getInstance(true); } /** * Change debugger info that was already generated before. * Returns true if html was set. * * @param int $index * @param string $html * @param string $type = {'append','prepend','replace'} * @return bool * @access public */ public function setHTMLByIndex($index, $html, $type = 'append') { if ( !isset($this->Data[$index]) || $this->Data[$index]['debug_type'] != 'html' ) { return false; } switch ( $type ) { case 'append': $this->Data[$index]['html'] .= '<br>' . $html; break; case 'prepend': $this->Data[$index]['html'] = $this->Data[$index]['html'] . '<br>' . $html; break; case 'replace': $this->Data[$index]['html'] = $html; break; } return true; } /** * Move $debugLineCount lines of input from debug output * end to beginning. * * @param int $debugLineCount * @return void * @access private */ private function moveToBegin($debugLineCount) { $lines = array_splice($this->Data, count($this->Data) - $debugLineCount, $debugLineCount); $this->Data = array_merge($lines, $this->Data); } /** * Moves all debugger report lines after $debugLineCount into $new_row position * * @param int $new_row * @param int $debugLineCount * @return void * @access private */ private function moveAfterRow($new_row, $debugLineCount) { $lines = array_splice($this->Data, count($this->Data) - $debugLineCount, $debugLineCount); $rows_before = array_splice($this->Data, 0, $new_row, $lines); $this->Data = array_merge($rows_before, $this->Data); } /** * Appends HTTP REQUEST information to debugger report * * @return void * @access private */ private function appendRequest() { if ( isset($_SERVER['SCRIPT_FILENAME']) ) { $script = $_SERVER['SCRIPT_FILENAME']; } else { $script = $_SERVER['DOCUMENT_ROOT'] . $_SERVER['PHP_SELF']; } $this->appendHTML('ScriptName: <b>' . $this->getFileLink($script, 1, basename($script)) . '</b> (<b>' . dirname($script) . '</b>)'); if ( $this->_isAjax ) { $this->appendHTML('RequestURI: ' . $_SERVER['REQUEST_URI'] . ' (QS Length:' . strlen($_SERVER['QUERY_STRING']) . ')'); } $tools_html = ' <table style="width: ' . $this->getWindowWidth() . 'px;"> <tr> <td>' . $this->_getDomViewerHTML() . '</td> <td>' . $this->_getToolsHTML() . '</td> </tr> </table>'; $this->appendHTML($tools_html); ob_start(); ?> <table border="0" cellspacing="0" cellpadding="0" class="dbg_flat_table" style="width: <?php echo $this->getWindowWidth(); ?>px;"> <thead style="font-weight: bold;"> <td width="20">Src</td><td>Name</td><td>Value</td> </thead> <?php $super_globals = Array ('GE' => $_GET, 'PO' => $_POST, 'CO' => $_COOKIE); foreach ($super_globals as $prefix => $data) { foreach ($data as $key => $value) { if ( !is_array($value) && trim($value) == '' ) { $value = '<b class="debug_error">no value</b>'; } else { $value = htmlspecialchars($this->print_r($value, true), ENT_QUOTES, 'UTF-8'); } echo '<tr><td>' . $prefix . '</td><td>' . $key . '</td><td>' . $value . '</td></tr>'; } } ?> </table> <?php $this->appendHTML(ob_get_contents()); ob_end_clean(); } /** * Appends php session content to debugger output * * @return void * @access private */ private function appendSession() { if ( isset($_SESSION) && $_SESSION ) { $this->appendHTML('PHP Session: [<b>' . ini_get('session.name') . '</b>]'); $this->dumpVars($_SESSION); $this->moveToBegin(2); } } /** * Starts profiling of a given $key * * @param string $key * @param string $description * @param int $timeStamp * @return void * @access public */ public function profileStart($key, $description = null, $timeStamp = null) { if ( !isset($timeStamp) ) { $timeStamp = microtime(true); } $this->ProfilerData[$key] = Array ('begins' => $timeStamp, 'ends' => 5000, 'debuggerRowID' => count($this->Data)); if ( isset($description) ) { $this->ProfilerData[$key]['description'] = $description; } if ( substr($key, 0, 4) == 'sql_' ) { // append place from what was called $trace_results = debug_backtrace(); $trace_count = count($trace_results); $i = 0; while ( $i < $trace_count ) { if ( !isset($trace_results[$i]['file']) ) { $i++; continue; } $trace_file = basename($trace_results[$i]['file']); if ( $trace_file != 'db_connection.php' && $trace_file != 'db_load_balancer.php' && $trace_file != 'adodb.inc.php' ) { break; } $i++; } $this->ProfilerData[$key]['file'] = $trace_results[$i]['file']; $this->ProfilerData[$key]['line'] = $trace_results[$i]['line']; if ( isset($trace_results[$i + 1]['object']) && isset($trace_results[$i + 1]['object']->Prefix) ) { $object =& $trace_results[$i + 1]['object']; /* @var $object kBase */ $prefix_special = rtrim($object->Prefix . '.' . $object->Special, '.'); $this->ProfilerData[$key]['prefix_special'] = $prefix_special; } unset($trace_results); } $this->Data[] = Array ('profile_key' => $key, 'debug_type' => 'profiler'); } /** * Ends profiling for a given $key * * @param string $key * @param string $description * @param int $timeStamp * @return void * @access public */ public function profileFinish($key, $description = null, $timeStamp = null) { if ( !isset($timeStamp) ) { $timeStamp = microtime(true); } $this->ProfilerData[$key]['ends'] = $timeStamp; if ( isset($description) ) { $this->ProfilerData[$key]['description'] = $description; } if ( substr($key, 0, 4) == 'sql_' ) { $func_arguments = func_get_args(); $rows_affected = $func_arguments[3]; $additional = Array (); if ( $rows_affected > 0 ) { $additional[] = Array ('name' => 'Affected Rows', 'value' => $rows_affected); if ( isset($func_arguments[4]) ) { if ( strlen($func_arguments[4]) > 200 ) { $func_arguments[4] = substr($func_arguments[4], 0, 50) . ' ...'; } $additional[] = Array ('name' => 'Result', 'value' => $func_arguments[4]); } } $additional[] = Array ('name' => 'Query Number', 'value' => $func_arguments[5]); if ( $func_arguments[6] ) { $this->profilerAddTotal('cachable_queries', $key); $this->ProfilerData[$key]['subtitle'] = 'cachable'; } if ( (string)$func_arguments[7] !== '' ) { $additional[] = Array ('name' => 'Server #', 'value' => $func_arguments[7]); } if ( array_key_exists('prefix_special', $this->ProfilerData[$key]) ) { $additional[] = Array ('name' => 'PrefixSpecial', 'value' => $this->ProfilerData[$key]['prefix_special']); } $this->ProfilerData[$key]['additional'] =& $additional; } } /** * Collects total execution time from profiler record * * @param string $total_key * @param string $key * @param int $value * @return void * @access public */ public function profilerAddTotal($total_key, $key = null, $value = null) { if ( !isset($this->ProfilerTotals[$total_key]) ) { $this->ProfilerTotals[$total_key] = 0; $this->ProfilerTotalCount[$total_key] = 0; } if ( !isset($value) ) { $value = $this->ProfilerData[$key]['ends'] - $this->ProfilerData[$key]['begins']; } if ( isset($key) ) { $this->ProfilerData[$key]['totalsKey'] = $total_key; $this->ProfilerData[$key]['totalsBefore'] = $this->ProfilerTotals[$total_key]; } $this->ProfilerTotals[$total_key] += $value; $this->ProfilerTotalCount[$total_key]++; } /** * Traces relative code execution speed between this method calls * * @param string $message * @return void * @access public */ public function appendTimestamp($message) { global $start; $time = microtime(true); $from_last = $time - $this->LastMoment; $from_start = $time - $start; $this->appendHTML(sprintf("<strong>%s</strong> %.5f from last %.5f from start", $message, $from_last, $from_start)); $this->LastMoment = $time; } /** * Returns unique ID for each method call * * @return int * @access public */ public function generateID() { list($usec, $sec) = explode(' ', microtime()); $id_part_1 = substr($usec, 4, 4); $id_part_2 = mt_rand(1, 9); $id_part_3 = substr($sec, 6, 4); $digit_one = substr($id_part_1, 0, 1); if ( $digit_one == 0 ) { $digit_one = mt_rand(1, 9); $id_part_1 = preg_replace('/^0/', '', $id_part_1); $id_part_1 = $digit_one . $id_part_1; } return $id_part_1 . $id_part_2 . $id_part_3; } /** * Returns error name based on it's code * * @param int $error_code * @return string * @access private */ private function getErrorNameByCode($error_code) { $error_map = Array ( 'Fatal Error' => Array (E_RECOVERABLE_ERROR, E_USER_ERROR, E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE), 'Warning' => Array (E_WARNING, E_USER_WARNING, E_CORE_WARNING, E_COMPILE_WARNING), 'Notice' => Array (E_NOTICE, E_USER_NOTICE, E_STRICT), ); if ( defined('E_DEPRECATED') ) { // since PHP 5.3 $error_map['Notice'][] = E_DEPRECATED; $error_map['Notice'][] = E_USER_DEPRECATED; } foreach ($error_map as $error_name => $error_codes) { if ( in_array($error_code, $error_codes) ) { return $error_name; } } return ''; } /** * Returns profile total key (check against missing key too) * * @param string $key * @return int * @access private */ private function getProfilerTotal($key) { if ( isset($this->ProfilerTotalCount[$key]) ) { return (int)$this->ProfilerTotalCount[$key]; } return 0; } /** * Counts how much calls were made to a place, where this method is called (basic version of profiler) * * @param string $title * @param int $level * @return void * @access public */ public function ProfilePoint($title, $level = 1) { $trace_results = debug_backtrace(); $level = min($level, count($trace_results) - 1); do { $point = $trace_results[$level]; $location = $point['file'] . ':' . $point['line']; $level++; $has_more = isset($trace_results[$level]); } while ( $has_more && $point['function'] == $trace_results[$level]['function'] ); if ( !isset($this->ProfilePoints[$title]) ) { $this->ProfilePoints[$title] = Array (); } if ( !isset($this->ProfilePoints[$title][$location]) ) { $this->ProfilePoints[$title][$location] = 0; } $this->ProfilePoints[$title][$location]++; } /** * Generates report * * @param boolean $return_result Returns or output report contents. * @param boolean $clean_output_buffer Clean output buffers before displaying anything. * @param boolean $is_shutdown_func Called from shutdown function. * * @return string */ public function printReport($return_result = false, $clean_output_buffer = true, $is_shutdown_func = false) { if ( $this->_inReportPrinting ) { // don't print same report twice (in case if shutdown function used + compression + fatal error) return ''; } $this->_inReportPrinting = true; $last_error = error_get_last(); if ( !is_null($last_error) && $is_shutdown_func ) { $this->saveError( $last_error['type'], $last_error['message'], $last_error['file'], $last_error['line'], null, $is_shutdown_func ); } $this->profileFinish('script_runtime'); $this->_breakOutOfBuffering(!$return_result); $debugger_start = memory_get_usage(); if ( defined('SPACER_URL') ) { $this->dummyImage = SPACER_URL; } $this->InitReport(); // set parameters required by AJAX // defined here, because user can define this constant while script is running, not event before debugger is started DebuggerUtil::safeDefine('DBG_RAISE_ON_WARNINGS', 0); DebuggerUtil::safeDefine('DBG_TOOLBAR_BUTTONS', 1); $this->appendSession(); // show php session if any // ensure, that 1st line of debug output always is this one: $top_line = '<table cellspacing="0" cellpadding="0" style="width: ' . $this->getWindowWidth() . 'px; margin: 0px;"><tr><td align="left" width="50%">[<a href="javascript:window.location.reload();">Reload Frame</a>] [<a href="javascript:$Debugger.Toggle(27);">Hide Debugger</a>] [<a href="javascript:$Debugger.Clear();">Clear Debugger</a>]</td><td align="right" width="50%">[Current Time: <b>' . date('H:i:s') . '</b>] [File Size: <b>#DBG_FILESIZE#</b>]</td></tr><tr><td align="left" colspan="2" style="padding-top: 5px;">' . $this->getFilterDropdown() . '</td></tr></table>'; $this->appendHTML($top_line); $this->moveToBegin(1); if ( count($this->ProfilePoints) > 0 ) { foreach ($this->ProfilePoints as $point => $locations) { arsort($this->ProfilePoints[$point]); } $this->appendHTML($this->highlightString($this->print_r($this->ProfilePoints, true))); } if ( DebuggerUtil::constOn('DBG_SQL_PROFILE') && isset($this->ProfilerTotals['sql']) ) { // sql query profiling was enabled -> show totals if ( array_key_exists('cachable_queries', $this->ProfilerTotalCount) ) { $append = ' <strong>Cachable queries</strong>: ' . $this->ProfilerTotalCount['cachable_queries']; } else { $append = ''; } $this->appendHTML('<b>SQL Total time:</b> ' . $this->ProfilerTotals['sql'] . ' <b>Number of queries</b>: ' . $this->ProfilerTotalCount['sql'] . $append); } if ( DebuggerUtil::constOn('DBG_PROFILE_INCLUDES') && isset($this->ProfilerTotals['includes']) ) { // included file profiling was enabled -> show totals $this->appendHTML('<b>Included Files Total time:</b> ' . $this->ProfilerTotals['includes'] . ' Number of includes: ' . $this->ProfilerTotalCount['includes']); } if ( DebuggerUtil::constOn('DBG_PROFILE_MEMORY') ) { // detailed memory usage reporting by objects was enabled -> show totals $this->appendHTML('<b>Memory used by Objects:</b> ' . round($this->ProfilerTotals['objects'] / 1024, 2) . 'Kb'); } if ( DebuggerUtil::constOn('DBG_INCLUDED_FILES') ) { $files = get_included_files(); $this->appendHTML('<strong>Included files:</strong>'); foreach ($files as $file) { $this->appendHTML($this->getFileLink($this->getLocalFile($file)) . ' (' . round(filesize($file) / 1024, 2) . 'Kb)'); } } if ( DebuggerUtil::constOn('DBG_PROFILE_INCLUDES') ) { $totals = $totals_configs = Array ('mem' => 0, 'time' => 0); $this->appendHTML('<b>Included files statistics:</b>' . (DebuggerUtil::constOn('DBG_SORT_INCLUDES_MEM') ? ' (sorted by memory usage)' : '')); if ( is_array($this->IncludesData['mem']) ) { if ( DebuggerUtil::constOn('DBG_SORT_INCLUDES_MEM') ) { array_multisort($this->IncludesData['mem'], SORT_DESC, $this->IncludesData['file'], $this->IncludesData['time'], $this->IncludesData['level']); } foreach ($this->IncludesData['file'] as $key => $file_name) { $this->appendHTML(str_repeat(' -> ', ($this->IncludesData['level'][$key] >= 0 ? $this->IncludesData['level'][$key] : 0)) . $file_name . ' Mem: ' . sprintf("%.4f Kb", $this->IncludesData['mem'][$key] / 1024) . ' Time: ' . sprintf("%.4f", $this->IncludesData['time'][$key])); if ( $this->IncludesData['level'][$key] == 0 ) { $totals['mem'] += $this->IncludesData['mem'][$key]; $totals['time'] += $this->IncludesData['time'][$key]; } elseif ( $this->IncludesData['level'][$key] == -1 ) { $totals_configs['mem'] += $this->IncludesData['mem'][$key]; $totals_configs['time'] += $this->IncludesData['time'][$key]; } } $this->appendHTML('<b>Sub-Total classes:</b> ' . ' Mem: ' . sprintf("%.4f Kb", $totals['mem'] / 1024) . ' Time: ' . sprintf("%.4f", $totals['time'])); $this->appendHTML('<b>Sub-Total configs:</b> ' . ' Mem: ' . sprintf("%.4f Kb", $totals_configs['mem'] / 1024) . ' Time: ' . sprintf("%.4f", $totals_configs['time'])); $this->appendHTML('<span class="error"><b>Grand Total:</b></span> ' . ' Mem: ' . sprintf("%.4f Kb", ($totals['mem'] + $totals_configs['mem']) / 1024) . ' Time: ' . sprintf("%.4f", $totals['time'] + $totals_configs['time'])); } } $skip_reporting = DebuggerUtil::constOn('DBG_SKIP_REPORTING') || DebuggerUtil::constOn('DBG_ZEND_PRESENT'); if ( ($this->_isAjax && !DebuggerUtil::constOn('DBG_SKIP_AJAX')) || !$skip_reporting ) { $debug_file = $this->tempFolder . '/debug_' . $this->rowSeparator . '.txt'; if ( file_exists($debug_file) ) { unlink($debug_file); } $i = 0; $fp = fopen($debug_file, 'a'); $lineCount = count($this->Data); while ( $i < $lineCount ) { $html = $this->prepareHTML($i); $row_type = $this->getRowType($i); fwrite($fp, json_encode(Array ('html' => $html, 'row_type' => $row_type)) . $this->rowSeparator); $i++; } fclose($fp); } if ( $skip_reporting ) { // let debugger write report and then don't output anything return ''; } $application =& kApplication::Instance(); $dbg_path = str_replace(FULL_PATH, '', $this->tempFolder); $debugger_params = Array ( 'FilterTypes' => $this->_filterTypes, 'RowSeparator' => $this->rowSeparator, 'ErrorsCount' => (int)$this->getProfilerTotal('error_handling'), 'IsFatalError' => $this->_fatalErrorHappened(), 'SQLCount' => (int)$this->getProfilerTotal('sql'), 'SQLTime' => isset($this->ProfilerTotals['sql']) ? sprintf('%.5f', $this->ProfilerTotals['sql']) : 0, 'ScriptTime' => sprintf('%.5f', $this->ProfilerData['script_runtime']['ends'] - $this->ProfilerData['script_runtime']['begins']), 'ScriptMemory' => DebuggerUtil::formatSize($this->getMemoryUsed($debugger_start)), 'Shortcut' => DBG_SHORTCUT, ); ob_start(); // the <script .. /script> and hidden div helps browser to break out of script tag or attribute esacped // with " or ' in case fatal error (or user-error) occurs inside it in compiled template, // otherwise it has no effect ?> <div style="display: none" x='nothing'><script></script></div><html><body></body></html> <link rel="stylesheet" rev="stylesheet" href="<?php echo $this->baseURL; ?>/debugger.css?v2" type="text/css" media="screen" /> <script type="text/javascript" src="<?php echo $this->baseURL; ?>/debugger.js?v4"></script> <script type="text/javascript"> var $Debugger = new Debugger(<?php echo json_encode($debugger_params); ?>); $Debugger.createEnvironment(<?php echo DBG_WINDOW_WIDTH; ?>, <?php echo $this->getWindowWidth(); ?>); $Debugger.DOMViewerURL = '<?php echo constant('DBG_DOMVIEWER'); ?>'; $Debugger.DebugURL = '<?php echo $this->baseURL.'/debugger_responce.php?sid='.$this->rowSeparator.'&path='.urlencode($dbg_path); ?>'; $Debugger.EventURL = '<?php echo /*is_object($application->Factory) &&*/ $application->InitDone ? $application->HREF('dummy', '', Array ('pass' => 'm', '__NO_REWRITE__' => 1)) : ''; ?>'; $Debugger.BasePath = '<?php echo $this->basePath; ?>'; <?php $is_install = defined('IS_INSTALL') && IS_INSTALL; if ( $this->_fatalErrorHappened() || (!$is_install && DBG_RAISE_ON_WARNINGS && $this->WarningCount) ) { echo '$Debugger.Toggle();'; } if ( DBG_TOOLBAR_BUTTONS ) { echo '$Debugger.AddToolbar("$Debugger");'; } ?> window.focus(); </script> <?php if ( $return_result ) { $ret = ob_get_contents(); if ( $clean_output_buffer ) { ob_end_clean(); } $ret .= $this->getShortReport($this->getMemoryUsed($debugger_start)); return $ret; } else { if ( !DebuggerUtil::constOn('DBG_HIDE_FULL_REPORT') ) { $this->_breakOutOfBuffering(); } elseif ( $clean_output_buffer ) { ob_clean(); } echo $this->getShortReport($this->getMemoryUsed($debugger_start)); } return ''; } function getFilterDropdown() { $filter_options = ''; foreach ( $this->_filterTypes as $filter_type ) { $filter_options .= '<option value="' . $filter_type . '">' . $filter_type . '</option>'; } return 'Show: <select id="dbg_filter" onchange="$Debugger.Filter()"><option value="">All</option>' . $filter_options .'</select>'; } function getMemoryUsed($debugger_start) { if ( !isset($this->ProfilerTotals['error_handling']) ) { $memory_used = $debugger_start; $this->ProfilerTotalCount['error_handling'] = 0; } else { $memory_used = $debugger_start - $this->ProfilerTotals['error_handling']; } return $memory_used; } /** * Format's memory usage report by debugger * * @param int $memory_used * @return string * @access private */ private function getShortReport($memory_used) { if ( DebuggerUtil::constOn('DBG_TOOLBAR_BUTTONS') ) { // evenrything is in toolbar - don't duplicate return ''; } else { // toolbar not visible, then show sql & error count too $info = Array ( 'Script Runtime' => 'PROFILE:script_runtime', 'SQL\'s Runtime' => 'PROFILE_T:sql', '-' => 'SEP:-', 'Notice / Warning' => 'PROFILE_TC:error_handling', 'SQLs Count' => 'PROFILE_TC:sql', ); } $ret = ''; // '<tr><td>Application:</td><td><b>' . DebuggerUtil::formatSize($memory_used) . '</b> (' . $memory_used . ')</td></tr>'; foreach ($info as $title => $value_key) { list ($record_type, $record_data) = explode(':', $value_key, 2); switch ( $record_type ) { case 'PROFILE': // profiler totals value $Data =& $this->ProfilerData[$record_data]; $profile_time = ($Data['ends'] - $Data['begins']); // in seconds $ret .= '<tr><td>' . $title . ':</td><td><b>' . sprintf('%.5f', $profile_time) . ' s</b></td></tr>'; break; case 'PROFILE_TC': // profile totals record count $record_cell = '<td>'; if ( $record_data == 'error_handling' && $this->ProfilerTotalCount[$record_data] > 0 ) { $record_cell = '<td class="debug_error">'; } $ret .= '<tr>' . $record_cell . $title . ':</td>' . $record_cell . '<b>' . $this->ProfilerTotalCount[$record_data] . '</b></td></tr>'; break; case 'PROFILE_T': // profile total $record_cell = '<td>'; $total = array_key_exists($record_data, $this->ProfilerTotals) ? $this->ProfilerTotals[$record_data] : 0; $ret .= '<tr>' . $record_cell . $title . ':</td>' . $record_cell . '<b>' . sprintf('%.5f', $total) . ' s</b></td></tr>'; break; case 'SEP': $ret .= '<tr><td colspan="2" style="height: 1px; background-color: #000000; padding: 0px;"><img src="' . $this->dummyImage . '" height="1" alt=""/></td></tr>'; break; } } return '<br /><table class="dbg_stats_table"><tr><td style="border-color: #FFFFFF;"><table class="dbg_stats_table" align="left">' . $ret . '</table></td></tr></table>'; } /** * Detects if there was a fatal error at some point * * @return boolean */ private function _fatalErrorHappened() { return $this->_fatalErrorHash !== 0; } /** * Creates error hash * * @param string $errfile File, where error happened. * @param integer $errline Line in file, where error happened. * * @return integer */ private function _getErrorHash($errfile, $errline) { return crc32($errfile . ':' . $errline); } /** * User-defined error handler * * @param integer $errno Error code. * @param string $errstr Error message. * @param string $errfile Error file. * @param integer $errline Error line. * @param array $errcontext Error context. * @param boolean $is_shutdown_func Called from shutdown function. * * @return boolean * @throws Exception When unknown error code given. */ public function saveError( $errno, $errstr, $errfile = null, $errline = null, array $errcontext = null, $is_shutdown_func = false ) { $this->ProfilerData['error_handling']['begins'] = memory_get_usage(); $errorType = $this->getErrorNameByCode($errno); if (!$errorType) { throw new Exception('Unknown error type [' . $errno . ']'); } elseif ( substr($errorType, 0, 5) == 'Fatal' ) { $this->_fatalErrorHash = $this->_getErrorHash($errfile, $errline); $this->appendTrace(4); } $this->expandError($errstr, $errfile, $errline); $this->Data[] = Array ( 'no' => $errno, 'str' => $errstr, 'file' => $errfile, 'line' => $errline, 'context' => $errcontext, 'debug_type' => 'error' ); $this->ProfilerData['error_handling']['ends'] = memory_get_usage(); $this->profilerAddTotal('error_handling', 'error_handling'); if ($errorType == 'Warning') { $this->WarningCount++; } if ( $this->_fatalErrorHappened() && $this->_getErrorHash($errfile, $errline) === $this->_fatalErrorHash ) { // Append debugger report to data in buffer & clean buffer afterwards. echo $this->_breakOutOfBuffering(false) . $this->printReport(true); if ( !$is_shutdown_func ) { exit; } } return true; } /** * Adds exception details into debugger but don't cause fatal error * * @param Exception $exception * @return void * @access public */ public function appendException($exception) { $this->ProfilerData['error_handling']['begins'] = memory_get_usage(); $this->appendExceptionTrace($exception); $errno = $exception->getCode(); $errstr = $exception->getMessage(); $errfile = $exception->getFile(); $errline = $exception->getLine(); $this->expandError($errstr, $errfile, $errline); $this->Data[] = Array ( 'no' => $errno, 'str' => $errstr, 'file' => $errfile, 'line' => $errline, 'exception_class' => get_class($exception), 'debug_type' => 'exception' ); $this->ProfilerData['error_handling']['ends'] = memory_get_usage(); $this->profilerAddTotal('error_handling', 'error_handling'); } /** * User-defined exception handler * * @param Exception $exception * @return void * @access public */ public function saveException($exception) { $this->appendException($exception); $this->_fatalErrorHash = $this->_getErrorHash($exception->getFile(), $exception->getLine()); // Append debugger report to data in buffer & clean buffer afterwards. echo $this->_breakOutOfBuffering(false) . $this->printReport(true); } /** * Transforms short error messages into long ones * * @param string $errstr * @param string $errfile * @param int $errline * @return void * @access private */ private function expandError(&$errstr, &$errfile, &$errline) { $errstr = kLogger::expandMessage($errstr); list ($errno, $errstr, $sql) = kLogger::parseDatabaseError($errstr); if ( $errno != 0 ) { $errstr = '<span class="debug_error">' . $errstr . ' (' . $errno . ')</span><br/><strong>SQL</strong>: ' . $this->formatSQL($sql); } if ( strpos($errfile, 'eval()\'d code') !== false ) { $errstr = '[<b>EVAL</b>, line <b>' . $errline . '</b>]: ' . $errstr; $tmpStr = $errfile; $pos = strpos($tmpStr, '('); $errfile = substr($tmpStr, 0, $pos); $pos++; $errline = substr($tmpStr, $pos, strpos($tmpStr, ')', $pos) - $pos); } } /** * Break buffering in case if fatal error is happened in the middle * * @param bool $flush * @return string * @access private */ private function _breakOutOfBuffering($flush = true) { $buffer_content = Array (); while ( ob_get_level() ) { $buffer_content[] = ob_get_clean(); } $ret = implode('', array_reverse($buffer_content)); if ( $flush ) { echo $ret; flush(); } return $ret; } /** * Saves given message to "vb_debug.txt" file in DocumentRoot * * @param string $msg * @return void * @access public */ public function saveToFile($msg) { $fp = fopen($_SERVER['DOCUMENT_ROOT'] . '/vb_debug.txt', 'a'); fwrite($fp, $msg . "\n"); fclose($fp); } /** * Prints given constant values in a table * * @param mixed $constants * @return void * @access public */ public function printConstants($constants) { if ( !is_array($constants) ) { $constants = explode(',', $constants); } $constant_tpl = '<tr><td>%s</td><td><b>%s</b></td></tr>'; $ret = '<table class="dbg_flat_table" style="width: ' . $this->getWindowWidth() . 'px;">'; foreach ($constants as $constant_name) { $ret .= sprintf($constant_tpl, $constant_name, constant($constant_name)); } $ret .= '</table>'; $this->appendHTML($ret); } /** * Attaches debugger to Application * * @return void * @access public */ public function AttachToApplication() { if ( !DebuggerUtil::constOn('DBG_HANDLE_ERRORS') ) { return; } if ( class_exists('kApplication') ) { $this->Application =& kApplication::Instance(); $this->Application->Debugger = $this; } // kLogger will auto-detect these automatically // error/exception handlers registered before debugger will be removed! set_error_handler( Array ($this, 'saveError') ); set_exception_handler( Array ($this, 'saveException') ); } /** * Returns HTML for tools section * * @return string * @access private */ private function _getToolsHTML() { $html = '<table> <tr> <td>System Tools:</td> <td> <select id="reset_cache" style="border: 1px solid #000000;"> <option value=""></option> <option value="events[adm][OnResetModRwCache]">Reset mod_rewrite Cache</option> <option value="events[adm][OnResetCMSMenuCache]">Reset SMS Menu Cache</option> <option value="events[adm][OnResetSections]">Reset Sections Cache</option> <option value="events[adm][OnResetConfigsCache]">Reset Configs Cache</option> <option value="events[adm][OnRebuildThemes]">Re-build Themes Files</option> <option value="events[lang][OnReflectMultiLingualFields]">Re-build Multilanguage Fields</option> <option value="events[adm][OnDeleteCompiledTemplates]">Delete Compiled Templates</option> </select> </td> <td> <input type="button" class="button" onclick="$Debugger.resetCache(\'reset_cache\');" value="Go"/> </td> </tr> </table>'; return $html; } /** * Returns HTML for dom viewer section * * @return string * @access private */ private function _getDomViewerHTML() { $html = '<table> <tr> <td> <a href="http://www.brainjar.com/dhtml/domviewer/" target="_blank">DomViewer</a>: </td> <td> <input id="dbg_domviewer" type="text" value="window" style="border: 1px solid #000000;"/> </td> <td> <button class="button" onclick="return $Debugger.OpenDOMViewer();">Show</button> </td> </tr> </table>'; return $html; } } if ( !function_exists('memory_get_usage') ) { // PHP 4.x and compiled without --enable-memory-limit option function memory_get_usage() { return -1; } } if ( !DebuggerUtil::constOn('DBG_ZEND_PRESENT') ) { $debugger = new Debugger(); } if ( DebuggerUtil::constOn('DBG_USE_SHUTDOWN_FUNC') ) { register_shutdown_function(array(&$debugger, 'printReport'), false, true, true); } } Index: branches/5.3.x/core/kernel/utility/validator.php =================================================================== --- branches/5.3.x/core/kernel/utility/validator.php (revision 16394) +++ branches/5.3.x/core/kernel/utility/validator.php (revision 16395) @@ -1,525 +1,527 @@ <?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 kValidator extends kBase { /** * Form name, used for validation * * @var string */ protected $lastFormName = false; /** * Prefix & special of last data source * * @var string */ protected $lastPrefixSpecial = ''; /** * Object, that holds a data to be validated * * @var kDBItem */ protected $dataSource = null; /** * Holds field errors * * @var Array * @access protected */ protected $FieldErrors = Array (); /** * Phrases for each error pseudo * * @var Array * @access protected */ protected $ErrorMsgs = Array ( 'required' => '!la_err_required!', // Field is required 'unique' => '!la_err_unique!', // Field value must be unique 'value_out_of_range' => '!la_err_value_out_of_range!', // Field is out of range, possible values from %s to %s 'length_out_of_range' => '!la_err_length_out_of_range!', // Field is out of range 'bad_type' => '!la_err_bad_type!', // Incorrect data format, please use %s 'invalid_format' => '!la_err_invalid_format!', // Incorrect data format, please use %s 'bad_date_format' => '!la_err_bad_date_format!', // Incorrect date format, please use (%s) ex. (%s) 'primary_lang_required' => '!la_err_primary_lang_required!', // Primary Lang. value Required ); /** * Sets data source for validation * * @param kDBItem $object */ public function setDataSource(&$object) { if ( $object->getFormName() === $this->lastFormName && $object->getPrefixSpecial() === $this->lastPrefixSpecial ) { return ; } $this->reset(); $this->dataSource =& $object; $this->lastFormName = $object->getFormName(); $this->lastPrefixSpecial = $object->getPrefixSpecial(); } /** * Validate all item fields based on * constraints set in each field options * in config * * @return bool * @access private */ public function Validate() { // order is critical - should be called BEFORE checking errors $this->dataSource->UpdateFormattersMasterFields(); $global_res = true; $fields = array_keys( $this->dataSource->getFields() ); foreach ($fields as $field) { // call separately, otherwise 2+ validation errors will be ignored $res = $this->ValidateField($field); $global_res = $global_res && $res; } if ( !$global_res && $this->Application->isDebugMode() ) { $title_info = $this->dataSource->GetTitleField(); $item_info = Array ( $this->dataSource->IDField . ': <strong>' . $this->dataSource->GetID() . '</strong>', ); if ( $title_info && reset($title_info) ) { $item_info[] = key($title_info) . ': <strong>' . current($title_info) . '</strong>'; } $error_msg = ' Validation failed in prefix - <strong>' . $this->dataSource->Prefix . '</strong> (' . implode('; ', $item_info) . '), FieldErrors follow (look at items with <strong>"pseudo"</strong> key set)<br /> You may ignore this notice if submitted data really has a validation error'; trigger_error(trim($error_msg), E_USER_NOTICE); $this->Application->Debugger->dumpVars($this->FieldErrors); } return $global_res; } /** * Validates given field * * @param string $field * @return bool * @access public */ public function ValidateField($field) { $options = $this->dataSource->GetFieldOptions($field); $error_field = isset($options['error_field']) ? $options['error_field'] : $field; $res = $this->GetErrorPseudo($error_field) == ''; $res = $res && $this->ValidateRequired($field, $options); $res = $res && $this->ValidateType($field, $options); $res = $res && $this->ValidateRange($field, $options); $res = $res && $this->ValidateUnique($field, $options); $res = $res && $this->CustomValidation($field, $options); return $res; } /** * Check if value is set for required field * * @param string $field field name * @param Array $params field options from config * @return bool * @access public */ public function ValidateRequired($field, $params) { if ( !isset($params['required']) || !$params['required'] ) { return true; } $value = $this->dataSource->GetDBField($field); if ( $this->Application->ConfigValue('TrimRequiredFields') ) { $value = trim($value); } if ( (string)$value == '' ) { $this->SetError($field, 'required'); return false; } return true; } /** * Check if value in field matches field type specified in config * * @param string $field field name * @param Array $params field options from config * @return bool */ protected function ValidateType($field, $params) { $val = $this->dataSource->GetDBField($field); $type_regex = "#int|integer|double|float|real|numeric|string#"; if ( $val == '' || !isset($params['type']) || !preg_match($type_regex, $params['type']) ) { return true; } if ( $params['type'] == 'numeric' ) { trigger_error('Invalid field type <strong>' . $params['type'] . '</strong> (in ValidateType method), please use <strong>float</strong> instead', E_USER_NOTICE); $params['type'] = 'float'; } $res = is_numeric($val); if ( $params['type'] == 'string' || $res ) { $f = 'is_' . $params['type']; settype($val, $params['type']); $res = $f($val) && ($val == $this->dataSource->GetDBField($field)); } if ( !$res ) { $this->SetError($field, 'bad_type', null, array('type' => $params['type'])); return false; } return true; } /** * Check if field value is in range specified in config * * @param string $field field name * @param Array $params field options from config * @return bool * @access private */ protected function ValidateRange($field, $params) { $res = true; $val = $this->dataSource->GetDBField($field); if ( isset($params['type']) && preg_match("#int|integer|double|float|real#", $params['type']) && strlen($val) > 0 ) { // validate number if ( isset($params['max_value_inc'])) { $res = $res && $val <= $params['max_value_inc']; $max_val = $params['max_value_inc'].' (inclusive)'; } if ( isset($params['min_value_inc'])) { $res = $res && $val >= $params['min_value_inc']; $min_val = $params['min_value_inc'].' (inclusive)'; } if ( isset($params['max_value_exc'])) { $res = $res && $val < $params['max_value_exc']; $max_val = $params['max_value_exc'].' (exclusive)'; } if ( isset($params['min_value_exc'])) { $res = $res && $val > $params['min_value_exc']; $min_val = $params['min_value_exc'].' (exclusive)'; } } if ( !$res ) { if ( !isset($min_val) ) $min_val = '-∞'; if ( !isset($max_val) ) $max_val = '∞'; $this->SetError($field, 'value_out_of_range', null, array( 'min_value' => $min_val, 'max_value' => $max_val )); return false; } - // validate string - if ( isset($params['max_len']) ) { - $res = $res && mb_strlen($val) <= $params['max_len']; - } + if ( strlen($val) > 0 ) { + // Validate string. + if ( isset($params['max_len']) ) { + $res = $res && mb_strlen($val) <= $params['max_len']; + } - if ( isset($params['min_len']) ) { - $res = $res && mb_strlen($val) >= $params['min_len']; + if ( isset($params['min_len']) ) { + $res = $res && mb_strlen($val) >= $params['min_len']; + } } if ( !$res ) { $error_params = array( 'min_length' => (int)getArrayValue($params, 'min_len'), 'max_length' => (int)getArrayValue($params, 'max_len'), 'value' => mb_strlen($val) ); $this->SetError($field, 'length_out_of_range', null, $error_params); return false; } return true; } /** * Validates that current record has unique field combination among other table records * * @param string $field field name * @param Array $params field options from config * @return bool * @access private */ protected function ValidateUnique($field, $params) { $unique_fields = getArrayValue($params, 'unique'); if ( $unique_fields === false ) { return true; } $where = Array (); array_push($unique_fields, $field); foreach ($unique_fields as $unique_field) { // if field is not empty or if it is required - we add where condition $field_value = $this->dataSource->GetDBField($unique_field); if ( (string)$field_value != '' || $this->dataSource->isRequired($unique_field) ) { $where[] = '`' . $unique_field . '` = ' . $this->Conn->qstr($field_value); } else { // not good if we check by less fields than indicated return true; } } // This can ONLY happen if all unique fields are empty and not required. // In such case we return true, because if unique field is not required there may be numerous empty values // if (!$where) return true; $sql = 'SELECT COUNT(*) FROM %s WHERE (' . implode(') AND (', $where) . ') AND (' . $this->dataSource->IDField . ' <> ' . (int)$this->dataSource->GetID() . ')'; $res_temp = $this->Conn->GetOne( str_replace('%s', $this->dataSource->TableName, $sql) ); $current_table_only = getArrayValue($params, 'current_table_only'); // check unique record only in current table $res_live = $current_table_only ? 0 : $this->Conn->GetOne( str_replace('%s', $this->Application->GetLiveName($this->dataSource->TableName), $sql) ); $res = ($res_temp == 0) && ($res_live == 0); if ( !$res ) { $this->SetError($field, 'unique'); return false; } return true; } /** * Check field value by user-defined alghoritm * * @param string $field field name * @param Array $params field options from config * @return bool */ protected function CustomValidation($field, $params) { return true; } /** * 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) { $error_field = $this->dataSource->GetFieldOption($field, 'error_field', false, $field); if ( $this->GetErrorPseudo($error_field) ) { // don't set more then one error on field return false; } $this->FieldErrors[$error_field]['pseudo'] = $pseudo; if ( isset($error_params) ) { if ( array_key_exists('value', $error_params) ) { $this->FieldErrors[$error_field]['value'] = $error_params['value']; unset($error_params['value']); } // additional params, that helps to determine error sources $this->FieldErrors[$error_field]['params'] = $error_params; } if ( isset($error_label) && !isset($this->ErrorMsgs[$pseudo]) ) { // label for error (only when not already set) $this->ErrorMsgs[$pseudo] = (substr($error_label, 0, 1) == '+') ? substr($error_label, 1) : '!'.$error_label.'!'; } return true; } /** * Return error message for field * * @param string $field * @param bool $force_escape * @return string * @access public */ public function GetErrorMsg($field, $force_escape = null) { $error_pseudo = $this->GetErrorPseudo($field); if ( !$error_pseudo ) { return ''; } // if special error msg defined in config $error_msgs = $this->dataSource->GetFieldOption($field, 'error_msgs', false, Array ()); if ( isset($error_msgs[$error_pseudo]) ) { $msg = $error_msgs[$error_pseudo]; } else { // fallback to defaults if ( !isset($this->ErrorMsgs[$error_pseudo]) ) { trigger_error('No user message is defined for pseudo error <strong>' . $error_pseudo . '</strong>', E_USER_WARNING); return $error_pseudo; //return the pseudo itself } $msg = $this->ErrorMsgs[$error_pseudo]; } $msg = $this->Application->ReplaceLanguageTags($msg, $force_escape); if ( isset($this->FieldErrors[$field]['params']) ) { $params = $this->FieldErrors[$field]['params']; } else { $params = array(); } $field_phrase = $this->Application->isAdmin ? 'la_fld_' . $field : 'lu_fld_' . $field; $params['field'] = $this->Application->Phrase($field_phrase); foreach ( $params as $param_name => $param_value ) { $msg = str_replace('{' . $param_name . '}', $param_value, $msg, $replacement_count); } if ( strpos($msg, '%s') !== false ) { trigger_error('Unexpected "%s" in field "<b>' . $field . '</b>" validation error message (pseudo: "<b>' . $error_pseudo . '</b>") in "<b>' . $this->dataSource->Prefix . '</b>" unit', E_USER_WARNING); } return $msg; } /** * Returns error pseudo * * @param string $field * @return string * @access public * */ 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] ); } /** * Returns field errors * * @return Array * @access public */ public function GetFieldErrors() { return $this->FieldErrors; } /** * Check if item has errors * * @param Array $skip_fields fields to skip during error checking * @return bool * @access public */ public function HasErrors( $skip_fields = Array () ) { $fields = array_keys( $this->dataSource->getFields() ); $fields = array_diff($fields, $skip_fields); foreach ($fields as $field) { // if Formatter has set some error messages during values parsing if ( $this->GetErrorPseudo($field) ) { return true; } } return false; } /** * Clears all validation errors * * @access public */ public function reset() { $this->FieldErrors = Array(); } } Index: branches/5.3.x/core/kernel/utility/unit_config_reader.php =================================================================== --- branches/5.3.x/core/kernel/utility/unit_config_reader.php (revision 16394) +++ branches/5.3.x/core/kernel/utility/unit_config_reader.php (revision 16395) @@ -1,750 +1,766 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class kUnitConfigReader extends kBase implements kiCacheable { /** * Unit config data storage. * * @var kUnitConfig[] */ protected $configData = array(); /** * List of discovered unit config files. * * @var array */ protected $configFiles = array(); /** * Mapping between unit config prefixes and files, where they data is stored. * * @var array */ protected $prefixFiles = array(); /** * Tells, that it's final stage of application initialization, where OnAfterConfigRead events can be called. * * @var boolean */ protected $finalStage = false; /** * Determines if cache should be stored. * * @var boolean */ protected $storeCache = false; /** * List of unit configs, that have called their OnAfterConfigRead event. * * @var array */ protected $afterConfigProcessed = array(); /** * Escaped directory separator for using in regular expressions * * @var string */ protected $directorySeparator = ''; /** * Regular expression for detecting module folder * * @var string */ protected $moduleFolderRegExp = ''; /** * Folders to skip during unit config search * * @var array */ protected $skipFolders = array('CVS', '.svn', '.git', 'admin_templates', 'libchart', 'install'); /** * Cloner. * * @var kUnitConfigCloner */ protected $cloner; /** * Creates instance of unit config reader. */ public function __construct() { parent::__construct(); $this->directorySeparator = preg_quote(DIRECTORY_SEPARATOR); $this->skipFolders[] = basename(EDITOR_PATH); $this->moduleFolderRegExp = '#' . $this->directorySeparator . '(core|modules' . $this->directorySeparator . '.*?)' . $this->directorySeparator . '#'; $this->cloner = $this->Application->makeClass('kUnitConfigCloner', array($this)); } /** * Sets data from cache to object * * @param Array $data */ public function setFromCache(&$data) { $this->cloner->setFromCache($data); $this->prefixFiles = $data['ConfigReader.prefixFiles']; } /** * Gets object data for caching * * @return Array */ public function getToCache() { return array_merge( $this->cloner->getToCache(), array( 'ConfigReader.prefixFiles' => $this->prefixFiles, ) ); } public function scanModules($folder_path, $cache = true) { if ( defined('IS_INSTALL') && IS_INSTALL && !defined('FORCE_CONFIG_CACHE') ) { // disable config caching during installation $cache = false; } if ( $cache ) { $restored = $this->Application->cacheManager->LoadUnitCache(); if ( $restored ) { if ( defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode() ) { $this->Application->Debugger->appendHTML('UnitConfigReader: Restoring Cache'); } return; } } if ( defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode() ) { $this->Application->Debugger->appendHTML('UnitConfigReader: Generating Cache'); } // === lines below would only executed on cold start (no unit config cache) === // no cache found -> include all unit configs to create it ! $this->includeConfigFiles($folder_path, $cache); $this->parseConfigs(); $this->sortRouters(); // tell AfterConfigRead to store cache if needed // can't store it here because AfterConfigRead needs ability to change config data $this->storeCache = $cache; if ( !$this->Application->InitDone ) { // scanModules is called multiple times during installation process $this->Application->InitManagers(); } $this->Application->cacheManager->applyDelayedUnitProcessing(); + + if ( !$this->Application->InitDone && $cache ) { + // Allow hooks to modify "m:QueryString" before URL parsing is started during cold start. + $this->runAfterConfigRead('m'); + } } /** * Locates (recursively) and reads all unit configs at given path. * * @param string $folder_path Folder path. * @param boolean $cache Store information to cache. * * @throws Exception When unit config file is missing a prefix defined inside it. */ protected function includeConfigFiles($folder_path, $cache = true) { + $data = false; $this->Application->refreshModuleInfo(); - if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) { - $data = $this->Application->getCache('master:config_files', false, $cache ? CacheSettings::$unitCacheRebuildTime : 0); - } - else { - $data = $this->Application->getDBCache('config_files', $cache ? CacheSettings::$unitCacheRebuildTime : 0); + if ( $cache ) { + if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) { + $data = $this->Application->getCache( + 'master:config_files', + false, + CacheSettings::$unitCacheRebuildTime + ); + } + else { + $data = $this->Application->getDBCache( + 'config_files', + CacheSettings::$unitCacheRebuildTime + ); + } } if ( $data ) { $this->configFiles = unserialize($data); if ( !(defined('DBG_VALIDATE_CONFIGS') && DBG_VALIDATE_CONFIGS) ) { shuffle($this->configFiles); } } else { /*if ( $this->Application->isDebugMode() ) { $this->Application->Debugger->profileStart('fcf'); }*/ $this->findConfigFiles(FULL_PATH . DIRECTORY_SEPARATOR . 'core'); // Search from core directory. $this->findConfigFiles($folder_path); // Search from modules directory. /*if ( $this->Application->isDebugMode() ) { $this->Application->Debugger->profileFinish( 'fcf', 'findConfigFiles [' . count($this->configFiles) . ']' ); }*/ if ( $cache ) { if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) { $this->Application->setCache('master:config_files', serialize($this->configFiles)); } else { $this->Application->setDBCache('config_files', serialize($this->configFiles)); } } } foreach ( $this->configFiles as $filename ) { $prefix = $this->PreloadConfigFile($filename); if ( !$prefix ) { throw new Exception('Prefix not defined in config file <strong>' . $filename . '</strong>'); } } // TODO: needed? if ( $cache ) { unset($this->configFiles); } } /** * Recursively searches for unit configs in given folder. * * @param string $folder_path Path to the folder. * @param integer $level Deep level of the folder. * * @return void */ protected function findConfigFiles($folder_path, $level = 0) { // If FULL_PATH = "/" ensure, that all "/" in $folder_path are not deleted. $reg_exp = '/^' . preg_quote(FULL_PATH, '/') . '/'; // This make sense, since $folder_path may NOT contain FULL_PATH. $folder_path = preg_replace($reg_exp, '', $folder_path, 1); $base_folder = FULL_PATH . $folder_path . DIRECTORY_SEPARATOR; $sub_folders = glob($base_folder . '*', GLOB_ONLYDIR); if ( !$sub_folders ) { return; } foreach ( $sub_folders as $full_path ) { $sub_folder = substr($full_path, strlen($base_folder)); if ( in_array($sub_folder, $this->skipFolders) || substr($sub_folder, 0, 1) == '.' ) { // Don't scan skipped or hidden folders. continue; } $config_name = $this->getConfigName($folder_path . DIRECTORY_SEPARATOR . $sub_folder); if ( file_exists(FULL_PATH . $config_name) ) { $this->configFiles[] = $config_name; } $this->findConfigFiles($full_path, $level + 1); } } /** * Process all read config files - called ONLY when there is no cache! * * @return void */ protected function parseConfigs() { $this->parseUnitConfigs($this->getUnitConfigsWithoutPriority()); $this->parseUnitConfigs($this->getUnitConfigsWithPriority()); } /** * Parses unit config sub-set. * * @param array $prefixes Unit config prefixes. * * @return array */ protected function parseUnitConfigs(array $prefixes) { foreach ( $prefixes as $prefix ) { $this->configData[$prefix]->parse(); } $this->cloner->extrudeAndParse($prefixes); } /** * Returns unit configs prefixes without priority defined. * * @return array */ public function getUnitConfigsWithoutPriority() { $ret = array(); foreach ( $this->configData as $prefix => $config ) { if ( $config->getConfigPriority() === false ) { $ret[] = $prefix; } } return $ret; } /** * Returns unit configs prefixes with priority defined. * * @return array */ public function getUnitConfigsWithPriority() { $ret = array(); foreach ( $this->configData as $prefix => $config ) { $priority = $config->getConfigPriority(); if ( $priority !== false ) { $ret[$prefix] = $priority; } } asort($ret); return array_keys($ret); } public function AfterConfigRead($store_cache = null) { $this->finalStage = true; foreach ($this->configData as $prefix => $config) { $this->runAfterConfigRead($prefix); } if ( !isset($store_cache) ) { // $store_cache not overridden -> use global setting $store_cache = $this->storeCache; } if ( $store_cache || (defined('IS_INSTALL') && IS_INSTALL) ) { // cache is not stored during install, but dynamic clones should be processed in any case $this->cloner->processDynamicallyAdded(); $this->retrieveCollections(); } if ( $store_cache ) { $this->Application->HandleEvent(new kEvent('adm:OnAfterCacheRebuild')); $this->Application->cacheManager->UpdateUnitCache(); if ( defined('DEBUG_MODE') && DEBUG_MODE && defined('DBG_VALIDATE_CONFIGS') && DBG_VALIDATE_CONFIGS ) { // validate configs here to have changes from OnAfterConfigRead hooks to prefixes foreach ( $this->configData as $config ) { if ( !$config->getTableName() ) { continue; } $config->validate(); } } } } /** * Sort routers according to their weight (non-prioritized routers goes first). * * @return void */ protected function sortRouters() { $sorted_routers = array(); $prioritized_routers = array(); $routers = $this->collectRouters(); // Process non-prioritized routers. foreach ( $routers as $prefix => $router_data ) { if ( $router_data['priority'] === false ) { $sorted_routers[$prefix] = $router_data; } else { $prioritized_routers[$prefix] = $router_data['priority']; } } // Process prioritized routers. asort($prioritized_routers, SORT_NUMERIC); foreach ( $prioritized_routers as $prefix => $priority ) { $sorted_routers[$prefix] = $routers[$prefix]; } $this->Application->routers = $sorted_routers; } /** * Collects routers. * * @return array */ protected function collectRouters() { $routers = array(); $router_classes = $this->Application->getSubClasses('AbstractRouter'); foreach ( $router_classes as $router_class ) { if ( !class_exists($router_class) ) { // This can happen, when: // - router class (coming from cache) was renamed; // - new cache is built based on outdated class map. continue; } /** @var AbstractRouter $router */ $router = new $router_class(); $routers[$router->getPrefix()] = array( 'class' => $router_class, 'priority' => $router->getWeight(), ); } return $routers; } /** * Re-reads all configs. * * @return void */ public function ReReadConfigs() { if ( $this->storeCache && $this->finalStage ) { // Building cache right now, so all unit configs are read anyway. return; } // don't reset prefix file, since file scanning could slow down the process $prefix_files_backup = $this->prefixFiles; $this->Application->cacheManager->EmptyUnitCache(); $this->prefixFiles = $prefix_files_backup; // parse all configs $this->afterConfigProcessed = array(); $this->includeConfigFiles(MODULES_PATH, false); $this->parseConfigs(); $this->sortRouters(); $this->AfterConfigRead(false); $this->cloner->processDynamicallyAdded(); $this->retrieveCollections(); } /** * Process all collectible unit config options here to also catch ones, defined from OnAfterConfigRead events * */ protected function retrieveCollections() { foreach ( $this->configData as $prefix => $config ) { // collect replacement templates if ( $config->getReplacementTemplates() ) { $this->Application->ReplacementTemplates = array_merge($this->Application->ReplacementTemplates, $config->getReplacementTemplates()); } } } public function loadConfig($prefix) { $preloaded_prefix = $this->PreloadConfigFile($this->getPrefixFile($prefix)); - if ( $this->finalStage ) { - // run prefix OnAfterConfigRead so all hooks to it can define their clones + if ( $this->finalStage || $prefix == 'm' ) { + // Run prefix OnAfterConfigRead so all hooks to it can define their clones. + // Allow hooks to modify "m:QueryString" before URL parsing is started during warm start. $this->runAfterConfigRead($preloaded_prefix); } // Only use cached clones for calls in the middle of initialization (e.g. url parsing). $clones = $this->cloner->extrude($preloaded_prefix, !$this->finalStage); if ( $this->finalStage ) { foreach ( $clones as $a_prefix ) { $this->runAfterConfigRead($a_prefix); } } } /** * Runs OnAfterConfigRead event for given prefix once. * * @param string $prefix Unit config prefix. * * @return void */ public function runAfterConfigRead($prefix) { if ( in_array($prefix, $this->afterConfigProcessed) ) { return; } $this->Application->HandleEvent(new kEvent($prefix . ':OnAfterConfigRead')); if ( !(defined('IS_INSTALL') && IS_INSTALL) ) { // allow to call OnAfterConfigRead multiple times during install array_push($this->afterConfigProcessed, $prefix); } } /** * Loads unit config file contents from disk. * * @param string $filename Unit config filename. * * @return string */ protected function PreloadConfigFile($filename) { $config_found = file_exists(FULL_PATH . $filename) && $this->configAllowed($filename); if ( defined('DEBUG_MODE') && DEBUG_MODE && defined('DBG_PROFILE_INCLUDES') && DBG_PROFILE_INCLUDES ) { if ( in_array($filename, get_included_files()) ) { return ''; } global $debugger; if ( $config_found ) { $file = FULL_PATH . $filename; $file_crc = crc32($file); $debugger->ProfileStart('inc_' . $file_crc, $file); include_once($file); $debugger->ProfileFinish('inc_' . $file_crc); $debugger->profilerAddTotal('includes', 'inc_' . $file_crc); } } elseif ( $config_found ) { include_once(FULL_PATH . $filename); } if ( $config_found ) { /* @var $config kUnitConfig|Array */ if ( isset($config) && $config ) { // config file is included for 1st time -> save it's content for future processing if ( !is_object($config) ) { $prefix = array_key_exists('Prefix', $config) ? $config['Prefix'] : ''; $config = new kUnitConfig($prefix, $config); } else { $prefix = $config->getPrefix(); } preg_match($this->moduleFolderRegExp, $filename, $regs); $config->setModuleFolder(str_replace(DIRECTORY_SEPARATOR, '/', $regs[1])); $config->setBasePath(dirname(FULL_PATH . $filename)); if ( $config->getAdminTemplatePath() !== false ) { // append template base folder for admin templates path of this prefix $module_templates = $regs[1] == 'core' ? '' : substr($regs[1], 8) . '/'; $config->setAdminTemplatePath($module_templates . $config->getAdminTemplatePath()); } if ( array_key_exists($prefix, $this->prefixFiles) && ($this->prefixFiles[$prefix] != $filename) ) { trigger_error( 'Single unit config prefix "<strong>' . $prefix . '</strong>" ' . 'is used in multiple unit config files: ' . '"<strong>' . $this->prefixFiles[$prefix] . '</strong>", "<strong>' . $filename . '</strong>"', E_USER_WARNING ); } $this->add($config, $filename); return $prefix; } else { $prefix = array_search($filename, $this->prefixFiles); if ( $prefix ) { // attempt is made to include config file twice or more, but include_once prevents that, // but file exists on hdd, then it is already saved to all required arrays, just return it's prefix return $prefix; } } } return 'dummy'; } /** * Sets a file, for a given prefix. * * @param kUnitConfig $config Unit config. * @param string $filename File. * * @return void */ public function add(kUnitConfig $config, $filename) { $config->setFilename($filename); $prefix = $config->getPrefix(); $this->configData[$prefix] = $config; $this->prefixFiles[$prefix] = $filename; } /** * Removes unit config. * * @param string $prefix Unit config prefix. * * @return void */ public function remove($prefix) { unset($this->configData[$prefix], $this->prefixFiles[$prefix]); } /** * Returns unit config for given prefix * * @param string $prefix * @return kUnitConfig */ public function getUnitConfig($prefix = null) { if ( !isset($this->configData[$prefix]) ) { $this->loadConfig($prefix); } return $this->configData[$prefix]; } /** * Returns prefixes of unit configs, that were registered. * * @param boolean $loaded_only Only return prefixes, that were loaded till now. * * @return array */ public function getPrefixes($loaded_only = true) { if ( $loaded_only ) { return array_keys($this->configData); } return array_keys($this->prefixFiles); } /** * Get's config file name based * on folder name supplied * * @param string $folder_path * @return string */ protected function getConfigName($folder_path) { return $folder_path . DIRECTORY_SEPARATOR . basename($folder_path) . '_config.php'; } /** * Checks if config file is allowed for inclusion (if module of config is installed). * * @param string $config_path Relative path from In-Portal directory. * * @return boolean */ protected function configAllowed($config_path) { static $module_paths = null; if ( defined('IS_INSTALL') && IS_INSTALL ) { // at installation start no modules in db and kernel configs could not be read return true; } if ( preg_match('#^' . $this->directorySeparator . 'core#', $config_path) ) { // always allow to include configs from "core" module's folder return true; } if ( !$this->Application->ModuleInfo ) { return false; } if ( !isset($module_paths) ) { $module_paths = array(); foreach ( $this->Application->ModuleInfo as $module_info ) { $module_paths[] = str_replace('/', DIRECTORY_SEPARATOR, rtrim($module_info['Path'], '/')); } $module_paths = array_unique($module_paths); } preg_match($this->moduleFolderRegExp, $config_path, $regs); // config file path starts with module folder path return in_array($regs[1], $module_paths); } /** * Returns true if config exists and is allowed for reading * * @param string $prefix Unit config prefix. * * @return boolean */ public function prefixRegistered($prefix) { return isset($this->prefixFiles[$prefix]); } /** * Returns unit config file location by it's prefix. * * @param string $prefix Unit config prefix. * * @return string * @throws InvalidArgumentException When unit config is not found. */ public function getPrefixFile($prefix) { if ( !isset($this->prefixFiles[$prefix]) ) { throw new InvalidArgumentException( 'Configuration file for prefix "<strong>' . $prefix . '</strong>" is unknown' ); } return $this->prefixFiles[$prefix]; } } Index: branches/5.3.x/core/kernel/utility/email.php =================================================================== --- branches/5.3.x/core/kernel/utility/email.php (revision 16394) +++ branches/5.3.x/core/kernel/utility/email.php (revision 16395) @@ -1,925 +1,963 @@ <?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 kEmail extends kBase { /** * Reference to class, that could send out e-mail * * @var kEmailSendingHelper * @access protected */ protected $sender = null; /** * Parameters of e-mail * * @var Array * @access protected * @see kEmail::_getCustomParams() List of possible system parameters supported */ protected $params = Array (); /** * Reference to e-mail template object, that would be used as data source * * @var kDBItem */ protected $emailTemplate = null; /** * Sender name * * @var string * @access protected */ protected $fromName = ''; /** * Sender e-mail * * @var string * @access protected */ protected $fromEmail = ''; /** * Recipient name * * @var string * @access protected */ protected $toName = ''; /** * Recipient e-mail * * @var string * @access protected */ protected $toEmail = ''; /** * ID of recipient user * * @var int */ protected $recipientUserId = null; /** * List of e-mail recipients * * @var Array * @access protected */ protected $recipients = Array ( EmailTemplate::RECIPIENT_TYPE_TO => 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 "<strong>' . $name . '</strong>". Only <strong>UPPERCASE characters</strong> and <strong>dots</strong> 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); // 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() ) { // 1. prepare log $this->logData = Array ( 'From' => $this->fromName . ' (' . $this->fromEmail . ')', 'To' => $this->toName . ' (' . $this->toEmail . ')', 'OtherRecipients' => serialize($this->recipients), 'Subject' => $message_subject, '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(); } // 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', - ); + $header_mapping = $this->getHeaderMapping(); $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(); + $this->_moveDirectRecipients(); 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); } } /** + * Move recipients, that were added manually via "$this->sender->Add*" methods. + * + * @return void + * @access protected + */ + protected function _moveDirectRecipients() + { + foreach ( $this->getHeaderMapping() as $recipient_type => $header_name ) { + $manual_recipients = $this->sender->GetRecipientsByHeader($header_name); + + if ( !$manual_recipients ) { + continue; + } + + foreach ( $manual_recipients as $manual_recipient ) { + $this->recipients[$recipient_type][] = array( + 'RecipientName' => $manual_recipient['Name'], + 'RecipientAddressType' => EmailTemplate::ADDRESS_TYPE_EMAIL, + 'RecipientAddress' => $manual_recipient['Email'], + ); + } + + $this->sender->SetHeader($header_name, ''); + } + } + + /** + * Returns mapping between recipient type and header name. + * + * @return array + */ + protected function getHeaderMapping() + { + return array( + EmailTemplate::RECIPIENT_TYPE_TO => 'To', + EmailTemplate::RECIPIENT_TYPE_CC => 'Cc', + EmailTemplate::RECIPIENT_TYPE_BCC => 'Bcc', + ); + } + + /** * 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); $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 ( '<inp:touser _Field="password"' => '<inp2:u_Field name="Password_plain"', '<inp:touser _Field="UserName"' => '<inp2:u_Field name="Username"', '<inp:touser _Field' => '<inp2:u_Field name', ); $replacement_tags = $this->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 16394) +++ branches/5.3.x/core/kernel/globals.php (revision 16395) @@ -1,1071 +1,1076 @@ <?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 kUtil { // const KG_TO_POUND = 2.20462262; const POUND_TO_KG = 0.45359237; /** * Escape text as HTML. * * @see escape */ const ESCAPE_HTML = 'html'; /** * Escape text as JavaScript. * * @see escape */ const ESCAPE_JS = 'js'; /** * Escape text as Url. */ const ESCAPE_URL = 'url'; /** * Don't escape anything. */ const ESCAPE_RAW = 'raw'; /** * Current escape strategy. * * @var string * @see escape */ public static $escapeStrategy = self::ESCAPE_HTML; /** * Checks, that given array is associative. * * @param array $array Array. * * @return bool * @access public */ public static function isAssoc($array) { return array_keys($array) !== range(0, count($array) - 1); } /** * Similar to array_merge_recursive but keyed-valued are always overwritten. * Priority goes to the 2nd array. * - * @param $paArray1 array - * @param $paArray2 array + * @param mixed $array1 Array 1. + * @param mixed $array2 Array 2. + * * @return array - * @access public */ - public static function array_merge_recursive($paArray1, $paArray2) + public static function array_merge_recursive($array1, $array2) { - if (!is_array($paArray1) or !is_array($paArray2)) { - return $paArray2; + if ( !is_array($array1) || !is_array($array2) ) { + return $array2; } - foreach ($paArray2 AS $sKey2 => $sValue2) { - $paArray1[$sKey2] = isset($paArray1[$sKey2]) ? self::array_merge_recursive($paArray1[$sKey2], $sValue2) : $sValue2; + foreach ( $array2 as $array2_key => $array2_value ) { + if ( isset($array1[$array2_key]) ) { + $array1[$array2_key] = self::array_merge_recursive($array1[$array2_key], $array2_value); + } + else { + $array1[$array2_key] = $array2_value; + } } - return $paArray1; + return $array1; } /** * 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('<strong>' . $label . '</strong>'); } $application->Debugger->dumpVars($data); } else { if ( $label ) { echo '<strong>' . $label . '</strong><br/>'; } echo '<pre>', print_r($data, true), '</pre>'; } } /** * 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") { ob_start(); $fp = fopen('php://output', 'w'); fputcsv($fp, $data, $delimiter, $enclosure); fclose($fp); $ret = ob_get_clean(); if ( $recordSeparator != "\n" ) { return substr($ret, 0, -1) . $recordSeparator; } 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('</script>', "</'+'script>", $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("</'+'script>", '</script>', $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; } } Index: branches/5.3.x/core/kernel/nparser/nparser.php =================================================================== --- branches/5.3.x/core/kernel/nparser/nparser.php (revision 16394) +++ branches/5.3.x/core/kernel/nparser/nparser.php (revision 16395) @@ -1,1188 +1,1232 @@ <?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!'); include_once(KERNEL_PATH.'/nparser/ntags.php'); define('TAG_NAMESPACE', 'inp2:'); define('TAG_NAMESPACE_LENGTH', 5); class NParser extends kBase { var $Stack = Array (); var $Level = 0; var $Buffers = array(); var $InsideComment = false; /** * Parse tags inside HTML comments * * @var bool */ var $SkipComments = true; var $Params = array(); var $ParamsStack = array(); var $ParamsLevel = 0; var $Definitions = ''; /** * Holds dynamic elements to function names mapping during execution * * @var Array */ var $Elements = Array (); /** * Holds location of element definitions inside templates. * key - element function name, value - array of 2 keys: {from_pos, to_pos} * * @var Array */ var $ElementLocations = Array (); var $DataExists = false; var $TemplateName = null; var $TempalteFullPath = null; var $CachePointers = Array (); var $Cachable = Array (); /** * Deep level during parsing * * @var int */ var $CacheLevel = 0; /** * Caching in templates enabled * * @var bool */ var $CachingEnabled = false; /** * Completely cache given page * * @var bool */ var $FullCachePage = false; /** * Prefixes, that are used on current page * * @var Array */ var $PrefixesInUse = Array (); /** * Parser parameter names, that are created via m_Capture tag are listed here * * @var Array */ var $Captures = array(); /** * Phrases, used on "Edit" buttons, that parser adds during block decoration * * @var Array */ var $_btnPhrases = Array (); /** * Mod-rewrite system enabled * * @var bool */ var $RewriteUrls = false; /** * Current user is logged-in * * @var bool */ var $UserLoggedIn = false; /** * Creates template parser object * * @access public */ public function __construct() { parent::__construct(); if (defined('EDITING_MODE') && (EDITING_MODE == EDITING_MODE_DESIGN)) { $this->_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 Clear() + { + // Discard any half-parsed content (e.g. from nested RenderElements). + $keep_buffering_levels = kUtil::constOn('SKIP_OUT_COMPRESSION') ? 1 : 2; + + while ( ob_get_level() > $keep_buffering_levels ) { + ob_end_clean(); + } + + $this->Stack = array(); + $this->Level = 0; + + $this->Buffers = array(); + $this->InsideComment = false; + + $this->SkipComments = true; + + $this->Params = array(); + $this->ParamsStack = array(); + $this->ParamsLevel = 0; + + $this->Definitions = ''; + + $this->Elements = array(); + + $this->ElementLocations = array(); + + $this->DataExists = false; + + $this->TemplateName = null; + $this->TempalteFullPath = null; + + $this->CachePointers = array(); + $this->Cachable = array(); + + $this->CacheLevel = 0; + + $this->FullCachePage = false; + + $this->PrefixesInUse = array(); + + $this->Captures = array(); + } + 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 "<strong>' . $template_name . '</strong>" 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] = '<?'."php $code ?>\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], '<!--') != 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] .= '<?'.'php '."\n\$_parser->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})(.*?)(?<!\\\)\\2/s', $params_str, $rets, PREG_SET_ORDER); $values = Array(); // we need to replace all occurences of any current param $key with {$key} for correct variable substitution foreach ($rets AS $key => $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'] == '</' || $tag['closing'] == '/>'; 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 <empty/> 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) . ' - <strong>probably opening tag was removed or nested tags error</strong>'; 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) . ' - <strong>probably missing <empty <span style="color: red">/</span>> tag closing</strong>'; 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 .= '<?'.'php ' . $compiled . " ?>\n"; // $o .= '<?'.'php '; // $o .= (isset($tag['NP']['cachable']) && (!$tag['NP']['cachable'] || $tag['NP']['cachable'] == 'false')) ? $this->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 "<b>{$tag['prefix']}_{$tag['name']}".($with_params ? ' '.$tag['attrs'] : '')."</b>"; } 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) . ' - <strong>incorrect tag name or prefix</strong>'; 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 "<strong>' . $t . '</strong>" - 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 "<strong>' . $page->GetOriginalField('PageCacheKey') . '</strong>" to "<strong>' . $page_cache_key . '</strong>".'); } $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); } } /** * 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 = '<strong>Rendering of undefined element ' . $params['name'] . '</strong>'; 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 = ' <div id="' . $container_id . '" params="' . $param_string . '" class="' . $btn_container_class . '" title="' . kUtil::escape($block_title, kUtil::ESCAPE_HTML) . '"> <button style="background-image: url(' . $icon_url . ');" class="cms-btn-new ' . $btn_class . '" id="' . $container_id . '_btn">' . $btn_text . '</button> <div class="cms-btn-content"> %s </div> </div>'; // 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[5] . '>' . $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:<prefixA>[,<prefixB>,<prefixC>] - include global serial for given prefix(-es) * skip_prefix:<prefix1>[,<prefix2>,<prefix3>] - exclude global serial for given prefix(-es) * prefix_id:<prefixA>[,<prefixB>,<prefixC>] - include id-based serial for given prefix(-es) * skip_prefix_id:<prefix1>[,<prefix2>,<prefix3>] - exclude id-based serial for given prefix(-es) * var:<aaa>[,<bbb>,<ccc>] - include request variable value(-s) * skip_var:<varA>[,<varB>,<varC>] - 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=<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 "<strong>' . $key . '</strong>" used in "<strong>key</strong>" parameter of <inp2:m_Cache key="..."/> 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 ? '<!-- CACHED OUTPUT START -->' . $ret . '<!-- /CACHED OUTPUT END -->' : $ret; $this->PopPointer(); return true; } if ($debug_mode) { echo '<!-- NO CACHE FOR POINTER: ' . $pointer . ' -->'; } } 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 '<!-- STORING CACHE FOR POINTER: ' . $pointer . ' [' . $res . '] -->'; } } 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/ntags.php =================================================================== --- branches/5.3.x/core/kernel/nparser/ntags.php (revision 16394) +++ branches/5.3.x/core/kernel/nparser/ntags.php (revision 16395) @@ -1,721 +1,721 @@ <?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 _BlockTag extends kBase { /** * Enter description here... * * @var NParser */ var $Parser = null; var $Tag = null; /** * Contains parameter names, that should be given to tag in any case * * @var Array */ var $_requiredParams = Array (); /** * Creates new tag * * @param Array $tag * @access public */ public function __construct($tag) { parent::__construct(); $this->Tag = $tag; } function Open($tag) { if (!$this->_checkRequiredParams($tag)) { return false; } return ''; } /** * Checks, that all required attributes for tag are passed * * @param Array $tag * @return bool */ function _checkRequiredParams($tag) { $missing_params = array_diff($this->_requiredParams, array_keys($tag['NP'])); if (!$missing_params) { return true; } $error_msg = 'Tag ' . $this->Parser->TagInfo($tag, true) . ' called <strong>without required ' . implode(', ', $missing_params) . '</strong> attribute'; if (count($missing_params) > 1) { $error_msg .= '(-s)'; } throw new ParserException($error_msg, 0, null, $tag); } /** * All tags inside block tag are passed through to this method * Any returned result is appened to current level's buffer * This can be used to implement special handling of such tags as <inp2:m_else/> * * @param unknown_type $tag * @return unknown */ function PassThrough(&$tag) { return ''; } function Close($tag) { return $this->Parser->Buffers[$this->Parser->Level]; } function AppendCode(&$o, $code, $php_tags=true) { if ($php_tags) { $o .= '<?'."php\n" . ( is_array($code) ? "\t".implode("\n\t", $code)."\n" : $code) .'?>'; } else { $o .= ( is_array($code) ? "\t".implode("\n\t", $code)."\n" : $code); } } } class _Tag_Comment extends _BlockTag { function Open($tag) { $this->Parser->SkipComments = false; return ''; } function Close($tag) { $this->Parser->SkipComments = true; return $this->Parser->Buffers[$this->Parser->Level]; } } class _Tag_DefineElement extends _BlockTag { /*var $ElemName; function Open($tag) { $o = ''; $pointer = abs(crc32($tag['file'])).'_'.$tag['line']; $f_name = $tag['NP']['name'].'_'.$pointer; $this->ElemName = $tag['NP']['name']; $code[] = "\$_parser->Elements['{$tag['NP']['name']}'] = '$f_name';"; // function_exists is required here, because a template may be included more than once // leading to Cannot redeclare error $code[] = "if (!function_exists('{$f_name}')) {"; $code[] = "function $f_name(&\$_parser, \$params) {"; $code[] = "global \$application;"; $code[] = "if (!\$_output = \$_parser->CacheStart('{$pointer}')) {"; $defaults = $this->Parser->CompileParamsArray($tag['NP']); $code[] = "\$params = array_merge($defaults, \$params);"; $code[] = "if (!isset(\$params['PrefixSpecial']) && isset(\$params['prefix'])) {\$params['PrefixSpecial'] = \$params['prefix'];};"; $code[] = "extract(\$params);"; $code[] = "\$_parser->SetParams(\$params);"; $code[] = 'ob_start();'; $this->AppendCode($o, $code, false); return $o . '?'.'>'; } function Close($tag) { $o = $this->Parser->Buffers[$this->Parser->Level]; $code[] = "\$_parser->CacheEnd();\n"; $code[] = '$_output = ob_get_contents();'; $code[] = 'ob_end_clean();'; $code[] = '}'; $code[] = "return \$_output === true ? '' : \$_output;"; $code[] = '}}'; $code[] = "\$_parser->CachableElements['".$this->ElemName."'] = ".($this->Parser->Cachable[$this->Parser->Level] ? 'true':'false').';'; $o .= '<?'.'php'; $this->AppendCode($o, $code, false); return $o; // $this->Parser->Definitions .= $o."\n"; // return ''; } */ public function __construct($tag) { parent::__construct($tag); $this->_requiredParams = Array ('name'); } function Open($tag) { $o = parent::Open($tag); if ($o === false) { // some required params not passed return $o; } $f_name = $tag['NP']['name'].'_'.abs(crc32($tag['file'])).'_'.$tag['line']; $this->Tag['function_name'] = $f_name; // for later use in closing tag $code[] = "\$_parser->Elements['{$tag['NP']['name']}'] = '$f_name';"; // function_exists is required here, because a template may be included more than once // leading to Cannot redeclare error $code[] = "if (!function_exists('{$f_name}')) {"; $code[] = "function $f_name(&\$_parser, \$params) {"; $code[] = "global \$application;"; $tag['NP'] = $this->_extractParams($tag['NP']); $defaults = $this->Parser->CompileParamsArray($tag['NP']); $code[] = "\$params = array_merge($defaults, \$params);"; $code[] = "if (!isset(\$params['PrefixSpecial']) && isset(\$params['prefix'])) {\$params['PrefixSpecial'] = \$params['prefix'];};"; - $code[] = "extract(\$params);"; + $code[] = 'extract($params, EXTR_SKIP);'; $code[] = "\$_parser->SetParams(\$params);"; $code[] = 'ob_start();'; $this->AppendCode($o, $code); return $o; } /** * Converts $param_name to $params['param_name'] * * @param Array $params * @return Array */ function _extractParams($params) { foreach ($params as $param_name => $param_value) { $params[$param_name] = preg_replace('/[\{]{0,1}([\$])(.*?[^\$\s\{\}]*)[\}]{0,1}/', '{$params[\'\\2\']}', $param_value); } return $params; } function Close($tag) { $o = $this->Parser->Buffers[$this->Parser->Level]; $code[] = '$_output = ob_get_contents();'; $code[] = 'ob_end_clean();'; $code[] = 'return $_output;'; $code[] = '}}'; $end_pos = $tag['pos'] + strlen($tag['opening']) + strlen($tag['tag']) + strlen($tag['closing']) + TAG_NAMESPACE_LENGTH; $code[] = "\$_parser->ElementLocations['{$this->Tag['function_name']}'] = Array('template' => '{$this->Tag['template']}', 'start_pos' => {$this->Tag['pos']}, 'end_pos' => {$end_pos});"; $this->AppendCode($o, $code); return $o; } } class _Tag_Capture extends _Tag_DefineElement { public function __construct($tag) { parent::__construct($tag); $this->_requiredParams = Array ('to_var'); } function Open($tag) { if (!$this->_checkRequiredParams($tag)) { return false; } $tag['NP']['name'] = '__capture_'.$tag['NP']['to_var']; $o = ''; // $this->AppendCode($o, "\$_parser->Captures['{$tag['NP']['to_var']}'] = 1;", false); $this->AppendCode($o, "\$_parser->Captures['{$tag['NP']['to_var']}'] = 1;"); $o .= parent::Open($tag); return $o; } } class _Tag_RenderElement extends _Tag_DefineElement { var $Single = true; var $OriginalTag; var $_lambdaName = ''; public function __construct($tag) { parent::__construct($tag); if (!$tag['is_closing']) { $this->_requiredParams = Array ('design'); } } function Open($tag) { if (!$this->_checkRequiredParams($tag)) { return false; } $o = ''; if ($tag['is_closing']) { if (isset($tag['NP']['design'])) { $this->RenderDesignCode($o, $tag['NP']); return $o; } $to_pass = $this->Parser->CompileParamsArray($tag['NP']); /* $pointer = abs(crc32($tag['file'])).'_'.$tag['line']; // $code[] = "}"; // $code[] = "if (!\$_parser->CachableElements['".$tag['NP']['name']."']) {"; // $code[] = "\$_parser->CacheEndInside();"; // $code[] = "}"; $o .= '<?'.'php '; $this->AppendCode($o, $this->Parser->BreakCache('', $pointer.'a', "\$_parser->CachableElements['".$tag['NP']['name']."']"), false); $this->AppendCode($o, "echo (\$_parser->ParseBlock($to_pass));\n", false); $this->AppendCode($o, $this->Parser->BreakCache('', $pointer.'b') . " ?".">\n", false); // $this->AppendCode($o, "if (!\$_parser->CacheStartOrContinue(\$_parser->CachableElements['".$tag['NP']['name']."'], '{$pointer}')) {".' ?'.'>', false); */ $code = array("echo (\$_parser->ParseBlock($to_pass));"); if ( array_key_exists('result_to_var', $tag['NP']) && $tag['NP']['result_to_var'] ) { $param_name = $tag['NP']['result_to_var']; $code[] = "\$params['{$param_name}'] = \$_parser->GetParam('{$param_name}');"; $code[] = "\${$param_name} = \$params['{$param_name}'];"; } $this->AppendCode($o, $code); return $o; } $this->Single = false; $this->OriginalTag = $tag; $tag['NP']['name'] = $tag['NP']['design'] . '_' . abs(crc32($tag['file'])) . '_' . $tag['line']; //'__lambda'; return parent::Open($tag); } function RenderDesignCode(&$o, $params) { $to_pass = $this->Parser->CompileParamsArray($params); $code[] = "echo (\$_parser->ParseBlock(array_merge($to_pass, array('name'=>\"{$params['design']}\",'content'=>\$_parser->ParseBlock($to_pass), 'keep_data_exists'=>1))));"; $this->AppendCode($o, $code); } function Close($tag) { if ($this->Single) { return $this->Parser->Buffers[$this->Parser->Level]; } $o = parent::Close($tag); $this->OriginalTag['NP']['name'] = $this->OriginalTag['NP']['design'] . '_' . abs(crc32($this->OriginalTag['file'])) . '_' . $this->OriginalTag['line']; //'__lambda'; $this->RenderDesignCode($o, $this->OriginalTag['NP']); return $o; } } class _Tag_RenderElements extends _BlockTag { public function __construct($tag) { parent::__construct($tag); $this->_requiredParams = Array ('elements'); } function Open($tag) { $o = parent::Open($tag); if ($o === false) { // some required params not passed return $o; } $element_names = array_map('trim', explode(',', $tag['NP']['elements'])); unset($tag['NP']['elements']); $class = '_Tag_RenderElement'; $instance = new $class($tag); /* @var $instance _Tag_RenderElement */ $instance->Parser =& $this->Parser; $skip_elements = array_key_exists('skip', $tag['NP']) ? array_map('trim', explode(',', $tag['NP']['skip'])) : Array (); foreach ($element_names as $element_name) { if (in_array($element_name, $skip_elements) || !$element_name) { // empty element name OR element should be excluded continue; } $tag['NP']['name'] = $element_name; $o .= $instance->Open($tag); } return $o; } } class _Tag_Param extends _BlockTag { public function __construct($tag) { parent::__construct($tag); $this->_requiredParams = Array ('name'); } function Open($tag) { $o = parent::Open($tag); if ($o === false) { // some required params not passed return $o; } $capture_params = $tag['NP']; $capture_params['name'] = '__capture_' . $tag['NP']['name']; $capture_to_pass = $this->Parser->CompileParamsArray($capture_params); $code[] = "if (isset(\$_parser->Captures['{$tag['NP']['name']}'])) {"; $code[] = "\${$tag['NP']['name']} = \$_parser->ParseBlock($capture_to_pass);"; $code[] = "}"; $code[] = "else {"; $to_pass = $this->Parser->CompileParamsArray($tag['NP']); $code[] = '$_p_ =& $_parser->GetProcessor(\'m\');'; $code[] = '$_tag_params = ' . $to_pass . ';'; $code[] = "\${$tag['NP']['name']} = \$_p_->PostProcess(\${$tag['NP']['name']}, \$_p_->PreparePostProcess(\$_tag_params));"; $code[] = "}"; 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']}');"; if (array_key_exists('plus', $tag['NP'])) { $code[] = "\$params['{$tag['NP']['result_to_var']}'] += {$tag['NP']['plus']};"; } $code[] = "\${$tag['NP']['result_to_var']} = \$params['{$tag['NP']['result_to_var']}'];"; } elseif (array_key_exists('plus', $tag['NP'])) { $code[] = "\${$tag['NP']['name']} += {$tag['NP']['plus']};"; } $code[] = "\$params['{$tag['NP']['name']}'] = \${$tag['NP']['name']};"; $code[] = "echo (\${$tag['NP']['name']});"; $this->AppendCode($o, $code); return $o; } } class _Tag_Include extends _BlockTag { function Open($tag) { $o = ''; $to_pass = $this->Parser->CompileParamsArray($tag['NP']); $this->AppendCode($o, "echo (\$_parser->IncludeTemplate($to_pass))"); return $o; } } class _Tag_If extends _BlockTag { /** * Permanently inverses if tag (used in ifnot tag) * * @var bool */ var $_Inversed = false; /** * Count of "elseif" tags inside * * @var int */ var $_elseIfCount = 0; public function __construct($tag) { parent::__construct($tag); $this->_requiredParams = Array ('check'); } function Open($tag) { $o = parent::Open($tag); if ($o === false) { // some required params not passed return $o; } $this->AppendCheckCode($o, $tag); return $o; } /** * Adds check code to $o * * @param string $o * @param Array $tag * @return void * @access protected */ protected function AppendCheckCode(&$o, $tag) { $to_pass = $this->Parser->CompileParamsArray($tag['NP']); $code = Array (); $code[] = "\$_splited = \$_parser->SplitTag(array('tag'=>\"{$tag['NP']['check']}\", 'file'=>'{$tag['file']}', 'line'=>{$tag['line']}));"; $code[] = 'if ($_splited[\'prefix\'] == \'__auto__\') {$_splited[\'prefix\'] = $PrefixSpecial;}'; $code[] = '$_p_ =& $_parser->GetProcessor($_splited[\'prefix\']);'; if ( isset($tag['NP']['inverse']) || $this->_Inversed ) { $code[] = "if (!\$_p_->ProcessParsedTag(\$_splited['name'], $to_pass, \$_splited['prefix'], '{$tag['file']}', {$tag['line']})) {"; } else { $code[] = "if (\$_p_->ProcessParsedTag(\$_splited['name'], $to_pass, \$_splited['prefix'], '{$tag['file']}', {$tag['line']})) {"; } $this->AppendCode($o, $code); } function PassThrough(&$tag) { $o = ''; if ($tag['name'] == 'else') { $this->AppendCode($o, '} else {'); $tag['processed'] = true; } if ($tag['name'] == 'elseif') { if (!$this->_checkRequiredParams($tag)) { return ''; } $this->_elseIfCount++; // add same count of closing brackets in closing tag $this->AppendCode($o, "} else {"); $this->AppendCheckCode($o, $tag); $tag['processed'] = true; } return $o; } function Close($tag) { $o = $this->Parser->Buffers[$this->Parser->Level]; $code = str_repeat("}\n", $this->_elseIfCount + 1); $this->AppendCode($o, $code); return $o; } } class _Tag_IfNot extends _Tag_If { public function __construct($tag) { parent::__construct($tag); $this->_Inversed = true; } } class _Tag_DefaultParam extends _BlockTag { function Open($tag) { $o = ''; foreach ($tag['NP'] as $key => $val) { $code[] = 'if (!isset($' . $key . ')) $params["' . $key . '"] = "' . $val . '";'; $code[] = '$' . $key . ' = isset($' . $key . ') ? $' . $key . ' : "' . $val . '";'; $code[] = '$_parser->SetParam("' . $key . '", $' . $key . ');'; } $this->AppendCode($o, $code); return $o; } } class _Tag_SetParam extends _BlockTag { function Open($tag) { $o = ''; foreach ($tag['NP'] as $key => $val) { $code[] = '$params[\'' . $key . '\'] = "' . $val . '";'; $code[] = '$' . $key . ' = "' . $val . '";'; $code[] = '$_parser->SetParam(\'' . $key . '\', $' . $key . ');'; } $this->AppendCode($o, $code); return $o; } } class _Tag_Cache extends _BlockTag { function Open($tag) { $o = ''; $pointer = abs(crc32($tag['file'])).'_'.$tag['line']; $key = array_key_exists('key', $tag['NP']) ? $tag['NP']['key'] : ''; $this->AppendCode($o, "if (!\$_parser->CacheStart('{$pointer}', \"{$key}\")) {\n"); return $o; } function Close($tag) { $o = $this->Parser->Buffers[$this->Parser->Level]; $cache_timeout = array_key_exists('cache_timeout', $this->Tag['NP']) ? $this->Tag['NP']['cache_timeout'] : 0; $this->AppendCode($o, "\$_parser->CacheEnd(" . (int)$cache_timeout . ");\n}\n"); return $o; } } class _Tag_IfDataExists extends _BlockTag { function Open($tag) { $o = ''; $code = array(); $code[] = "ob_start();\n"; $code[] = '$_tmp_data_exists = $_parser->DataExists;'; $code[] = '$_parser->DataExists = false;'; $this->AppendCode($o, $code); return $o; } function Close($tag) { $o = $this->Parser->Buffers[$this->Parser->Level]; $code = array(); $code[] = '$res = ob_get_clean();'; $code[] = 'if ($_parser->DataExists) {echo $res;}'; if (array_key_exists('block_no_data', $this->Tag['NP'])) { $tag_params = $this->Tag['NP']; $tag_params['name'] = $tag_params['block_no_data']; unset($tag_params['block_no_data']); $to_pass = $this->Parser->CompileParamsArray($tag_params); $code[] = "else { echo (\$_parser->ParseBlock($to_pass)); }"; } $code[] = '$_parser->DataExists = $_tmp_data_exists;'; $this->AppendCode($o, $code); return $o; } } class _Tag_Compress extends _BlockTag { public function __construct($tag) { parent::__construct($tag); if ($tag['is_closing']) { if (!array_key_exists('files', $tag['NP'])) { $this->_requiredParams = Array ('from'); } else { $this->_requiredParams = Array ('files'); } } else { $this->_requiredParams = Array ('type'); // js/css } } /** * When used as non-block tag, then compress given files and return url to result * * @param Array $tag * @return string */ function Open($tag) { $o = parent::Open($tag); if ($o === false) { // some required params not passed return $o; } if ($tag['is_closing']) { $to_pass = $this->Parser->CompileParamsArray($tag['NP']); $this->AppendCode($o, "echo \$_parser->CompressScript($to_pass, false);"); } else { $this->AppendCode($o, "ob_start();"); } return $o; } /** * When used as block tag, then compress contents between tags * * @param Array $tag * @return string */ function Close($tag) { if ($this->Tag['is_closing']) { return parent::Close($tag); } $o = parent::Close($tag); $code = Array (); $code[] = '$res = ob_get_clean();'; $code[] = 'echo $_parser->CompressScript($res, true, \'' . $this->Tag['NP']['type'] . '\');'; $this->AppendCode($o, $code); return $o; } } Index: branches/5.3.x/core/kernel/nparser/nparser_config.php =================================================================== --- branches/5.3.x/core/kernel/nparser/nparser_config.php (revision 16394) +++ branches/5.3.x/core/kernel/nparser/nparser_config.php (revision 16395) @@ -1,25 +1,40 @@ <?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' => 'nparser', 'EventHandlerClass' => Array ('class' => 'kEventHandler', 'file' => '', 'build_event' => 'OnBuild'), 'RegisterClasses' => Array ( - Array ('pseudo' => 'NParserCompiler', 'class' => 'NParserCompiler', 'file' => 'compiler.php', 'build_event' => ''), + Array('pseudo' => 'NParserCompiler', 'class' => 'NParserCompiler', 'file' => 'compiler.php', 'build_event' => ''), + Array('pseudo' => '_BlockTag', 'class' => '_BlockTag', 'file' => 'ntags.php', 'build_event' => ''), + Array('pseudo' => '_Tag_Comment', 'class' => '_Tag_Comment', 'file' => 'ntags.php', 'build_event' => ''), + Array('pseudo' => '_Tag_DefineElement', 'class' => '_Tag_DefineElement', 'file' => 'ntags.php', 'build_event' => ''), + Array('pseudo' => '_Tag_Capture', 'class' => '_Tag_Capture', 'file' => 'ntags.php', 'build_event' => ''), + Array('pseudo' => '_Tag_RenderElement', 'class' => '_Tag_RenderElement', 'file' => 'ntags.php', 'build_event' => ''), + Array('pseudo' => '_Tag_RenderElements', 'class' => '_Tag_RenderElements', 'file' => 'ntags.php', 'build_event' => ''), + Array('pseudo' => '_Tag_Param', 'class' => '_Tag_Param', 'file' => 'ntags.php', 'build_event' => ''), + Array('pseudo' => '_Tag_Include', 'class' => '_Tag_Include', 'file' => 'ntags.php', 'build_event' => ''), + Array('pseudo' => '_Tag_If', 'class' => '_Tag_If', 'file' => 'ntags.php', 'build_event' => ''), + Array('pseudo' => '_Tag_IfNot', 'class' => '_Tag_IfNot', 'file' => 'ntags.php', 'build_event' => ''), + Array('pseudo' => '_Tag_DefaultParam', 'class' => '_Tag_DefaultParam', 'file' => 'ntags.php', 'build_event' => ''), + Array('pseudo' => '_Tag_SetParam', 'class' => '_Tag_SetParam', 'file' => 'ntags.php', 'build_event' => ''), + Array('pseudo' => '_Tag_Cache', 'class' => '_Tag_Cache', 'file' => 'ntags.php', 'build_event' => ''), + Array('pseudo' => '_Tag_IfDataExists', 'class' => '_Tag_IfDataExists', 'file' => 'ntags.php', 'build_event' => ''), + Array('pseudo' => '_Tag_Compress', 'class' => '_Tag_Compress', 'file' => 'ntags.php', 'build_event' => ''), ), ); Index: branches/5.3.x/core/kernel/kbase.php =================================================================== --- branches/5.3.x/core/kernel/kbase.php (revision 16394) +++ branches/5.3.x/core/kernel/kbase.php (revision 16395) @@ -1,1193 +1,1206 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ //defined('FULL_PATH') or die('restricted access!'); /** * Base * */ class kBase { /** * Reference to global kApplication instance * * @var kApplication * @access protected */ protected $Application = null; /** * Connection to database * * @var IDBConnection * @access protected */ protected $Conn = null; /** * Prefix, used during object creation * * @var string * @access public */ public $Prefix = ''; /** * Special, used during object creation * * @var string * @access public */ public $Special = ''; /** * Joined prefix and special, usually taken directly from * tag beeing processed, to use in kApplication::recallObject method * * @var string * @access protected * @see kApplication::recallObject */ protected $prefixSpecial = ''; /** * Set's references to kApplication and DBConnection interface class instances * * @access public * @see kApplication * @see IDBConnection */ public function __construct() { $this->Application =& kApplication::Instance(); $this->Conn =& $this->Application->GetADODBConnection(); } /** * Set's prefix and special * * @param string $prefix * @param string $special * @access public */ public function Init($prefix, $special) { $prefix = explode('_', $prefix, 2); $this->Prefix = $prefix[0]; $this->Special = $special; $this->prefixSpecial = rtrim($this->Prefix . '.' . $this->Special, '.'); } /** * Returns prefix and special (when present) joined by a "." * * @return string * @access public */ public function getPrefixSpecial() { return $this->prefixSpecial; } /** * Returns unit config, used in tag * * @return kUnitConfig * @access public */ public function getUnitConfig() { return $this->Application->getUnitConfig($this->Prefix); } /** * Creates string representation of a class (for logging) * * @return string * @access public */ public function __toString() { $ret = 'ClassName: ' . get_class($this); try { $ret .= '; PrefixSpecial: ' . $this->getPrefixSpecial(); } catch (Exception $e) {} return $ret; } } class kHelper extends kBase { /** * Performs helper initialization * * @access public */ public function InitHelper() { } /** * Append prefix and special to tag * params (get them from tagname) like * they were really passed as params * * @param string $prefix_special * @param Array $tag_params * @return Array * @access protected */ protected function prepareTagParams($prefix_special, $tag_params = Array()) { $parts = explode('.', $prefix_special); $ret = $tag_params; $ret['Prefix'] = $parts[0]; $ret['Special'] = count($parts) > 1 ? $parts[1] : ''; $ret['PrefixSpecial'] = $prefix_special; return $ret; } } abstract class kDBBase extends kBase { /** * Name of primary key field for the unit * * @var string * @access public * @see kDBBase::TableName */ public $IDField = ''; /** * Unit's database table name * * @var string * @access public */ public $TableName = ''; /** * Form name, used for validation * * @var string */ protected $formName = ''; /** * Final form configuration * * @var Array */ protected $formConfig = Array (); /** * SELECT, FROM, JOIN parts of SELECT query (no filters, no limit, no ordering) * * @var string * @access protected */ protected $SelectClause = ''; /** * Unit fields definitions (fields from database plus virtual fields) * * @var Array * @access protected */ protected $Fields = Array (); /** + * Fields, that have current time as their default value. + * + * @var array + */ + protected $currentTimeFields = array(); + + /** * Mapping between unit custom field IDs and their names * * @var Array * @access protected */ protected $customFields = Array (); /** * Unit virtual field definitions * * @var Array * @access protected * @see kDBBase::getVirtualFields() * @see kDBBase::setVirtualFields() */ protected $VirtualFields = Array (); /** * Fields that need to be queried using custom expression, e.g. IF(...) AS value * * @var Array * @access protected */ protected $CalculatedFields = Array (); /** * Fields that contain aggregated functions, e.g. COUNT, SUM, etc. * * @var Array * @access protected */ protected $AggregatedCalculatedFields = Array (); /** * Tells, that multilingual fields sould not be populated by default. * Can be overriden from kDBBase::Configure method * * @var bool * @access protected */ protected $populateMultiLangFields = false; /** * Event, that was used to create this object * * @var kEvent * @access protected */ protected $parentEvent = null; /** * Sets new parent event to the object * * @param kEvent $event * @return void * @access public */ public function setParentEvent($event) { $this->parentEvent = $event; } /** * Set object' TableName to LIVE table, defined in unit config * * @access public */ public function SwitchToLive() { $this->TableName = $this->getUnitConfig()->getTableName(); } /** * Set object' TableName to TEMP table created based on table, defined in unit config * * @access public */ public function SwitchToTemp() { $table_name = $this->getUnitConfig()->getTableName(); $this->TableName = $this->Application->GetTempName($table_name, 'prefix:' . $this->Prefix); } /** * Checks if object uses temp table * * @return bool * @access public */ public function IsTempTable() { return $this->Application->IsTempTable($this->TableName); } /** * Sets SELECT part of list' query * * @param string $sql SELECT and FROM [JOIN] part of the query up to WHERE * @access public */ public function SetSelectSQL($sql) { $this->SelectClause = $sql; } /** * Returns object select clause without any transformations * * @return string * @access public */ public function GetPlainSelectSQL() { return $this->SelectClause; } /** * Returns SELECT part of list' query. * 1. Occurrences of "%1$s" and "%s" are replaced to kDBBase::TableName * 2. Occurrences of "%3$s" are replaced to temp table prefix (only for table, using TABLE_PREFIX) * * @param string $base_query given base query will override unit default select query * @param bool $replace_table replace all possible occurrences * @return string * @access public * @see kDBBase::replaceModePrefix */ public function GetSelectSQL($base_query = null, $replace_table = true) { if (!isset($base_query)) { $base_query = $this->SelectClause; } if (!$replace_table) { return $base_query; } $query = str_replace(Array('%1$s', '%s'), $this->TableName, $base_query); return $this->replaceModePrefix($query); } /** * Allows sub-stables to be in same mode as main item (e.g. LEFT JOINED ones) * * @param string $query * @return string * @access protected */ protected function replaceModePrefix($query) { $live_table = substr($this->Application->GetLiveName($this->TableName), strlen(TABLE_PREFIX)); if (preg_match('/'.preg_quote(TABLE_PREFIX, '/').'(.*)'.preg_quote($live_table, '/').'/', $this->TableName, $rets)) { // will only happen, when table has a prefix (like in K4) return str_replace('%3$s', $rets[1], $query); } // will happen, when K3 table without prefix is used return $query; } /** * Sets calculated fields * * @param Array $fields * @access public */ public function setCalculatedFields($fields) { $this->CalculatedFields = $fields; } /** * Adds calculated field declaration to object. * * @param string $name * @param string $sql_clause * @access public */ public function addCalculatedField($name, $sql_clause) { $this->CalculatedFields[$name] = $sql_clause; } /** * Returns required mixing of aggregated & non-aggregated calculated fields * * @param int $aggregated 0 - having + aggregated, 1 - having only, 2 - aggregated only * @return Array * @access public */ public function getCalculatedFields($aggregated = 1) { switch ($aggregated) { case 0: $fields = array_merge($this->CalculatedFields, $this->AggregatedCalculatedFields); break; case 1: $fields = $this->CalculatedFields; break; case 2: $fields = $this->AggregatedCalculatedFields; // TODO: never used break; default: $fields = Array(); break; } return $fields; } /** * Checks, that given field is a calculated field * * @param string $field * @return bool * @access public */ public function isCalculatedField($field) { return array_key_exists($field, $this->CalculatedFields); } /** * Insert calculated fields sql into query in place of %2$s, * return processed query. * * @param string $query * @param int $aggregated 0 - having + aggregated, 1 - having only, 2 - aggregated only * @return string * @access protected */ protected function addCalculatedFields($query, $aggregated = 1) { $fields = $this->getCalculatedFields($aggregated); if ( $fields ) { $sql = Array (); // inside calculated field "%2$s" is current language $fields = str_replace('%2$s', $this->Application->GetVar('m_lang'), $fields); // can't use "%3$s" as usual, because it's already populated in kDBBase::replaceModePrefix() across whole query $fields = str_replace('%4$s', $this->Application->GetDefaultLanguageId(), $fields); foreach ($fields as $field_name => $field_expression) { $sql[] = '(' . $field_expression . ') AS `' . $field_name . '`'; } $sql = implode(',', $sql); // inside sql "%2$s" is placeholder for calculated fields return $this->Application->ReplaceLanguageTags(str_replace('%2$s', ',' . $sql, $query)); } return str_replace('%2$s', '', $query); } /** * Performs initial object configuration, which includes setting the following: * - primary key and table name * - field definitions (including field modifiers, formatters, default values) * * @param bool $populate_ml_fields create all ml fields from db in config or not * @param string $form_name form name for validation * @access public */ public function Configure($populate_ml_fields = null, $form_name = null) { if ( isset($populate_ml_fields) ) { $this->populateMultiLangFields = $populate_ml_fields; } $config = $this->getUnitConfig(); $this->IDField = $config->getIDField(); $this->TableName = $config->getTableName(); $this->initForm($form_name); $this->defineFields(); $this->ApplyFieldModifiers(null, true); // should be called only after all fields definitions been set $this->prepareConfigOptions(); // this should go last, but before setDefaultValues, order is significant! // only set on first call of method if ( isset($populate_ml_fields) ) { $this->SetDefaultValues(); } } /** * Adjusts object according to given form name * * @param string $form_name * @return void * @access protected */ protected function initForm($form_name = null) { $config = $this->getUnitConfig(); $this->formName = $form_name; $this->formConfig = $config->getFormByName('default', Array ()); if ( !$this->formName ) { return ; } $form_data = $config->getFormByName($this->formName); if ( $form_data === false ) { trigger_error('Form "<strong>' . $this->formName . '</strong>" isn\'t declared in "<strong>' . $this->Prefix . '</strong>" unit config.', E_USER_NOTICE); } else { $this->formConfig = kUtil::array_merge_recursive($this->formConfig, $form_data); } } /** * Add field definitions from all possible sources * Used field sources: database fields, custom fields, virtual fields, calculated fields, aggregated calculated fields * * @access protected */ protected function defineFields() { $this->Fields = $this->getFormOption('Fields', Array ()); $this->customFields = $this->getFormOption('CustomFields', Array()); $this->setVirtualFields( $this->getFormOption('VirtualFields', Array ()) ); $calculated_fields = $this->getFormOption('CalculatedFields', Array()); $this->CalculatedFields = $this->getFieldsBySpecial($calculated_fields); $aggregated_calculated_fields = $this->getFormOption('AggregatedCalculatedFields', Array()); $this->AggregatedCalculatedFields = $this->getFieldsBySpecial($aggregated_calculated_fields); } /** * Returns form name, used for validation * * @return string */ public function getFormName() { return $this->formName; } /** * Reads unit (specified by $prefix) option specified by $option and applies form change to it * * @param string $option * @param mixed $default * @return string * @access public */ public function getFormOption($option, $default = false) { $ret = $this->getUnitConfig()->getSetting($option, $default); if ( isset($this->formConfig[$option]) ) { $ret = kUtil::array_merge_recursive($ret, $this->formConfig[$option]); } return $ret; } /** * Only exteracts fields, that match current object Special * * @param Array $fields * @return Array * @access protected */ protected function getFieldsBySpecial($fields) { if ( array_key_exists($this->Special, $fields) ) { return $fields[$this->Special]; } return array_key_exists('', $fields) ? $fields[''] : Array(); } /** * Sets aggeregated calculated fields * * @param Array $fields * @access public */ public function setAggregatedCalculatedFields($fields) { $this->AggregatedCalculatedFields = $fields; } /** * Set's field names from table from config * * @param Array $fields * @access public */ public function setCustomFields($fields) { $this->customFields = $fields; } /** * Returns custom fields information from table from config * * @return Array * @access public */ public function getCustomFields() { return $this->customFields; } /** * Set's fields information from table from config * * @param Array $fields * @access public */ public function setFields($fields) { $this->Fields = $fields; } /** * Returns fields information from table from config * * @return Array * @access public */ public function getFields() { return $this->Fields; } /** * Checks, that given field exists * * @param string $field * @return bool * @access public */ public function isField($field) { return array_key_exists($field, $this->Fields); } /** * Override field options with ones defined in submit via "field_modfiers" array (common for all prefixes) * * @param Array $field_modifiers * @param bool $from_submit * @return void * @access public * @author Alex */ public function ApplyFieldModifiers($field_modifiers = null, $from_submit = false) { $allowed_modifiers = Array ('required', 'multiple'); if ( $this->Application->isAdminUser ) { // can change upload dir on the fly (admin only!) $allowed_modifiers[] = 'upload_dir'; } if ( !isset($field_modifiers) ) { $field_modifiers = $this->Application->GetVar('field_modifiers'); if ( !$field_modifiers ) { // no field modifiers return; } $field_modifiers = getArrayValue($field_modifiers, $this->getPrefixSpecial()); } if ( !$field_modifiers ) { // no field modifiers for current prefix_special return; } $config = $this->getUnitConfig(); $fields = $config->getFields(Array ()); $virtual_fields = $config->getVirtualFields(Array ()); foreach ($field_modifiers as $field => $field_options) { foreach ($field_options as $option_name => $option_value) { if ( !in_array(strtolower($option_name), $allowed_modifiers) ) { continue; } if ( $from_submit ) { // there are no "lN_FieldName" fields, since ApplyFieldModifiers is // called before PrepareOptions method, which creates them $field = preg_replace('/^l[\d]+_(.*)/', '\\1', $field); } if ( $this->isVirtualField($field) ) { $virtual_fields[$field][$option_name] = $option_value; $this->SetFieldOption($field, $option_name, $option_value, true); } $fields[$field][$option_name] = $option_value; $this->SetFieldOption($field, $option_name, $option_value); } } $config->setFields($fields); $config->setVirtualFields($virtual_fields); } /** * Set fields (+options) for fields that physically doesn't exist in database * * @param Array $fields * @access public */ public function setVirtualFields($fields) { if ($fields) { $this->VirtualFields = $fields; $this->Fields = array_merge($this->VirtualFields, $this->Fields); } } /** * Returns virtual fields * * @return Array * @access public */ public function getVirtualFields() { return $this->VirtualFields; } /** * Checks, that given field is a virtual field * * @param string $field * @return bool * @access public */ public function isVirtualField($field) { return array_key_exists($field, $this->VirtualFields); } /** * Performs additional initialization for field default values * * @access protected */ protected function SetDefaultValues() { - foreach ($this->Fields as $field => $options) { - if ( array_key_exists('default', $options) && $options['default'] === '#NOW#' ) { - $this->Fields[$field]['default'] = time(); + foreach ( $this->Fields as $field => $options ) { + if ( array_key_exists('default', $options) ) { + if ( $options['default'] === '#NOW#' ) { + $this->currentTimeFields[] = $field; + } + + if ( in_array($field, $this->currentTimeFields) ) { + $this->Fields[$field]['default'] = time(); + } } } } /** * Overwrites field definition in unit config * * @param string $field * @param Array $options * @param bool $is_virtual * @access public */ public function SetFieldOptions($field, $options, $is_virtual = false) { if ($is_virtual) { $this->VirtualFields[$field] = $options; $this->Fields = array_merge($this->VirtualFields, $this->Fields); } else { $this->Fields[$field] = $options; } } /** * Changes/sets given option's value in given field definiton * * @param string $field * @param string $option_name * @param mixed $option_value * @param bool $is_virtual * @access public */ public function SetFieldOption($field, $option_name, $option_value, $is_virtual = false) { if ($is_virtual) { $this->VirtualFields[$field][$option_name] = $option_value; } $this->Fields[$field][$option_name] = $option_value; } /** * Returns field definition from unit config. * Also executes sql from "options_sql" field option to form "options" field option * * @param string $field * @param bool $is_virtual * @return Array * @access public */ public function GetFieldOptions($field, $is_virtual = false) { $property_name = $is_virtual ? 'VirtualFields' : 'Fields'; if ( !array_key_exists($field, $this->$property_name) ) { return Array (); } if (!$is_virtual) { if (!array_key_exists('options_prepared', $this->Fields[$field]) || !$this->Fields[$field]['options_prepared']) { // executes "options_sql" from field definition, only when field options are accessed (late binding) $this->PrepareFieldOptions($field); $this->Fields[$field]['options_prepared'] = true; } } return $this->{$property_name}[$field]; } /** * Returns field option * * @param string $field * @param string $option_name * @param bool $is_virtual * @param mixed $default * @return mixed * @access public */ public function GetFieldOption($field, $option_name, $is_virtual = false, $default = false) { $field_options = $this->GetFieldOptions($field, $is_virtual); if ( !$field_options && strpos($field, '#') === false ) { // we use "#FIELD_NAME#" as field for InputName tag in JavaScript, so ignore it $form_name = $this->getFormName(); trigger_error('Field "<strong>' . $field . '</strong>" is not defined' . ($form_name ? ' on "<strong>' . $this->getFormName() . '</strong>" form' : '') . ' in "<strong>' . $this->Prefix . '</strong>" unit config', E_USER_WARNING); return false; } return array_key_exists($option_name, $field_options) ? $field_options[$option_name] : $default; } /** * Returns formatted field value * * @param string $name * @param string $format * * @return string * @access protected */ public function GetField($name, $format = null) { $formatter_class = $this->GetFieldOption($name, 'formatter'); if ( $formatter_class ) { $value = ($formatter_class == 'kMultiLanguage') && !preg_match('/^l[0-9]+_/', $name) ? '' : $this->GetDBField($name); $formatter = $this->Application->recallObject($formatter_class); /* @var $formatter kFormatter */ return $formatter->Format($value, $name, $this, $format); } return $this->GetDBField($name); } /** * Returns unformatted field value * * @param string $field * @return string * @access public */ abstract public function GetDBField($field); /** * Checks of object has given field * * @param string $name * @return bool * @access public */ abstract public function HasField($name); /** * Returns field values * * @return Array * @access public */ abstract public function GetFieldValues(); /** * Populates values of sub-fields, based on formatters, set to mater fields * * @param Array $fields * @access public * @todo Maybe should not be publicly accessible */ public function UpdateFormattersSubFields($fields = null) { if ( !is_array($fields) ) { $fields = array_keys($this->Fields); } foreach ($fields as $field) { if ( isset($this->Fields[$field]['formatter']) ) { $formatter = $this->Application->recallObject($this->Fields[$field]['formatter']); /* @var $formatter kFormatter */ $formatter->UpdateSubFields($field, $this->GetDBField($field), $this->Fields[$field], $this); } } } /** * Use formatters, specified in field declarations to perform additional field initialization in unit config * * @access protected */ protected function prepareConfigOptions() { $field_names = array_keys($this->Fields); foreach ($field_names as $field_name) { if ( !array_key_exists('formatter', $this->Fields[$field_name]) ) { continue; } $formatter = $this->Application->recallObject( $this->Fields[$field_name]['formatter'] ); /* @var $formatter kFormatter */ $formatter->PrepareOptions($field_name, $this->Fields[$field_name], $this); } } /** * Escapes fields only, not expressions * * @param string $field_expr * @return string * @access protected */ protected function escapeField($field_expr) { return preg_match('/[.(]/', $field_expr) ? $field_expr : '`' . $field_expr . '`'; } /** * Replaces current language id in given field options * * @param string $field_name * @param Array $field_option_names * @access protected */ protected function _replaceLanguageId($field_name, $field_option_names) { // don't use GetVar('m_lang') since it's always equals to default language on editing form in admin $current_language_id = $this->Application->Phrases->LanguageId; $primary_language_id = $this->Application->GetDefaultLanguageId(); $field_options =& $this->Fields[$field_name]; foreach ($field_option_names as $option_name) { $field_options[$option_name] = str_replace('%2$s', $current_language_id, $field_options[$option_name]); $field_options[$option_name] = str_replace('%3$s', $primary_language_id, $field_options[$option_name]); } } /** * Transforms "options_sql" field option into valid "options" array for given field * * @param string $field_name * @access protected */ protected function PrepareFieldOptions($field_name) { $field_options =& $this->Fields[$field_name]; if (array_key_exists('options_sql', $field_options) ) { // get options based on given sql $replace_options = Array ('option_title_field', 'option_key_field', 'options_sql'); $this->_replaceLanguageId($field_name, $replace_options); $select_clause = $this->escapeField($field_options['option_title_field']) . ',' . $this->escapeField($field_options['option_key_field']); $sql = sprintf($field_options['options_sql'], $select_clause); if (array_key_exists('serial_name', $field_options)) { // try to cache option sql on serial basis $cache_key = 'sql_' . crc32($sql) . '[%' . $field_options['serial_name'] . '%]'; $dynamic_options = $this->Application->getCache($cache_key); if ($dynamic_options === false) { $this->Conn->nextQueryCachable = true; $dynamic_options = $this->Conn->GetCol($sql, preg_replace('/^.*?\./', '', $field_options['option_key_field'])); $this->Application->setCache($cache_key, $dynamic_options); } } else { // don't cache options sql $dynamic_options = $this->Conn->GetCol($sql, preg_replace('/^.*?\./', '', $field_options['option_key_field'])); } $options_hash = array_key_exists('options', $field_options) ? $field_options['options'] : Array (); $field_options['options'] = kUtil::array_merge_recursive($options_hash, $dynamic_options); // because of numeric keys } } /** * Returns ID of currently processed record * * @return int * @access public */ public function GetID() { return $this->GetDBField($this->IDField); } /** * Allows kDBTagProcessor.SectionTitle to detect if it's editing or new item creation * * @return bool * @access public */ public function IsNewItem() { return $this->GetID() ? false : true; } /** * Returns parent table information * * @param string $special special of main item * @param bool $guess_special if object retrieved with specified special is not loaded, then try not to use special * @return Array * @access public */ public function getLinkedInfo($special = '', $guess_special = false) { $config = $this->getUnitConfig(); $parent_prefix = $config->getParentPrefix(); if ( $parent_prefix ) { // if this is linked table, then set id from main table $table_info = Array ( 'TableName' => $config->getTableName(), 'IdField' => $config->getIDField(), 'ForeignKey' => $config->getForeignKey($parent_prefix), 'ParentTableKey' => $config->getParentTableKey($parent_prefix), 'ParentPrefix' => $parent_prefix ); $main_object = $this->Application->recallObject($parent_prefix . '.' . $special, null, Array ('raise_warnings' => 0)); /* @var $main_object kDBItem */ if ( !$main_object->isLoaded() && $guess_special ) { $main_object = $this->Application->recallObject($parent_prefix); } $table_info['ParentId'] = $main_object->GetDBField($table_info['ParentTableKey']); return $table_info; } return false; } /** * Returns true, when list/item was queried/loaded * * @return bool * @access public */ abstract public function isLoaded(); /** * Returns specified field value from all selected rows. * Don't affect current record index * * @param string $field * @return Array * @access public */ abstract public function GetCol($field); } /** * Base class for exceptions, that trigger redirect action once thrown */ class kRedirectException extends Exception { /** * Redirect template * * @var string * @access protected */ protected $template = ''; /** * Redirect params * * @var Array * @access protected */ protected $params = Array (); /** * Creates redirect exception * * @param string $message * @param int $code * @param Exception $previous */ public function __construct($message = '', $code = 0, $previous = NULL) { parent::__construct($message, $code, $previous); } /** * Initializes exception * * @param string $template * @param Array $params * @return void * @access public */ public function setup($template, $params = Array ()) { $this->template = $template; $this->params = $params; } /** * Display exception details in debugger (only useful, when DBG_REDIRECT is enabled) and performs redirect * * @return void * @access public */ public function run() { $application =& kApplication::Instance(); if ( $application->isDebugMode() ) { $application->Debugger->appendException($this); } $application->Redirect($this->template, $this->params); } } /** * Exception, that is thrown when user don't have permission to perform requested action */ class kNoPermissionException extends kRedirectException { } Index: branches/5.3.x/core/units/visits/visits_tag_processor.php =================================================================== --- branches/5.3.x/core/units/visits/visits_tag_processor.php (revision 16394) +++ branches/5.3.x/core/units/visits/visits_tag_processor.php (revision 16395) @@ -1,184 +1,181 @@ <?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 VisitsTagProcessor extends kDBTagProcessor { function UserFound($params) { $virtual_users = Array(USER_ROOT, USER_GUEST, 0); $object = $this->getObject($params); /* @var $object kDBItem */ return !in_array( $object->GetDBField( $params['user_field'] ) , $virtual_users ); } /** * Returns link for user editing * * @param Array $params * * @return string * @access protected */ protected function UserLink($params) { $object = $this->getObject($params); /* @var $object kDBItem */ $user_id = $object->GetDBField( $params['user_field'] ); if (!$user_id) { return ''; } $url_params = Array ( 'm_opener' => 'd', 'u_mode' => 't', 'u_event' => 'OnEdit', 'u_id' => $user_id, 'pass' => 'all,u' ); return $this->Application->HREF($params['edit_template'], '', $url_params); } function getDateLimitClause($field) { $search_filter = $this->Application->RecallVar( $this->getPrefixSpecial().'_search_filter'); if($search_filter) { $search_filter = unserialize($search_filter); return $search_filter[$field]['value']; } return ''; } function AffiliateOrderInfo($params) { $list =& $this->GetList($params); $date_limit = str_replace($list->TableName, 'vis', $this->getDateLimitClause('VisitDate') ); $affiliates_config = $this->Application->getUnitConfig('affil'); $sql = 'SELECT '. $affiliates_config->getIDField() .' FROM '. $affiliates_config->getTableName() .' WHERE PortalUserId = '.$this->Application->RecallVar('user_id'); $affiliate_id = $this->Conn->GetOne($sql); $sql = 'SELECT COUNT(ord.OrderId) AS OrderCount FROM '.$list->TableName.' vis LEFT JOIN '.TABLE_PREFIX.'Orders ord ON ord.VisitId = vis.VisitId WHERE (vis.AffiliateId = '.$affiliate_id.') AND (ord.Status = '.ORDER_STATUS_PROCESSED.')'.($date_limit ? ' AND '.$date_limit : ''); $result = $this->Conn->GetRow($sql); $sql = 'SELECT COUNT(*) FROM '.$list->TableName.' vis WHERE AffiliateId = '.$affiliate_id.($date_limit ? ' AND '.$date_limit : ''); $result['TotalVisitors'] = $this->Conn->GetOne($sql); $result['OrderTotalAmount'] = $list->getTotal('OrderTotalAmount', 'SUM'); $result['OrderAffiliateCommission'] = $list->getTotal('OrderAffiliateCommission', 'SUM'); $block_params = $this->prepareTagParams($params); $block_params['name'] = $params['render_as']; $format_fields = Array('OrderTotalAmount', 'OrderAffiliateCommission'); if (array_key_exists('currency', $params) && $params['currency']) { $iso = $this->GetISO($params['currency']); foreach($format_fields as $format_field) { $format = $list->GetFieldOption($format_field, 'format'); $value = sprintf($format, $result[$format_field]); $value = $this->ConvertCurrency($value, $iso); $value = $this->AddCurrencySymbol($value, $iso); $result[$format_field] = $value; } } $block_params = array_merge($block_params, $result); return $this->Application->ParseBlock($block_params); } function ListVisitors($params) { $o = ''; $params['render_as'] = $params['item_render_as']; $o_visitors = $this->PrintList2($params); if($o_visitors) { $header = ''; $footer = ''; $block_params = $this->prepareTagParams($params); $header_block = getArrayValue($params, 'header_render_as'); if($header_block) { $block_params['name'] = $header_block; $header = $this->Application->ParseBlock($block_params); } $footer_block = getArrayValue($params, 'footer_render_as'); if($footer_block) { $block_params['name'] = $footer_block; $footer = $this->Application->ParseBlock($block_params); } $o = $header.$o_visitors.$footer; } else { $visitors_params = array('name' => $params['empty_myvisitors_render_as']); $o = $this->Application->ParseBlock($visitors_params); } return $o; } /** * Enter description here... * * @param string $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'); } $types = $this->SelectParam($params, 'types'); if ( $types == 'myvisitororders' || $types == 'myvisitors' ) { - $special = 'incommerce'; - $names_mapping = $this->Application->GetVar('NamesToSpecialMapping', Array ()); - $names_mapping[$this->Prefix][$list_name] = $special; - $this->Application->SetVar('NamesToSpecialMapping', $names_mapping); + $this->nameToSpecialMapping[$list_name] = 'incommerce'; } return parent::GetList($params); } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/categories/categories_config.php =================================================================== --- branches/5.3.x/core/units/categories/categories_config.php (revision 16394) +++ branches/5.3.x/core/units/categories/categories_config.php (revision 16395) @@ -1,543 +1,545 @@ <?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' => 'c', 'ItemClass' => Array ('class' => 'CategoriesItem', 'file' => 'categories_item.php', 'build_event' => 'OnItemBuild'), 'ListClass' => Array ('class' => 'kDBList', 'file' => '', 'build_event' => 'OnListBuild'), 'EventHandlerClass' => Array ('class' => 'CategoriesEventHandler', 'file' => 'categories_event_handler.php', 'build_event' => 'OnBuild'), 'TagProcessorClass' => Array ('class' => 'CategoriesTagProcessor', 'file' => 'categories_tag_processor.php', 'build_event' => 'OnBuild'), 'RegisterClasses' => Array ( + Array ('pseudo' => 'clsCachedPermissions', 'class' => 'clsCachedPermissions', 'file' => 'cache_updater.php', 'build_event' => ''), + Array ('pseudo' => 'clsRecursionStack', 'class' => 'clsRecursionStack', 'file' => 'cache_updater.php', 'build_event' => ''), Array ('pseudo' => 'kPermCacheUpdater', 'class' => 'kPermCacheUpdater', 'file' => 'cache_updater.php', 'build_event' => ''), ), 'ConfigPriority' => 0, 'Hooks' => Array ( Array ( 'Mode' => hAFTER, 'Conditional' => false, 'HookToPrefix' => 'adm', //self 'HookToSpecial' => '*', 'HookToEvent' => Array ('OnRebuildThemes'), 'DoPrefix' => '', 'DoSpecial' => '', 'DoEvent' => 'OnAfterRebuildThemes', ), Array ( 'Mode' => hBEFORE, 'Conditional' => false, 'HookToPrefix' => '', 'HookToSpecial' => '*', 'HookToEvent' => Array ('OnAfterConfigRead'), 'DoPrefix' => 'cdata', 'DoSpecial' => '*', 'DoEvent' => 'OnDefineCustomFields', ), Array ( 'Mode' => hBEFORE, 'Conditional' => false, 'HookToPrefix' => 'rel', 'HookToSpecial' => '*', 'HookToEvent' => Array ('OnAfterConfigRead'), 'DoPrefix' => '', 'DoSpecial' => '*', 'DoEvent' => 'OnCloneSubItem', ), Array ( 'Mode' => hBEFORE, 'Conditional' => false, 'HookToPrefix' => 'img', 'HookToSpecial' => '*', 'HookToEvent' => Array ('OnAfterConfigRead'), 'DoPrefix' => '', 'DoSpecial' => '*', 'DoEvent' => 'OnCloneSubItem', ), ), 'AutoLoad' => true, 'CatalogItem' => true, 'AdminTemplatePath' => 'categories', 'AdminTemplatePrefix' => 'categories_', 'SearchConfigPostfix' => 'category', 'QueryString' => Array ( 1 => 'id', 2 => 'Page', 3 => 'PerPage', 4 => 'event', 5 => 'mode', ), 'AggregateTags' => Array ( Array ( 'AggregateTo' => 'm', 'AggregatedTagName' => 'CategoryLink', 'LocalTagName' => 'CategoryLink', ), ), 'IDField' => 'CategoryId', 'StatusField' => Array ('Status'), // 'Status' 'TitleField' => 'Name', 'TitlePhrase' => 'la_Text_Category', 'ItemType' => 1, // used for custom fields only 'StatisticsInfo' => Array ( 'pending' => Array ( 'icon' => 'icon16_cat_pending.gif', 'label' => 'la_tab_Categories', 'js_url' => '#url#', 'url' => Array ('t' => 'catalog/advanced_view', 'SetTab' => 'c', 'pass' => 'm,c.showall', 'c.showall_event' => 'OnSetFilterPattern', 'c.showall_filters' => 'show_active=0,show_pending=1,show_disabled=0,show_new=1,show_pick=1'), 'status' => STATUS_PENDING, ), ), 'TableName' => TABLE_PREFIX.'Categories', 'CustomDataTableName' => TABLE_PREFIX . 'CategoryCustomData', 'ViewMenuPhrase' => 'la_text_Categories', 'CatalogTabIcon' => 'icon16_sections.png', 'TitlePresets' => Array ( 'default' => Array ( 'new_status_labels' => Array ('c' => '!la_title_Adding_Category!'), 'edit_status_labels' => Array ('c' => '!la_title_Editing_Category!'), 'new_titlefield' => Array ('c' => '!la_title_New_Category!'), ), 'category_list' => Array ('prefixes' => Array ('c_List'), 'format' => "!la_title_Categories! (#c_recordcount#)"), 'catalog' => Array ( 'prefixes' => Array (), 'format' => "<span id='category_path'>!la_title_Categories!</span>", 'toolbar_buttons' => Array ('select', 'cancel', 'upcat', 'homecat', 'new_cat', 'new_link', 'new_article', 'new_topic', 'new_product', 'edit', 'delete', 'new_listing', 'approve', 'decline', 'cut', 'copy', 'paste', 'move_up', 'move_down', 'tools', 'view', 'dbl-click') ), 'advanced_view' => Array ( 'prefixes' => Array (), 'format' => "!la_title_AdvancedView!", 'toolbar_buttons' => Array ('select', 'cancel', 'new_cat', 'new_link', 'new_article', 'new_topic', 'new_product', 'edit', 'delete', 'new_listing', 'approve', 'decline', 'view', 'dbl-click'), ), 'reviews' => Array ( 'prefixes' => Array (), 'format' => "!la_title_Reviews!", 'toolbar_buttons' => Array ('edit', 'delete', 'approve', 'decline', 'view', 'dbl-click',) ), 'review_edit' => Array ('prefixes' => Array (), 'format' => "!la_title_Editing_Review!"), 'categories_edit' => Array ( 'prefixes' => Array ('c'), 'format' => "#c_status# '#c_titlefield#' - !la_title_General!", ), 'categories_properties' => Array ( 'prefixes' => Array ('c'), 'format' => "#c_status# '#c_titlefield#' - !la_title_Properties!", 'toolbar_buttons' => Array ('select', 'cancel', 'prev', 'next'), ), 'categories_relations' => Array ( 'prefixes' => Array ('c'), 'format' => "#c_status# '#c_titlefield#' - !la_title_Relations!", 'toolbar_buttons' => Array ('select', 'cancel', 'prev', 'next', 'new_item', 'edit', 'delete', 'approve', 'decline', 'view', 'dbl-click'), ), 'categories_related_searches' => Array ( 'prefixes' => Array ('c'), 'format' => "#c_status# '#c_titlefield#' - !la_title_RelatedSearches!", 'toolbar_buttons' => Array ('select', 'cancel', 'prev', 'next', 'new_item', 'edit', 'delete', 'move_up', 'move_down', 'approve', 'decline', 'view', 'dbl-click'), ), 'categories_images' => Array ( 'prefixes' => Array ('c'), 'format' => "#c_status# '#c_titlefield#' - !la_title_Images!", 'toolbar_buttons' => Array ('select', 'cancel', 'prev', 'next', 'new_item', 'edit', 'delete', 'move_up', 'move_down', 'primary_image', 'view', 'dbl-click'), ), 'categories_permissions' => Array ( 'prefixes' => Array ('c'), 'format' => "#c_status# '#c_titlefield#' - !la_title_Permissions!", 'toolbar_buttons' => Array ('select', 'cancel', 'prev', 'next'), ), 'categories_custom' => Array ('prefixes' => Array ('c'), 'format' => "#c_status# '#c_titlefield#' - !la_title_Custom!"), 'categories_update' => Array ('prefixes' => Array (), 'format' => "!la_title_UpdatingCategories!"), 'images_edit' => Array ( 'prefixes' => Array ('c', 'c-img'), 'new_status_labels' => Array ('c-img' => '!la_title_Adding_Image!'), 'edit_status_labels' => Array ('c-img' => '!la_title_Editing_Image!'), 'new_titlefield' => Array ('c-img' => ''), 'format' => "#c_status# '#c_titlefield#' - #c-img_status# '#c-img_titlefield#'", 'toolbar_buttons' => Array ('select', 'cancel', 'prev', 'next'), ), 'relations_edit' => Array ( 'prefixes' => Array ('c', 'c-rel'), 'new_status_labels' => Array ('c-rel' => "!la_title_Adding_Relationship! '!la_title_New_Relationship!'"), 'edit_status_labels' => Array ('c-rel' => '!la_title_Editing_Relationship!'), 'format' => "#c_status# '#c_titlefield#' - #c-rel_status#", 'toolbar_buttons' => Array ('select', 'cancel', 'prev', 'next'), ), 'related_searches_edit' => Array ( 'prefixes' => Array ('c', 'c-search'), 'new_status_labels' => Array ('c-search' => "!la_title_Adding_RelatedSearch_Keyword!"), 'edit_status_labels' => Array ('c-search' => '!la_title_Editing_RelatedSearch_Keyword!'), 'format' => "#c_status# '#c_titlefield#' - #c-search_status#", 'toolbar_buttons' => Array ('select', 'cancel', 'prev', 'next'), ), 'edit_content' => Array ('format' => '!la_EditingContent!'), 'tree_site' => Array ('format' => '!la_selecting_categories!'), ), 'EditTabPresets' => Array ( 'Default' => Array ( 'general' => Array ('title' => 'la_tab_General', 't' => 'categories/categories_edit', 'priority' => 1), 'properties' => Array ('title' => 'la_tab_Properties', 't' => 'categories/categories_edit_properties', 'priority' => 2), 'relations' => Array ('title' => 'la_tab_Relations', 't' => 'categories/categories_edit_relations', 'priority' => 3), 'related_searches' => Array ('title' => 'la_tab_Related_Searches', 't' => 'categories/categories_edit_related_searches', 'priority' => 4), 'images' => Array ('title' => 'la_tab_Images', 't' => 'categories/categories_edit_images', 'priority' => 5), 'permissions' => Array ('title' => 'la_tab_Permissions', 't' => 'categories/categories_edit_permissions', 'priority' => 6), 'custom' => Array ('title' => 'la_tab_Custom', 't' => 'categories/categories_edit_custom', 'priority' => 7), ), ), 'PermItemPrefix' => 'CATEGORY', 'PermSection' => Array ('main' => 'CATEGORY:in-portal:categories', /*'search' => 'in-portal:configuration_search',*/ 'custom' => 'in-portal:configuration_custom'), 'Sections' => Array ( 'in-portal:configure_categories' => Array ( 'parent' => 'in-portal:website_setting_folder', 'icon' => 'conf_output', 'label' => 'la_tab_ConfigOutput', 'url' => Array ('t' => 'config/config_universal', 'pass_section' => true, 'pass' => 'm'), 'permissions' => Array ('view', 'add', 'edit'), 'priority' => 11.1, 'type' => stTREE, ), 'in-portal:configuration_search' => Array ( 'parent' => 'in-portal:website_setting_folder', 'icon' => 'conf_search', 'label' => 'la_tab_ConfigSearch', 'url' => Array ('t' => 'config/config_search', 'module_key' => 'category', 'pass_section' => true, 'pass' => 'm'), 'permissions' => Array ('view', 'edit'), 'priority' => 11.2, 'type' => stTREE, ), 'in-portal:configuration_custom' => Array ( 'parent' => 'in-portal:website_setting_folder', 'icon' => 'conf_customfields', 'label' => 'la_tab_ConfigCustom', 'url' => Array ('t' => 'custom_fields/custom_fields_list', 'cf_type' => 1, 'pass_section' => true, 'pass' => 'm,cf'), 'permissions' => Array ('view', 'add', 'edit', 'delete'), 'priority' => 11.3, 'type' => stTREE, ), ), 'FilterMenu' => Array ( 'Groups' => Array ( Array ('mode' => 'AND', 'filters' => Array ('show_new'), 'type' => kDBList::HAVING_FILTER), Array ('mode' => 'AND', 'filters' => Array ('show_pick'), 'type' => kDBList::WHERE_FILTER), ), 'Filters' => Array ( 'show_new' => Array ('label' => 'la_Text_New', 'on_sql' => '', 'off_sql' => '`IsNew` != 1'), 'show_pick' => Array ('label' => 'la_prompt_EditorsPick', 'on_sql' => '', 'off_sql' => '`EditorsPick` != 1'), ) ), 'ListSQLs' => Array ( '' => ' SELECT %1$s.* %2$s FROM %1$s LEFT JOIN '.TABLE_PREFIX.'%3$sCatalogImages img ON img.ResourceId = %1$s.ResourceId AND img.DefaultImg = 1 {PERM_JOIN} LEFT JOIN '.TABLE_PREFIX.'%3$sCategoryCustomData cust ON %1$s.ResourceId = cust.ResourceId', '-virtual' => ' SELECT %1$s.* %2$s FROM %1$s', ), 'SubItems' => Array ('c-rel', 'c-search', 'c-img', 'c-cdata', 'c-perm', 'content', 'page-revision'), 'ListSortings' => Array ( '' => Array ( 'Sorting' => Array ('Priority' => 'desc', 'Name' => 'asc'), ) ), 'CalculatedFields' => Array ( '' => Array ( 'CurrentSort' => "REPLACE(ParentPath, CONCAT('|', ".'%1$s'.".CategoryId, '|'), '')", 'AltName' => 'img.AltName', 'SameImages' => 'img.SameImages', 'LocalThumb' => 'img.LocalThumb', 'ThumbPath' => 'img.ThumbPath', 'ThumbUrl' => 'img.ThumbUrl', 'LocalImage' => 'img.LocalImage', 'LocalPath' => 'img.LocalPath', 'FullUrl' => 'img.Url', ), '-virtual' => Array (), ), 'CacheModRewrite' => true, 'Fields' => Array ( 'CategoryId' => Array ('type' => 'int', 'not_null' => 1,'default' => 0), 'Type' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_opt_Virtual', 2 => 'la_opt_Template'), 'use_phrases' => 1, 'not_null' => 1,'default' => 1 ), 'SymLinkCategoryId' => Array ('type' => 'int', 'default' => NULL), 'ParentId' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (), 'not_null' => 1,'default' => 0, 'required' => 1), 'Name' => Array ('type' => 'string', 'formatter' => 'kMultiLanguage', 'not_null' => 1, 'required' => 1, 'default' => ''), 'Filename' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''), 'AutomaticFilename' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_No', 1 => 'la_Yes'), 'use_phrases' => 1, 'default' => 1, 'not_null' => 1, ), 'Description' => Array ('type' => 'string', 'formatter' => 'kMultiLanguage', 'using_fck' => 1, 'default' => null), 'CreatedOn' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'required' => 1, 'default' => '#NOW#'), 'EditorsPick' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_No', 1 => 'la_Yes'), 'use_phrases' => 1, 'default' => 0, 'not_null' => 1, ), 'Status' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Active', 2 => 'la_Pending', 0 => 'la_Disabled' ), 'use_phrases' => 1, 'not_null' => 1,'default' => 1), 'Priority' => Array ('type' => 'int', 'not_null' => 1, 'formatter' => 'kOptionsFormatter', 'options' => Array (), 'default' => 0), 'MetaKeywords' => Array ('type' => 'string', 'formatter' => 'kFormatter', 'using_fck' => 1, 'default' => null), 'CachedDescendantCatsQty' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'CachedNavbar' => Array ('type' => 'string', 'formatter' => 'kMultiLanguage', 'default' => null), 'CreatedById' => Array ('type' => 'int', 'formatter' => 'kLEFTFormatter', 'error_msgs' => Array ('invalid_option' => '!la_error_UserNotFound!'), 'options' => Array (USER_ROOT => 'root', USER_GUEST => 'Guest'),'left_sql' => 'SELECT %s FROM '.TABLE_PREFIX.'Users WHERE %s', 'left_key_field' => 'PortalUserId', 'left_title_field' => USER_TITLE_FIELD, 'default' => NULL), 'ResourceId' => Array ('type' => 'int', 'default' => null), 'ParentPath' => Array ('type' => 'string', 'default' => null), 'TreeLeft' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'TreeRight' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'NamedParentPath' => Array ('type' => 'string', 'default' => null), 'NamedParentPathHash' => Array ('type' => 'string', 'not_null' => 1, 'default' => 0), 'MetaDescription' => Array ('type' => 'string', 'formatter' => 'kFormatter', 'using_fck' => 1, 'default' => null), 'HotItem' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (2 => 'la_Auto', 1 => 'la_Always', 0 => 'la_Never'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 2), 'NewItem' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (2 => 'la_Auto', 1 => 'la_Always', 0 => 'la_Never'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 2), 'PopItem' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (2 => 'la_Auto', 1 => 'la_Always', 0 => 'la_Never'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 2), 'Modified' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => '#NOW#'), 'ModifiedById' => Array ('type' => 'int', 'formatter' => 'kLEFTFormatter', 'error_msgs' => Array ('invalid_option' => '!la_error_UserNotFound!'), 'options' => Array (USER_ROOT => 'root', USER_GUEST => 'Guest'),'left_sql' => 'SELECT %s FROM '.TABLE_PREFIX.'Users WHERE %s', 'left_key_field' => 'PortalUserId', 'left_title_field' => USER_TITLE_FIELD, 'default' => NULL), 'CachedTemplate' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''), 'CachedTemplateHash' => Array ('type' => 'string', 'not_null' => 1, 'default' => 0), // fields from Pages 'Template' => Array ( 'type' => 'string', 'formatter' => 'kOptionsFormatter', 'options_sql' => ' SELECT CONCAT(tf.Description, " :: ", FilePath, "/", TRIM(TRAILING ".tpl" FROM FileName) ) AS Title, IF(tf.TemplateAlias <> "", tf.TemplateAlias, CONCAT(FilePath, "/", TRIM(TRAILING ".tpl" FROM FileName))) AS Value FROM ' . TABLE_PREFIX . 'ThemeFiles AS tf LEFT JOIN ' . TABLE_PREFIX . 'Themes AS t ON t.ThemeId = tf.ThemeId WHERE (t.Enabled = 1) AND (tf.FileName NOT LIKE "%%.elm.tpl") AND (tf.FileName NOT LIKE "%%.des.tpl") AND (tf.FilePath = "/designs") ORDER BY tf.Description ASC, tf.FileName ASC', 'option_key_field' => 'Value', 'option_title_field' => 'Title', 'error_msgs' => Array ( 'no_inherit' => '!la_error_NoInheritancePossible!', ), 'required' => 1, 'not_null' => 1, 'default' => CATEGORY_TEMPLATE_INHERIT ), 'UseExternalUrl' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_No', 1 => 'la_Yes'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0, ), 'ExternalUrl' => Array ('type' => 'string', 'max_len' => 255, 'not_null' => 1, 'default' => ''), 'UseMenuIconUrl' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_No', 1 => 'la_Yes'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0, ), 'MenuIconUrl' => Array ('type' => 'string', 'max_len' => 255, 'not_null' => 1, 'default' => ''), 'Title' => Array ('type' => 'string', 'formatter' => 'kMultiLanguage', 'default' => '', 'not_null'=>1), 'MenuTitle' => Array ('type' => 'string', 'formatter' => 'kMultiLanguage', 'not_null' => 1, 'default' => ''), 'MetaTitle' => Array ('type' => 'string', 'default' => null), 'IndexTools' => Array ('type' => 'string', 'formatter' => 'kFormatter', 'using_fck' => 1, 'default' => null), 'IsMenu' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Show', 0 => 'la_Hide'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 1), 'Protected' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0), 'FormId' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array ('' => ''), 'options_sql' => 'SELECT Title, FormId FROM '.TABLE_PREFIX.'Forms ORDER BY Title', 'option_key_field' => 'FormId', 'option_title_field' => 'Title', 'default' => NULL ), 'FormSubmittedTemplate' => Array ('type' => 'string', 'default' => null), 'FriendlyURL' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''), 'ThemeId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'EnablePageCache' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0 ), 'OverridePageCacheKey' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0 ), 'PageCacheKey' => Array ('type' => 'string', 'max_len' => 255, 'not_null' => 1, 'default' => ''), 'PageExpiration' => Array ('type' => 'int', 'default' => NULL), 'LiveRevisionNumber' => Array ('type' => 'int', 'not_null' => 1, 'default' => 1), 'DirectLinkEnabled' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 1 ), 'DirectLinkAuthKey' => Array ('type' => 'string', 'max_len' => 20, 'not_null' => 1, 'default' => ''), 'PromoBlockGroupId' => Array ( 'type' => 'int', 'options_sql' => 'SELECT %s FROM ' . TABLE_PREFIX . 'PromoBlockGroups ORDER BY Title', 'option_title_field' => 'Title', 'option_key_field' => 'PromoBlockGroupId', 'not_null' => 1, 'default' => 0, ), 'RequireSSL' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0 ), 'RequireLogin' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0 ), ), 'VirtualFields' => Array ( 'Relevance' => Array ('type' => 'float', 'default' => 0), 'CurrentSort' => Array ('type' => 'string', 'default' => ''), 'IsNew' => Array ('type' => 'int', 'default' => 0), 'OldPriority' => Array ('type' => 'int', 'default' => 0), // for primary image 'AltName' => Array ('type' => 'string', 'default' => ''), 'SameImages' => Array ('type' => 'string', 'default' => ''), 'LocalThumb' => Array ('type' => 'string', 'default' => ''), 'ThumbPath' => Array ('type' => 'string', 'default' => ''), 'ThumbUrl' => Array ('type' => 'string', 'default' => ''), 'LocalImage' => Array ('type' => 'string', 'default' => ''), 'LocalPath' => Array ('type' => 'string', 'default' => ''), 'FullUrl' => Array ('type' => 'string', 'default' => ''), ), 'Grids' => Array ( 'Default' => Array ( 'Icons' => Array ( // 'StatusField' => Array ('Type', 'Status', 'IsMenu'), // 'Status' 'default' => 'icon_section.png', '1_0_0' => 'icon16_section_system.png', // system '1_0_1' => 'icon16_section_system.png', // system '1_1_1' => 'icon16_section_system.png', // system '0_0_0' => 'icon16_section_disabled.png', // disabled '0_0_1' => 'icon16_section_disabled.png', // disabled '0_1_0' => 'icon16_section_menuhidden.png', // hidden from menu '0_2_0' => 'icon16_section_pending.png', // pending '0_2_1' => 'icon16_section_pending.png', // pending 'NEW' => 'icon16_section_new.png', // section is new ), 'Fields' => Array ( 'CategoryId' => Array ('title' => 'column:la_fld_Id', 'data_block' => 'grid_checkbox_td', 'filter_block' => 'grid_range_filter', 'width' => 55), 'Name' => Array ('title' => 'column:la_fld_PageTitle', 'data_block' => 'page_browse_td', 'filter_block' => 'grid_like_filter', 'width' => 250), 'Priority' => Array ('filter_block' => 'grid_options_filter', 'width' => 65), 'Modified' => Array ('filter_block' => 'grid_date_range_filter', 'width' => 170), 'Template' => Array ('title' => 'column:la_fld_TemplateType', 'filter_block' => 'grid_options_filter', 'width' => 220), 'IsMenu' => Array ('title' => 'la_col_InMenu', 'filter_block' => 'grid_options_filter', 'width' => 70), 'Type' => Array ('filter_block' => 'grid_options_filter', 'width' => 100), 'Status' => Array ('filter_block' => 'grid_options_filter', 'width' => 100), 'Protected' => Array ('filter_block' => 'grid_options_filter', 'width' => 100), 'RequireSSL' => Array ('filter_block' => 'grid_options_filter', 'width' => 100), 'RequireLogin' => Array ('filter_block' => 'grid_options_filter', 'width' => 100), ), ), 'Radio' => Array ( 'Selector' => 'radio', 'Icons' => Array ( // 'StatusField' => Array ('Type', 'Status', 'IsMenu'), // 'Status' 'default' => 'icon_section.png', '1_0_0' => 'icon16_section_system.png', // system '1_0_1' => 'icon16_section_system.png', // system '1_1_1' => 'icon16_section_system.png', // system '0_0_0' => 'icon16_section_disabled.png', // disabled '0_0_1' => 'icon16_section_disabled.png', // disabled '0_1_0' => 'icon16_section_menuhidden.png', // hidden from menu '0_2_0' => 'icon16_section_pending.png', // pending '0_2_1' => 'icon16_section_pending.png', // pending 'NEW' => 'icon16_section_new.png', // section is new ), 'Fields' => Array ( 'CategoryId' => Array ('title' => 'column:la_fld_Id', 'data_block' => 'grid_radio_td', 'filter_block' => 'grid_range_filter', 'width' => 55), 'Name' => Array ('title' => 'column:la_fld_PageTitle', 'data_block' => 'page_browse_td', 'filter_block' => 'grid_like_filter', 'width' => 250), 'Priority' => Array ('filter_block' => 'grid_options_filter', 'width' => 65), 'IsMenu' => Array ('title' => 'la_col_InMenu', 'filter_block' => 'grid_options_filter', 'width' => 70), 'Modified' => Array ('filter_block' => 'grid_date_range_filter', 'width' => 170), 'Template' => Array ('title' => 'column:la_fld_TemplateType', 'filter_block' => 'grid_options_filter', 'width' => 220), 'Type' => Array ('filter_block' => 'grid_options_filter', 'width' => 100), 'Status' => Array ('filter_block' => 'grid_options_filter', 'width' => 100), 'Protected' => Array ('filter_block' => 'grid_options_filter', 'width' => 100), 'RequireSSL' => Array ('filter_block' => 'grid_options_filter', 'width' => 100), 'RequireLogin' => Array ('filter_block' => 'grid_options_filter', 'width' => 100), ), ), 'Structure' => Array ( 'Icons' => Array ( // 'StatusField' => Array ('Type', 'Status', 'IsMenu'), // 'Status' 'default' => 'icon_section.png', '1_0_0' => 'icon16_section_system.png', // system '1_0_1' => 'icon16_section_system.png', // system '1_1_1' => 'icon16_section_system.png', // system '0_0_0' => 'icon16_section_disabled.png', // disabled '0_0_1' => 'icon16_section_disabled.png', // disabled '0_1_0' => 'icon16_section_menuhidden.png', // hidden from menu '0_2_0' => 'icon16_section_pending.png', // pending '0_2_1' => 'icon16_section_pending.png', // pending 'NEW' => 'icon16_section_new.png', // section is new ), 'Fields' => Array ( 'CategoryId' => Array ('title' => 'column:la_fld_Id', 'data_block' => 'grid_checkbox_td', 'filter_block' => 'grid_range_filter', 'width' => 55), 'Name' => Array ('title' => 'column:la_fld_PageTitle', 'data_block' => 'page_browse_td', 'filter_block' => 'grid_like_filter', 'width' => 250), 'Priority' => Array ('filter_block' => 'grid_options_filter', 'width' => 65), 'IsMenu' => Array ('title' => 'la_col_InMenu', 'filter_block' => 'grid_options_filter', 'width' => 70), 'Modified' => Array ('filter_block' => 'grid_date_range_filter', 'width' => 170), 'Template' => Array ('title' => 'column:la_fld_TemplateType', 'filter_block' => 'grid_options_filter', 'width' => 220), 'Type' => Array ('filter_block' => 'grid_options_filter', 'width' => 100), 'Status' => Array ('filter_block' => 'grid_options_filter', 'width' => 100), 'Protected' => Array ('filter_block' => 'grid_options_filter', 'width' => 100), 'RequireSSL' => Array ('filter_block' => 'grid_options_filter', 'width' => 100), 'RequireLogin' => Array ('filter_block' => 'grid_options_filter', 'width' => 100), ), ), ), 'ConfigMapping' => Array ( 'PerPage' => 'Perpage_Category', 'ShortListPerPage' => 'Perpage_Category_Short', 'DefaultSorting1Field' => 'Category_Sortfield', 'DefaultSorting2Field' => 'Category_Sortfield2', 'DefaultSorting1Dir' => 'Category_Sortorder', 'DefaultSorting2Dir' => 'Category_Sortorder2', ), ); Index: branches/5.3.x/core/units/categories/categories_tag_processor.php =================================================================== --- branches/5.3.x/core/units/categories/categories_tag_processor.php (revision 16394) +++ branches/5.3.x/core/units/categories/categories_tag_processor.php (revision 16395) @@ -1,2286 +1,2312 @@ <?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 CategoriesTagProcessor extends kDBTagProcessor { function SubCatCount($params) { $object = $this->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') { + $module_info = $category_helper->getCategoryModule($params, $category_path); + if ( $module_info ) { // 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, <prefixes_list>, 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; } /** + * Builds link to a category in the Admin Tree. + * + * @param array $params Tag params. + * + * @return string + */ + protected function AdminTreeLink(array $params) + { + $params['direct_link'] = 1; + $params['pass'] = 'm'; + $params['m_opener'] = 'r'; + + if ( $this->Application->ConfigValue('Catalog_PreselectModuleTab') ) { + $module_prefix = $this->GetModulePrefix($params); + + if ( $module_prefix ) { + $params['anchor'] = 'tab-' . $module_prefix; + } + } + + return $this->CategoryLink($params); + } + + /** * 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']; + + return $module_info ? $module_info['Var'] : 'c'; } 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 = $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 = ' <div class="cms-edit-btn-container mode-inline-edit" data-content-id="' . $content->GetID() . '"> <div class="inline-edit-layer"></div> <div class="inline-saving-layer"></div> <div class="cms-btn-content" title="' . kUtil::escape($editing_hint, kUtil::ESCAPE_HTML) . '" contenteditable="true" id="' . kUtil::escape('content_block_' . $num, kUtil::ESCAPE_HTML) . '">'; $edit_code_after = '</div></div>' . $this->FCKEditor($params); } else { $button_code = $this->Application->ProcessParsedTag($content->getPrefixSpecial(), 'AdminEditButton', $params); $edit_code_before = ' <div class="cms-edit-btn-container"> ' . $button_code . ' <div class="cms-btn-content">'; $edit_code_after = '</div></div>'; } } 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 <h1> tag) break; case 'htmlhead_title': // TODO: rename column to HtmlTitle $db_field = 'Title'; // "Title (on Page)" - in <title> 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) { + /** @var kDBItem $object */ $object = $this->getObject($params); - /* @var $object kDBItem */ - $search_results_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search'; + /** @var kSearchHelper $search_helper */ + $search_helper = $this->Application->recallObject('SearchHelper'); + + $search_results_table = $search_helper->getSearchTable(); $sql = 'SELECT Relevance - FROM '.$search_results_table.' - WHERE ResourceId = '.$object->GetDBField('ResourceId'); + 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); } } Index: branches/5.3.x/core/units/categories/categories_event_handler.php =================================================================== --- branches/5.3.x/core/units/categories/categories_event_handler.php (revision 16394) +++ branches/5.3.x/core/units/categories/categories_event_handler.php (revision 16395) @@ -1,2904 +1,2912 @@ <?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; + /** @var kSearchHelper $search_helper */ + $search_helper = $this->Application->recallObject('SearchHelper'); + 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'; + $search_sql = ' FROM ' . $search_helper->getSearchTable() . ' 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); } + $event->setEventParam(kEvent::FLAG_ID_FROM_REQUEST, true); + 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' ) { + $event->setEventParam(kEvent::FLAG_ID_FROM_REQUEST, true); + 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'); + $event->setEventParam(kEvent::FLAG_ID_FROM_REQUEST, true); } 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'); + $this->Application->UrlManager->show404(); } // 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; + // Don't allow creating records on behalf of another user. + if ( !$this->Application->isAdminUser && !defined('CRON') ) { + $object->SetDBField('CreatedById', $object->GetOriginalField('CreatedById')); + } - if ( (!$object->IsTempTable() && !$is_admin) || ($is_admin && !$object->GetDBField('CreatedById')) ) { + // Auto-assign records to currently logged-in user. + if ( !$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 ( + $section_adjustments = Array ( 'in-portal:browse' => Array ( - 'url' => Array ('m_cat_id' => $root_category), - 'late_load' => Array ('m_cat_id' => $root_category), - 'onclick' => 'checkCatalog(' . $root_category . ')', + 'url' => Array ('m_cat_id' => $root_category), + 'late_load' => Array ('m_cat_id' => $root_category), + 'onclick' => 'checkCatalog(' . $root_category . ', "c")', ), 'in-portal:browse_site' => Array ( - 'url' => Array ('editing_mode' => $settings['default_editing_mode']), + 'url' => Array ('editing_mode' => $settings['default_editing_mode']), ) - )); + ); + + if ( $this->Application->ConfigValue('Catalog_PreselectModuleTab') ) { + $section_adjustments['in-portal:browse']['url']['anchor'] = 'tab-c'; + } + + $config->addSectionAdjustments($section_adjustments); // 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 = $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 = $this->Application->unescapeRequestVariable(trim($this->Application->GetVar('keywords'))); $query_object = $this->Application->recallObject('kHTTPQuery'); /* @var $query_object kHTTPQuery */ + /** @var kSearchHelper $search_helper */ + $search_helper = $this->Application->recallObject('SearchHelper'); + + $search_table = $search_helper->getSearchTable(); $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); + $search_helper->ensureEmptySearchTable(); $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 */ - + // Keyword string processing. $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)'; }*/ if ( count($positive_words) > 1 ) { $condition = $field . ' LIKE "%' . implode(' ', $positive_words) . '%"'; $revelance_parts[] = 'IF(' . $condition . ', ' . $weight_sum . ', 0)'; } // search by partial word matches too 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'; + /** @var kSearchHelper $search_helper */ + $search_helper = $this->Application->recallObject('SearchHelper'); + + $search_table = $search_helper->getSearchTable(); $sql = 'SHOW TABLES LIKE "' . $search_table . '"'; + $ids = array(); 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, '')); } /** * 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 } } Index: branches/5.3.x/core/units/themes/themes_eh.php =================================================================== --- branches/5.3.x/core/units/themes/themes_eh.php (revision 16394) +++ branches/5.3.x/core/units/themes/themes_eh.php (revision 16395) @@ -1,258 +1,263 @@ <?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 ThemesEventHandler extends kDBEventHandler { /** * Allows to override standard permission mapping * * @return void * @access protected * @see kEventHandler::$permMapping */ protected function mapPermissions() { parent::mapPermissions(); $permissions = Array( 'OnChangeTheme' => Array('self' => 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 == '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); } /** * Ensure, that current object is always taken from live table. * * @param kDBBase|kDBItem|kDBList $object Object. * @param kEvent $event Event. * * @return void */ protected function dbBuild(&$object, kEvent $event) { if ( $event->Special == 'current' ) { $event->setEventParam('live_table', true); } parent::dbBuild($object, $event); } /** * Ensures that current theme detection will fallback to primary without extra DB query. * * @param kEvent $event Event. * * @return integer */ public function getPassedID(kEvent $event) { if ( $event->Special == 'current' ) { $theme_id = $this->Application->GetVar('m_theme'); if ( !$theme_id ) { $theme_id = 'default'; } + else { + $event->setEventParam(kEvent::FLAG_ID_FROM_REQUEST, true); + } $this->Application->SetVar('m_theme', $theme_id); $this->Application->SetVar($event->getPrefixSpecial() . '_id', $theme_id); + + return $theme_id; } return parent::getPassedID($event); } /** * Allows to set selected theme as primary * * @param kEvent $event */ function OnSetPrimary($event) { if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) { $event->status = kEvent::erFAIL; return; } $ids = $this->StoreSelectedIDs($event); if ($ids) { $id = array_shift($ids); $this->setPrimary($id); $this->Application->HandleEvent(new kEvent('adm:OnRebuildThemes')); } $this->clearSelectedIDs($event); } function setPrimary($id) { $config = $this->getUnitConfig(); $table_name = $config->getTableName(); $sql = 'UPDATE '.$table_name.' SET PrimaryTheme = 0'; $this->Conn->Query($sql); $sql = 'UPDATE '.$table_name.' SET PrimaryTheme = 1, Enabled = 1 WHERE '. $config->getIDField() .' = '.$id; $this->Conn->Query($sql); } /** * Validate entered stylesheet path. * * @param kEvent $event Event. * * @return void */ protected function OnBeforeItemUpdate(kEvent $event) { parent::OnBeforeItemUpdate($event); /** @var ThemeItem $object */ $object = $event->getObject(); if ( $object->GetDBField('StylesheetFile') && !$object->getStylesheetFile() ) { $object->SetError('StylesheetFile', 'not_found'); } } /** * Set's primary theme (when checkbox used on editing form) * * @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 kDBItem */ $object->Load($event->getEventParam('id')); if ( $object->GetDBField('PrimaryTheme') ) { $this->setPrimary($event->getEventParam('id')); } } /** * Also rebuilds theme files, when enabled theme is saved * * @param kEvent $event * @return void * @access protected */ protected function OnSave(kEvent $event) { parent::OnSave($event); if ( ($event->status != kEvent::erSUCCESS) || !$event->getEventParam('ids') ) { return ; } $config = $event->getUnitConfig(); $ids = $event->getEventParam('ids'); $sql = 'SELECT COUNT(*) FROM ' . $config->getTableName() . ' WHERE ' . $config->getIDField() . ' IN (' . $ids . ') AND (Enabled = 1)'; $enabled_themes = $this->Conn->GetOne($sql); if ( $enabled_themes ) { $this->Application->HandleEvent(new kEvent('adm:OnRebuildThemes')); } } /** * Allows to change the theme * * @param kEvent $event */ function OnChangeTheme($event) { if ($this->Application->isAdminUser) { // for structure theme dropdown $this->Application->StoreVar('theme_id', $this->Application->GetVar('theme')); $this->Application->StoreVar('RefreshStructureTree', 1); return ; } $this->Application->SetVar('t', 'index'); $this->Application->SetVar('m_cat_id', 0); $this->Application->SetVar('m_theme', $this->Application->GetVar('theme')); } /** * Apply system filter to themes 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 */ if ( in_array($event->Special, Array ('enabled', 'selected', 'available')) || !$this->Application->isAdminUser ) { // "enabled" special or Front-End $object->addFilter('enabled_filter', '%1$s.Enabled = ' . STATUS_ACTIVE); } // site domain theme 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, 'Themes'); } // apply domain-based theme filtering $themes = $this->Application->siteDomainField('Themes'); if ( strlen($themes) ) { $themes = explode('|', substr($themes, 1, -1)); $object->addFilter('domain_filter', '%1$s.ThemeId IN (' . implode(',', $themes) . ')'); } } } Index: branches/5.3.x/core/units/sections/sections_config.php =================================================================== --- branches/5.3.x/core/units/sections/sections_config.php (revision 16394) +++ branches/5.3.x/core/units/sections/sections_config.php (revision 16395) @@ -1,238 +1,238 @@ <?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' => 'core-sections', 'EventHandlerClass' => Array ('class' => 'SiteConfigEventHandler', 'file' => 'site_config_eh.php', 'build_event' => 'OnBuild'), 'TagProcessorClass' => Array ('class' => 'SiteConfigTagProcessor', 'file' => 'site_config_tp.php', 'build_event' => 'OnBuild'), 'Hooks' => Array ( Array ( 'Mode' => hBEFORE, 'Conditional' => false, 'HookToPrefix' => '*', 'HookToSpecial' => '*', 'HookToEvent' => Array ('OnAfterConfigRead'), 'DoPrefix' => '', 'DoSpecial' => '*', 'DoEvent' => 'OnApplySiteConfigChanges', ), ), 'Sections' => Array ( 'in-portal:site' => Array ( 'parent' => 'in-portal:root', 'icon' => 'struct', 'label' => 'la_tab_Site_Structure', 'url' => Array ('t' => 'index', 'pass_section' => true, 'pass' => 'm'), 'permissions' => Array ('view'), 'priority' => 1, 'container' => true, 'type' => stTREE, 'SectionPrefix' => 'c', ), 'in-portal:browse_site' => Array ( 'parent' => 'in-portal:site', 'icon' => 'browse-site', 'label' => 'la_tab_BrowsePages', 'url' => Array ('t' => 'index', 'index_file' => '../index.php', 'admin' => 1, 'pass' => 'm'), 'permissions' => Array ('view'), 'priority' => 1, 'type' => stTREE, ), 'in-portal:browse' => Array ( 'parent' => 'in-portal:site', 'icon' => 'structure', // 'catalog' 'label' => 'la_title_Structure', // 'la_tab_Browse', 'url' => Array ('t' => 'catalog/catalog', 'pass' => 'm'), 'late_load' => Array ('t' => 'categories/xml/tree_categories', 'pass' => 'm', 'm_cat_id' => 0), - 'onclick' => 'checkCatalog(0)', + 'onclick' => 'checkCatalog(0, "c")', 'permissions' => Array ('view'), 'priority' => 2, 'type' => stTREE, ), 'in-portal:reviews' => Array ( 'parent' => 'in-portal:site', 'icon' => 'reviews', 'label' => 'la_tab_Reviews', 'url' => Array ('t' => 'reviews/reviews', 'pass' => 'm'), 'permissions' => Array ('view'), 'priority' => 3.5, 'type' => stTREE, ), 'in-portal:users' => Array ( 'parent' => 'in-portal:root', 'icon' => 'user_management', 'label' => 'la_tab_Community', 'url' => Array ('t' => 'index', 'pass_section' => true, 'pass' => 'm'), 'permissions' => Array ('view'), 'priority' => 3, 'container' => true, 'type' => stTREE, 'SectionPrefix' => 'u', ), 'in-portal:user_groups' => Array ( 'parent' => 'in-portal:users', 'icon' => 'usergroups', 'label' => 'la_tab_User_Groups', 'url' => Array ('t' => 'groups/groups_list', 'pass' => 'm'), 'permissions' => Array ('view', 'add', 'edit', 'delete', 'advanced:send_email', 'advanced:manage_permissions'), 'SectionPrefix' => 'g', 'priority' => 3, 'type' => stTREE, ), // "Help" section /* 'in-portal:help' => Array ( 'parent' => 'in-portal:root', 'icon' => 'help', 'label' => 'la_tab_Help', 'url' => Array ('index_file' => 'help/manual.pdf', 'pass' => 'm'), 'permissions' => Array ('view'), 'priority' => 7, 'type' => stTREE, ), */ // "Logs & Reports" section 'in-portal:reports' => Array ( 'parent' => 'in-portal:root', 'icon' => 'summary_logs', 'label' => 'la_tab_LogsAndReports', 'url' => Array ('t' => 'index', 'pass_section' => true, 'pass' => 'm'), 'permissions' => Array ('view'), 'priority' => 4, 'container' => true, 'type' => stTREE, 'SectionPrefix' => 'adm', ), // "Configuration" section 'in-portal:system' => Array ( 'parent' => 'in-portal:root', 'icon' => 'conf', 'label' => 'la_tab_Sys_Config', 'url' => Array ('t' => 'index', 'pass_section' => true, 'pass' => 'm'), 'permissions' => Array ('view'), 'priority' => 5, 'container' => true, 'type' => stTREE, 'SectionPrefix' => 'adm', ), 'in-portal:website_setting_folder' => Array ( 'parent' => 'in-portal:system', 'icon' => 'conf_website', 'label' => 'la_title_Website', 'use_parent_header' => 1, 'url' => Array ('t' => 'index', 'pass_section' => true, 'pass' => 'm'), 'permissions' => Array ('view'), 'priority' => 1, 'container' => true, 'type' => stTREE, 'SectionPrefix' => 'adm', ), 'in-portal:configure_general' => Array ( 'parent' => 'in-portal:website_setting_folder', 'icon' => 'conf_general', 'label' => 'la_tab_General', 'url' => Array ('t' => 'config/config_universal', 'pass_section' => true, 'pass' => 'm'), 'permissions' => Array ('view', 'add', 'edit'), 'priority' => 1, 'type' => stTREE, ), 'in-portal:configure_advanced' => Array ( 'parent' => 'in-portal:website_setting_folder', 'icon' => 'conf_advanced', 'label' => 'la_title_Advanced', 'url' => Array ('t' => 'config/config_universal', 'pass_section' => true, 'pass' => 'm'), 'permissions' => Array ('view', 'add', 'edit'), 'priority' => 2, 'type' => stTREE, ), // "Tools" section 'in-portal:tools' => Array ( 'parent' => 'in-portal:root', 'icon' => 'tools', 'label' => 'la_tab_Tools', 'url' => Array ('t' => 'index', 'pass_section' => true, 'pass' => 'm'), 'permissions' => Array ('view'), 'priority' => 6, 'container' => true, 'type' => stTREE, 'SectionPrefix' => 'adm', ), 'in-portal:backup' => Array ( 'parent' => 'in-portal:tools', 'icon' => 'backup', 'label' => 'la_tab_Backup', 'url' => Array ('t' => 'tools/backup1', 'section' => 'in-portal:configure_general', 'module' => 'In-Portal', 'pass' => 'm'), 'permissions' => Array ('view'), 'priority' => 1, 'type' => stTREE, ), 'in-portal:restore' => Array ( 'parent' => 'in-portal:tools', 'icon' => 'restore', 'label' => 'la_tab_Restore', 'url' => Array ('t' => 'tools/restore1', 'pass' => 'm'), 'permissions' => Array ('view'), 'priority' => 2, 'type' => stTREE, ), 'in-portal:main_import' => Array ( 'parent' => 'in-portal:tools', 'icon' => 'import_data', 'label' => 'la_tab_ImportData', 'url' => Array ('t' => 'tools/import1'), 'permissions' => Array ('view'), 'priority' => 3, 'type' => stTREE, ), 'in-portal:sql_query' => Array ( 'parent' => 'in-portal:tools', 'icon' => 'query_database', 'label' => 'la_tab_QueryDB', 'url' => Array ('t' => 'tools/sql_query', 'pass' => 'm'), 'permissions' => Array ('view', 'edit'), 'priority' => 4, 'type' => stTREE, ), 'in-portal:server_info' => Array ( 'parent' => 'in-portal:tools', 'icon' => 'server_info', 'label' => 'la_tab_ServerInfo', 'url' => Array ('t' => 'tools/server_info', 'pass' => 'm'), 'permissions' => Array ('view'), 'priority' => 5, 'type' => stTREE, ), ), -); \ No newline at end of file +); Index: branches/5.3.x/core/units/structure/structure_eh.php =================================================================== --- branches/5.3.x/core/units/structure/structure_eh.php (revision 16394) +++ branches/5.3.x/core/units/structure/structure_eh.php (nonexistent) @@ -1,15 +0,0 @@ -<?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. -*/ - -kUtil::includeOnce(FULL_PATH . '/core/units/categories/categories_event_handler.php'); \ No newline at end of file Property changes on: branches/5.3.x/core/units/structure/structure_eh.php ___________________________________________________________________ Deleted: cvs2svn:cvs-rev ## -1 +0,0 ## -1.1.2.1 \ No newline at end of property Deleted: svn:eol-style ## -1 +0,0 ## -LF \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -Id \ No newline at end of property Index: branches/5.3.x/core/units/structure/structure_item.php =================================================================== --- branches/5.3.x/core/units/structure/structure_item.php (revision 16394) +++ branches/5.3.x/core/units/structure/structure_item.php (nonexistent) @@ -1,15 +0,0 @@ -<?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. -*/ - -kUtil::includeOnce(FULL_PATH . '/core/units/categories/categories_item.php'); \ No newline at end of file Property changes on: branches/5.3.x/core/units/structure/structure_item.php ___________________________________________________________________ Deleted: cvs2svn:cvs-rev ## -1 +0,0 ## -1.1.2.1 \ No newline at end of property Deleted: svn:eol-style ## -1 +0,0 ## -LF \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -Id \ No newline at end of property Index: branches/5.3.x/core/units/structure/structure_tp.php =================================================================== --- branches/5.3.x/core/units/structure/structure_tp.php (revision 16394) +++ branches/5.3.x/core/units/structure/structure_tp.php (nonexistent) @@ -1,15 +0,0 @@ -<?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. -*/ - -kUtil::includeOnce(FULL_PATH . '/core/units/categories/categories_tag_processor.php'); \ No newline at end of file Property changes on: branches/5.3.x/core/units/structure/structure_tp.php ___________________________________________________________________ Deleted: cvs2svn:cvs-rev ## -1 +0,0 ## -1.1.2.1 \ No newline at end of property Deleted: svn:eol-style ## -1 +0,0 ## -LF \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -Id \ No newline at end of property Index: branches/5.3.x/core/units/structure/structure_config.php =================================================================== --- branches/5.3.x/core/units/structure/structure_config.php (revision 16394) +++ branches/5.3.x/core/units/structure/structure_config.php (revision 16395) @@ -1,277 +1,277 @@ <?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' => 'st', - 'ItemClass' => Array ('class' => 'CategoriesItem', 'file' => 'structure_item.php', 'build_event' => 'OnItemBuild'), - 'ListClass' => Array ('class' => 'kDBList', 'file' => '', 'build_event' => 'OnListBuild'), - 'EventHandlerClass' => Array ('class' => 'CategoriesEventHandler', 'file' => 'structure_eh.php', 'build_event' => 'OnBuild'), - 'TagProcessorClass' => Array ('class' => 'CategoriesTagProcessor', 'file' => 'structure_tp.php', 'build_event' => 'OnBuild'), + 'ItemClass' => array('class' => 'CategoriesItem', 'file' => '../categories/categories_item.php', 'build_event' => 'OnItemBuild'), + 'ListClass' => array('class' => 'kDBList', 'file' => '', 'build_event' => 'OnListBuild'), + 'EventHandlerClass' => array('class' => 'CategoriesEventHandler', 'file' => '../categories/categories_event_handler.php', 'build_event' => 'OnBuild'), + 'TagProcessorClass' => array('class' => 'CategoriesTagProcessor', 'file' => '../categories/categories_tag_processor.php', 'build_event' => 'OnBuild'), 'AutoLoad' => true, 'QueryString' => Array ( 1 => 'id', 2 => 'Page', 3 => 'PerPage', 4 => 'event', 5 => 'mode', ), 'ConfigPriority' => 0, 'Hooks' => Array ( Array ( 'Mode' => hBEFORE, 'Conditional' => false, 'HookToPrefix' => '', 'HookToSpecial' => '*', 'HookToEvent' => Array ('OnAfterConfigRead'), 'DoPrefix' => 'cdata', 'DoSpecial' => '*', 'DoEvent' => 'OnDefineCustomFields', ), Array ( 'Mode' => hBEFORE, 'Conditional' => false, 'HookToPrefix' => 'rel', 'HookToSpecial' => '*', 'HookToEvent' => Array ('OnAfterConfigRead'), 'DoPrefix' => '', 'DoSpecial' => '*', 'DoEvent' => 'OnCloneSubItem', ), Array ( 'Mode' => hBEFORE, 'Conditional' => false, 'HookToPrefix' => 'img', 'HookToSpecial' => '*', 'HookToEvent' => Array ('OnAfterConfigRead'), 'DoPrefix' => '', 'DoSpecial' => '*', 'DoEvent' => 'OnCloneSubItem', ), ), 'IDField' => 'CategoryId', 'StatusField' => Array ('Status'), 'TitleField' => 'Name', // field, used in bluebar when editing existing item 'TableName' => TABLE_PREFIX.'Categories', 'CustomDataTableName' => TABLE_PREFIX . 'CategoryCustomData', 'TitlePresets' => Array ( 'default' => Array ( 'new_status_labels' => Array ('st' => '!la_title_Adding_Category!'), 'edit_status_labels' => Array ('st' => '!la_title_Editing_Category!'), 'new_titlefield' => Array ('st' => '!la_title_New_Category!'), ), 'structure_list' => Array ( 'prefixes' => Array ('st_List', 'st.current'), 'format' => "!la_title_Structure! - '#st.current_Name#'" ), 'structure_edit' => Array ( 'prefixes' => Array ('st'), 'format' => "#st_status# '#st_titlefield#'", ), 'edit_content' => Array ('format' => '!la_EditingContent!'), // 'categories_edit' => Array ('prefixes' => Array ('c'), 'format' => "#c_status# '#c_titlefield#' - !la_title_General!"), ), 'PermItemPrefix' => 'CATEGORY', 'PermSection' => Array ('main' => 'CATEGORY:in-portal:structure', 'email' => 'in-portal:configemail'), 'ListSQLs' => Array ( '' => ' SELECT %1$s.* %2$s FROM %1$s {PERM_JOIN}', '-virtual' => ' SELECT %1$s.* %2$s FROM %1$s', ), 'SubItems' => Array ('content', 'page-revision'), 'ListSortings' => Array ( '' => Array ( 'ForcedSorting' => Array ('Priority' => 'desc'), 'Sorting' => Array ('Name' => 'asc'), ) ), 'CalculatedFields' => Array ( '' => Array ( 'CurrentSort' => "REPLACE(ParentPath, CONCAT('|', ".'%1$s'.".CategoryId, '|'), '')", ), '-virtual' => Array (), ), 'Fields' => Array ( 'CategoryId' => Array ('type' => 'int', 'not_null' => 1,'default' => 0), 'Type' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_opt_Virtual', 2 => 'la_opt_Template'), 'use_phrases' => 1, 'not_null' => 1,'default' => 1 ), 'SymLinkCategoryId' => Array ('type' => 'int', 'default' => NULL), 'ParentId' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (), 'not_null' => 1,'default' => 0, 'required'=>1), 'Name' => Array ('type' => 'string', 'formatter' => 'kMultiLanguage', 'not_null' => 1, 'required' => 1, 'default' => ''), 'Filename' => Array ('type' => 'string', 'not_null' => 1, 'default' => '', 'required' => 1), 'AutomaticFilename' => Array ('type' => 'int', 'not_null' => 1, 'default' => 1), 'Description' => Array ('type' => 'string', 'formatter' => 'kMultiLanguage', 'default' => null), 'CreatedOn' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'required' => 1, 'default' => '#NOW#'), 'EditorsPick' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'Status' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Active', 2 => 'la_Pending', 0 => 'la_Disabled' ), 'use_phrases' => 1, 'not_null' => 1,'default' => 1), 'Priority' => Array ('type' => 'int', 'not_null' => 1, 'formatter' => 'kOptionsFormatter', 'options' => array(), 'required' => 1, 'default' => 0), 'MetaKeywords' => Array ('type' => 'string', 'default' => null), 'CachedDescendantCatsQty' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'CachedNavbar' => Array ('type' => 'string', 'formatter' => 'kMultiLanguage', 'default' => null), 'CreatedById' => Array ('type' => 'int', 'formatter' => 'kLEFTFormatter', 'options' => Array (USER_ROOT => 'root', USER_GUEST => 'Guest'),'left_sql' => 'SELECT %s FROM '.TABLE_PREFIX.'Users WHERE %s', 'left_key_field' => 'PortalUserId', 'left_title_field' => USER_TITLE_FIELD, 'default' => NULL), 'ResourceId' => Array ('type' => 'int', 'default' => null), 'ParentPath' => Array ('type' => 'string', 'default' => null), 'TreeLeft' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'TreeRight' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'NamedParentPath' => Array ('type' => 'string', 'default' => null), 'NamedParentPathHash' => Array ('type' => 'string', 'not_null' => 1, 'default' => 0), 'MetaDescription' => Array ('type' => 'string', 'default' => null), 'HotItem' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (2 => 'la_Auto', 1 => 'la_Always', 0 => 'la_Never'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 2), 'NewItem' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (2 => 'la_Auto', 1 => 'la_Always', 0 => 'la_Never'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 2), 'PopItem' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (2 => 'la_Auto', 1 => 'la_Always', 0 => 'la_Never'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 2), 'Modified' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => '#NOW#'), 'ModifiedById' => Array ('type' => 'int', 'formatter' => 'kLEFTFormatter', 'options' => Array (USER_ROOT => 'root', USER_GUEST => 'Guest'),'left_sql' => 'SELECT %s FROM '.TABLE_PREFIX.'Users WHERE %s', 'left_key_field' => 'PortalUserId', 'left_title_field' => USER_TITLE_FIELD, 'default' => NULL), 'CachedTemplate' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''), 'CachedTemplateHash' => Array ('type' => 'string', 'not_null' => 1, 'default' => 0), // fields from Pages 'Template' => Array ( 'type' => 'string', 'formatter' => 'kOptionsFormatter', 'options_sql' => ' SELECT CONCAT(tf.Description, " (", TRIM(TRAILING ".des" FROM TRIM(TRAILING ".tpl" FROM FileName) ), ")") AS Title, CONCAT(FilePath, "/", TRIM(TRAILING ".tpl" FROM FileName)) AS Value FROM ' . TABLE_PREFIX . 'ThemeFiles AS tf LEFT JOIN ' . TABLE_PREFIX . 'Themes AS t ON t.ThemeId = tf.ThemeId WHERE (t.Enabled = 1) AND (tf.FileName NOT LIKE "%%.elm.tpl") AND (tf.FileName NOT LIKE "%%.des.tpl") AND (tf.FilePath = "/designs")', 'option_key_field' => 'Value', 'option_title_field' => 'Title', 'not_null' => 1, 'default' => CATEGORY_TEMPLATE_INHERIT ), 'UseExternalUrl' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_No', 1 => 'la_Yes'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0 ), 'ExternalUrl' => Array ('type' => 'string', 'max_len' => 255, 'not_null' => 1, 'default' => ''), 'UseMenuIconUrl' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_No', 1 => 'la_Yes'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0 ), 'MenuIconUrl' => Array ('type' => 'string', 'max_len' => 255, 'not_null' => 1, 'default' => ''), 'Title' => Array ('type' => 'string', 'formatter' => 'kMultiLanguage', 'default' => '', 'not_null'=>1), 'MenuTitle' => Array ('type' => 'string', 'formatter' => 'kMultiLanguage', 'not_null' => 1, 'default' => ''), 'MetaTitle' => Array ('type' => 'string', 'default' => null), 'IndexTools' => Array ('type' => 'string', 'default' => null), 'IsMenu' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Show', 0 => 'la_Hide'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 1), 'Protected' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0), 'FormId' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => array('' => ''), 'options_sql' => 'SELECT Title, FormId FROM '.TABLE_PREFIX.'Forms ORDER BY Title', 'option_key_field' => 'FormId', 'option_title_field' => 'Title', 'default' => NULL), 'FormSubmittedTemplate' => Array ('type' => 'string', 'default' => null), 'FriendlyURL' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''), 'ThemeId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'EnablePageCache' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0 ), 'OverridePageCacheKey' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0 ), 'PageCacheKey' => Array ('type' => 'string', 'max_len' => 255, 'not_null' => 1, 'default' => ''), 'PageExpiration' => Array ('type' => 'int', 'default' => NULL), 'LiveRevisionNumber' => Array ('type' => 'int', 'not_null' => 1, 'default' => 1), 'DirectLinkEnabled' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 1 ), 'DirectLinkAuthKey' => Array ('type' => 'string', 'max_len' => 20, 'not_null' => 1, 'default' => '') ), 'VirtualFields' => Array ( 'CurrentSort' => Array ('type' => 'string', 'default' => ''), 'IsNew' => Array ('type' => 'int', 'default' => 0), 'OldPriority' => Array ('type' => 'int', 'default' => 0), ), 'Grids' => Array ( 'Default' => Array ( 'Icons' => Array (1 => 'icon16_folder.gif', 0 => 'icon16_folder-red.gif'), 'Fields' => Array ( 'CategoryId' => Array ('title' => 'column:la_fld_Id', 'data_block' => 'grid_checkbox_td', 'filter_block' => 'grid_range_filter', 'width' => 55), 'Name' => Array ('title' => 'column:la_fld_PageTitle', 'data_block' => 'page_browse_td', 'filter_block' => 'grid_like_filter', 'width' => 250), 'Modified' => Array ('filter_block' => 'grid_date_range_filter', 'width' => 170), 'Template' => Array ('title' => 'column:la_fld_TemplateType', 'filter_block' => 'grid_options_filter', 'width' => 220), 'IsMenu' => Array ('title' => 'la_col_Visible', 'filter_block' => 'grid_options_filter', 'width' => 70), 'Path' => Array ('title' => 'la_col_Path', 'data_block' => 'page_entercat_td', 'filter_block' => 'grid_like_filter' ), 'Protected' => Array ('filter_block' => 'grid_options_filter', 'width' => 100), ), ), 'Radio' => Array ( 'Icons' => Array (1 => 'icon16_folder.gif', 0 => 'icon16_folder-red.gif'), 'Selector' => 'radio', 'Fields' => Array ( 'CategoryId' => Array ('title' => 'column:la_fld_Id', 'data_block' => 'grid_radio_td', 'filter_block' => 'grid_range_filter', 'width' => 55), 'Name' => Array ('title' => 'column:la_fld_PageTitle', 'data_block' => 'page_browse_td', 'filter_block' => 'grid_like_filter', 'width' => 250), 'Modified' => Array ('filter_block' => 'grid_date_range_filter', 'width' => 170), 'Template' => Array ('title' => 'column:la_fld_TemplateType', 'filter_block' => 'grid_options_filter', 'width' => 220), 'IsMenu' => Array ('title' => 'la_col_Visible', 'filter_block' => 'grid_options_filter', 'width' => 70), 'Path' => Array ('title' => 'la_col_Path', 'data_block' => 'page_entercat_td', 'filter_block' => 'grid_like_filter' ), 'Protected' => Array ('filter_block' => 'grid_options_filter', 'width' => 100), ), ), 'AllPages' => Array ( 'Icons' => Array (1 => 'icon16_folder.gif', 0 => 'icon16_folder-red.gif'), 'Fields' => Array ( 'CategoryId' => Array ('title' => 'column:la_fld_Id', 'data_block' => 'grid_checkbox_td', 'filter_block' => 'grid_range_filter', 'width' => 55), 'Name' => Array ('title' => 'column:la_fld_PageTitle', 'data_block' => 'page_browse_td', 'filter_block' => 'grid_like_filter', 'width' => 250), 'Path' => Array ('title' => 'la_col_Path', 'data_block' => 'page_path_td', 'sort_field' => 'CachedNavbar', 'filter_block' => 'grid_like_filter' ), 'Modified' => Array ('filter_block' => 'grid_date_range_filter', 'width' => 170), 'Template' => Array ('title' => 'column:la_fld_TemplateType', 'filter_block' => 'grid_options_filter', 'width' => 220), 'IsMenu' => Array ('title' => 'la_col_Visible', 'filter_block' => 'grid_options_filter', 'width' => 70), 'Protected' => Array ('filter_block' => 'grid_options_filter', 'width' => 100), ), ), ), 'ConfigMapping' => Array ( 'PerPage' => 'Perpage_Category', 'ShortListPerPage' => 'Perpage_Category_Short', 'DefaultSorting1Field' => 'Category_Sortfield', 'DefaultSorting2Field' => 'Category_Sortfield2', 'DefaultSorting1Dir' => 'Category_Sortorder', 'DefaultSorting2Dir' => 'Category_Sortorder2', ), ); Index: branches/5.3.x/core/units/users/users_tag_processor.php =================================================================== --- branches/5.3.x/core/units/users/users_tag_processor.php (revision 16394) +++ branches/5.3.x/core/units/users/users_tag_processor.php (revision 16395) @@ -1,377 +1,378 @@ <?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->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 + * Returns user title * - * @param Array $params + * @param array $params Parameters. * @return string * @access protected */ - protected function UserSelectorTitle($params) + protected function UserTitle(array $params) { $object = $this->getObject($params); /* @var $object kDBItem */ return $object->GetDBField('Email') ? $object->GetDBField('Email') : $object->GetDBField('Username'); } + } Index: branches/5.3.x/core/units/users/users_event_handler.php =================================================================== --- branches/5.3.x/core/units/users/users_event_handler.php (revision 16394) +++ branches/5.3.x/core/units/users/users_event_handler.php (revision 16395) @@ -1,1967 +1,1971 @@ <?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); } /** * 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(); + /** @var SessionStorage $session_storage */ + $session_storage = $this->Application->recallObject('SessionStorage'); + $session_storage->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 */ $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'); } /** * [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); $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 */ $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->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 */ $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 */ $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(); /** @var UsersItem $user */ $user = $this->Application->recallObject('u', null, array('skip_autoload' => true)); foreach ($records as $record) { $user->Load($record['PortalUserId']); $this->Application->emailUser( 'USER.MEMBERSHIP.EXPIRATION.NOTICE', $record['PortalUserId'], $user->getEmailParams() ); $this->Application->emailAdmin('USER.MEMBERSHIP.EXPIRATION.NOTICE', null, $user->getEmailParams()); $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) { /** @var UsersItem $user */ $user = $this->Application->recallObject('u', null, array('skip_autoload' => true)); foreach ($user_ids as $id) { $user->Load($id); $this->Application->emailUser('USER.MEMBERSHIP.EXPIRED', $id, $user->getEmailParams()); $this->Application->emailAdmin('USER.MEMBERSHIP.EXPIRED', null, $user->getEmailParams()); } } $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->IgnoreValidation = true; $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'); + if ( $id ) { + $event->setEventParam(kEvent::FLAG_ID_FROM_REQUEST, true); + + return $id; } - return $id; + // If none user_id given use current user id. + return $this->Application->RecallVar('user_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->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 { /** @var kDBItem $object */ $object = $event->getObject(); $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_config.php =================================================================== --- branches/5.3.x/core/units/users/users_config.php (revision 16394) +++ branches/5.3.x/core/units/users/users_config.php (revision 16395) @@ -1,663 +1,664 @@ <?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' => 'u', 'ItemClass' => Array ('class' => 'UsersItem', 'file' => 'users_item.php', 'build_event' => 'OnItemBuild'), 'ListClass' => Array ('class' => 'kDBList', 'file' => '', 'build_event' => 'OnListBuild'), 'EventHandlerClass' => Array ('class' => 'UsersEventHandler', 'file' => 'users_event_handler.php', 'build_event' => 'OnBuild'), 'TagProcessorClass' => Array ('class' => 'UsersTagProcessor', 'file' => 'users_tag_processor.php', 'build_event' => 'OnBuild'), 'RegisterClasses' => Array ( - Array ('pseudo' => 'UsersSyncronizeManager', 'class' => 'UsersSyncronizeManager', 'file' => 'users_syncronize.php', 'build_event' => ''), + Array('pseudo' => 'UsersSyncronizeManager', 'class' => 'UsersSyncronizeManager', 'file' => 'users_syncronize.php', 'build_event' => ''), + Array('pseudo' => 'UsersSyncronize', 'class' => 'UsersSyncronize', 'file' => 'users_syncronize.php', 'build_event' => ''), ), 'AutoLoad' => true, 'ConfigPriority' => 0, 'Hooks' => Array ( Array ( 'Mode' => hBEFORE, 'Conditional' => false, 'HookToPrefix' => '', 'HookToSpecial' => '*', 'HookToEvent' => Array ('OnAfterConfigRead'), 'DoPrefix' => 'cdata', 'DoSpecial' => '*', 'DoEvent' => 'OnDefineCustomFields', ), Array ( 'Mode' => hAFTER, 'Conditional' => false, 'HookToPrefix' => 'adm', 'HookToSpecial' => '*', 'HookToEvent' => Array ('OnStartup'), 'DoPrefix' => '', 'DoSpecial' => '*', 'DoEvent' => 'OnAutoLoginUser', ), Array ( 'Mode' => hBEFORE, 'Conditional' => false, 'HookToPrefix' => 'img', 'HookToSpecial' => '*', 'HookToEvent' => Array ('OnAfterConfigRead'), 'DoPrefix' => '', 'DoSpecial' => '*', 'DoEvent' => 'OnCloneSubItem', ), // Captcha processing Array ( 'Mode' => hBEFORE, 'Conditional' => false, 'HookToPrefix' => '', 'HookToSpecial' => '*', 'HookToEvent' => Array ('OnAfterConfigRead'), 'DoPrefix' => 'captcha', 'DoSpecial' => '*', 'DoEvent' => 'OnPrepareCaptcha', ), /*Array ( 'Mode' => hAFTER, 'Conditional' => false, 'HookToPrefix' => '', 'HookToSpecial' => '*', 'HookToEvent' => Array ('OnBeforeItemCreate'), 'DoPrefix' => 'captcha', 'DoSpecial' => '*', 'DoEvent' => 'OnValidateCode', ),*/ ), 'QueryString' => Array ( 1 => 'id', 2 => 'Page', 3 => 'PerPage', 4 => 'event', 5 => 'mode', ), 'ScheduledTasks' => Array ( 'membership_expiration' => Array ('EventName' => 'OnCheckExpiredMembership', 'RunSchedule' => '*/30 * * * *'), 'delete_expired_sessions' => Array ('EventName' => 'OnDeleteExpiredSessions', 'RunSchedule' => '0 */12 * * *'), ), 'IDField' => 'PortalUserId', 'StatusField' => Array ('Status'), 'TitleField' => 'Username', 'ItemType' => 6, // used for custom fields only (on user's case) 'StatisticsInfo' => Array ( 'pending' => Array ( 'icon' => 'icon16_user_pending.gif', 'label' => 'la_Text_Users', 'js_url' => '#url#', 'url' => Array ('t' => 'users/users_list', 'pass' => 'm,u', 'u_event' => 'OnSetFilterPattern', 'u_filters' => 'show_active=0,show_pending=1,show_disabled=0'), 'status' => STATUS_PENDING, ), ), 'TitlePresets' => Array ( 'default' => Array ( 'new_status_labels' => Array ('u' => '!la_title_Adding_User!'), 'edit_status_labels' => Array ('u' => '!la_title_Editing_User!'), ), 'users_list' => Array ( 'prefixes' => Array ('u_List'), 'format' => "!la_title_Users!", 'toolbar_buttons' => Array ('new_item', 'edit', 'delete', 'setprimary', 'approve', 'decline', 'frontend_mail', 'e-mail', 'export', 'view', 'dbl-click'), ), 'users_edit' => Array ( 'prefixes' => Array ('u'), 'format' => "#u_status# #u_titlefield# - !la_title_General!", 'toolbar_buttons' => Array ('select', 'cancel', 'reset_edit', 'prev', 'next'), ), 'user_edit_images' => Array ( 'prefixes' => Array ('u', 'u-img_List'), 'format' => "#u_status# '#u_titlefield#' - !la_title_Images!", 'toolbar_buttons' => Array ('select', 'cancel', 'prev', 'next', 'new_item', 'edit', 'delete', 'approve', 'decline', 'setprimary', 'move_up', 'move_down', 'view', 'dbl-click'), ), 'user_edit_groups' => Array ( 'prefixes' => Array ('u', 'u-ug_List'), 'format' => "#u_status# '#u_titlefield#' - !la_title_Groups!", 'toolbar_buttons' => Array ('select', 'cancel', 'prev', 'next', 'select_user', 'edit', 'delete', 'setprimary', 'view', 'dbl-click'), ), 'user_edit_items' => Array ( 'prefixes' => Array ('u'), 'format' => "#u_status# '#u_titlefield#' - !la_title_Items!", 'toolbar_buttons' => Array ('select', 'cancel', 'prev', 'next', 'edit', 'delete', 'view', 'dbl-click'), ), 'user_edit_custom' => Array ( 'prefixes' => Array ('u'), 'format' => "#u_status# '#u_titlefield#' - !la_title_Custom!", 'toolbar_buttons' => Array ('select', 'cancel', 'prev', 'next'), ), 'admin_list' => Array ( 'prefixes' => Array ('u.admins_List'), 'format' => "!la_title_Administrators!", 'toolbar_buttons' => Array ('new_item', 'edit', 'delete', 'approve', 'decline', 'clone', 'refresh', 'view', 'dbl-click'), ), 'admins_edit' => Array ( 'new_status_labels' => Array ('u' => '!la_title_AddingAdministrator!'), 'edit_status_labels' => Array ('u' => '!la_title_EditingAdministrator!'), 'prefixes' => Array ('u'), 'format' => "#u_status# #u_titlefield#", 'toolbar_buttons' => Array ('select', 'cancel', 'reset_edit', 'prev', 'next'), ), 'regular_users_list' => Array ( 'prefixes' => Array ('u.regular_List'), 'format' => "!la_title_Users!", 'toolbar_buttons' => Array (), ), 'root_edit' => Array ( 'prefixes' => Array ('u'), 'format' => "!la_title_Editing_User! 'root'", 'toolbar_buttons' => Array ('select', 'cancel'), ), 'user_edit_group' => Array ( 'prefixes' => Array ('u', 'u-ug'), 'edit_status_labels' => Array ('u-ug' => '!la_title_EditingMembership!'), 'format' => "#u_status# '#u_titlefield#' - #u-ug_status# '#u-ug_titlefield#'", 'toolbar_buttons' => Array ('select', 'cancel', 'prev', 'next'), ), 'user_image_edit' => Array ( 'prefixes' => Array ('u', 'u-img'), 'new_status_labels' => Array ('u-img' => '!la_title_Adding_Image!'), 'edit_status_labels' => Array ('u-img' => '!la_title_Editing_Image!'), 'new_titlefield' => Array ('u-img' => '!la_title_New_Image!'), 'format' => "#u_status# '#u_titlefield#' - #u-img_status# '#u-img_titlefield#'", 'toolbar_buttons' => Array ('select', 'cancel', 'prev', 'next'), ), 'user_select' => Array ( 'prefixes' => Array ('u_List'), 'format' => "!la_title_Users! - !la_title_SelectUser!", 'toolbar_buttons' => Array ('select', 'cancel', 'dbl-click'), ), 'group_user_select' => Array ( 'prefixes' => Array ('u.group_List'), 'format' => "!la_title_Users! - !la_title_SelectUser!", 'toolbar_buttons' => Array ('select', 'cancel', 'view', 'dbl-click'), ), 'tree_users' => Array ('format' => '!la_section_overview!'), ), 'EditTabPresets' => Array ( 'Default' => Array ( 'general' => Array ('title' => 'la_tab_General', 't' => 'users/users_edit', 'priority' => 1), 'groups' => Array ('title' => 'la_tab_Groups', 't' => 'users/users_edit_groups', 'priority' => 2), 'images' => Array ('title' => 'la_tab_Images', 't' => 'users/user_edit_images', 'priority' => 3), 'items' => Array ('title' => 'la_tab_Items', 't' => 'users/user_edit_items', 'priority' => 4), 'custom' => Array ('title' => 'la_tab_Custom', 't' => 'users/users_edit_custom', 'priority' => 5), ), 'Admins' => Array ( 'general' => Array ('title' => 'la_tab_General', 't' => 'users/admins_edit', 'priority' => 1), 'groups' => Array ('title' => 'la_tab_Groups', 't' => 'users/admins_edit_groups', 'priority' => 2), ), ), 'PermSection' => Array ('main' => 'in-portal:user_list', 'custom' => 'in-portal:user_custom'), 'Sections' => Array ( 'in-portal:user_list' => Array ( 'parent' => 'in-portal:users', 'icon' => 'users', 'label' => 'la_title_Users', // 'la_tab_User_List', 'url' => Array ('t' => 'users/users_list', 'pass' => 'm'), 'permissions' => Array ('view', 'add', 'edit', 'delete', 'advanced:ban', 'advanced:send_email', /*'advanced:add_favorite', 'advanced:remove_favorite',*/), 'priority' => 1, 'type' => stTREE, ), 'in-portal:admins' => Array ( 'parent' => 'in-portal:users', 'icon' => 'administrators', 'label' => 'la_title_Administrators', 'url' => Array ('t' => 'users/admins_list', 'pass' => 'm'), 'permissions' => Array ('view', 'add', 'edit', 'delete'), 'perm_prefix' => 'u', 'priority' => 2, 'type' => stTREE, ), // user settings 'in-portal:user_setting_folder' => Array ( 'parent' => 'in-portal:system', 'icon' => 'conf_users', 'label' => 'la_title_Users', 'use_parent_header' => 1, 'url' => Array ('t' => 'index', 'pass_section' => true, 'pass' => 'm'), 'permissions' => Array ('view'), 'priority' => 2, 'container' => true, 'type' => stTREE, ), 'in-portal:configure_users' => Array ( 'parent' => 'in-portal:user_setting_folder', 'icon' => 'conf_users_general', 'label' => 'la_tab_ConfigSettings', 'url' => Array ('t' => 'config/config_universal', 'module' => 'In-Portal:Users', 'pass_section' => true, 'pass' => 'm'), 'permissions' => Array ('view', 'add', 'edit'), 'priority' => 1, 'type' => stTREE, ), 'in-portal:user_custom' => Array ( 'parent' => 'in-portal:user_setting_folder', 'icon' => 'conf_customfields', 'label' => 'la_tab_ConfigCustom', 'url' => Array ('t' => 'custom_fields/custom_fields_list', 'cf_type' => 6, 'pass_section' => true, 'pass' => 'm,cf'), 'permissions' => Array ('view', 'add', 'edit', 'delete'), 'priority' => 2, 'type' => stTREE, ), ), 'TableName' => TABLE_PREFIX.'Users', 'CustomDataTableName' => TABLE_PREFIX . 'UserCustomData', 'ListSQLs' => Array ( '' => ' SELECT %1$s.* %2$s FROM %1$s LEFT JOIN '.TABLE_PREFIX.'UserGroups g ON %1$s.PrimaryGroupId = g.GroupId LEFT JOIN '.TABLE_PREFIX.'%3$sUserCustomData cust ON %1$s.ResourceId = cust.ResourceId LEFT JOIN '.TABLE_PREFIX.'%3$sCatalogImages img ON img.ResourceId = %1$s.ResourceId AND img.DefaultImg = 1', 'online' => ' SELECT %1$s.* %2$s FROM %1$s LEFT JOIN '.TABLE_PREFIX.'UserSessions s ON s.PortalUserId = %1$s.PortalUserId LEFT JOIN '.TABLE_PREFIX.'UserGroups g ON %1$s.PrimaryGroupId = g.GroupId LEFT JOIN '.TABLE_PREFIX.'%3$sUserCustomData cust ON %1$s.ResourceId = cust.ResourceId LEFT JOIN '.TABLE_PREFIX.'%3$sCatalogImages img ON img.ResourceId = %1$s.ResourceId AND img.DefaultImg = 1', ), 'ListSortings' => Array ( '' => Array ( 'Sorting' => Array ('Username' => 'asc'), ) ), 'SubItems' => Array ('addr', 'u-cdata', 'u-ug', 'u-img', 'fav', 'user-profile'), /** * Required for depricated public profile templates to work */ 'UserProfileMapping' => Array ( 'pp_firstname' => 'FirstName', 'pp_lastname' => 'LastName', 'pp_dob' => 'dob', 'pp_email' => 'Email', 'pp_phone' => 'Phone', 'pp_street' => 'Street', 'pp_city' => 'City', 'pp_state' => 'State', 'pp_zip' => 'Zip', 'pp_country' => 'Country', ), 'CalculatedFields' => Array ( '' => Array ( 'PrimaryGroup' => 'g.Name', 'FullName' => 'CONCAT(%1$s.FirstName, " ", %1$s.LastName)', 'AltName' => 'img.AltName', 'SameImages' => 'img.SameImages', 'LocalThumb' => 'img.LocalThumb', 'ThumbPath' => 'img.ThumbPath', 'ThumbUrl' => 'img.ThumbUrl', 'LocalImage' => 'img.LocalImage', 'LocalPath' => 'img.LocalPath', 'FullUrl' => 'img.Url', ), ), 'Forms' => Array ( 'default' => Array ( 'Fields' => Array ( 'PortalUserId' => Array ('default' => 0), 'Username' => Array ( 'max_len' => 255, 'formatter' => 'kFormatter', 'regexp' => '/^[A-Z\d_\-\.]+$/i', 'error_msgs' => Array ( 'unique' => '!lu_user_already_exist!', 'invalid_format' => '!la_error_InvalidLogin!', 'banned' => '!la_error_UserBanned!' ), 'unique' => Array (), 'default' => '', ), 'Password' => Array ( 'formatter' => 'kPasswordFormatter', 'hashing_method_field' => 'PasswordHashingMethod', 'verify_field' => 'VerifyPassword', 'default' => '' ), 'PasswordHashingMethod' => Array ( 'formatter' => 'kOptionsFormatter', 'options' => Array ( PasswordHashingMethod::MD5 => 'md5', PasswordHashingMethod::MD5_AND_PHPPASS => 'md5+phppass', PasswordHashingMethod::PHPPASS => 'phppass' ), 'default' => PasswordHashingMethod::PHPPASS ), 'FirstName' => Array ('default' => ''), 'LastName' => Array ('default' => ''), 'Company' => Array ('default' => ''), 'Email' => Array ( 'formatter' => 'kFormatter', 'regexp' => '/^(' . REGEX_EMAIL_USER . '@' . REGEX_EMAIL_DOMAIN . ')$/i', 'sample_value' => 'email@domain.com', 'error_msgs' => Array ( 'invalid_format' => '!la_invalid_email!', 'unique' => '!lu_email_already_exist!' ), 'unique' => Array (), 'required' => 1, 'default' => '' ), 'CreatedOn' => Array ('formatter' => 'kDateFormatter', 'default' => '#NOW#'), 'Phone' => Array ('default' => ''), 'Fax' => Array ('default' => ''), 'Street' => Array ('default' => ''), 'Street2' => Array ('error_field' => 'Street', 'default' => ''), 'City' => Array ('default' => ''), 'State' => Array ( 'formatter' => 'kOptionsFormatter', 'options' => Array (), 'default' => '', ), 'Zip' => Array ('default' => ''), 'Country' => Array ( 'formatter' => 'kOptionsFormatter', 'options_sql' => ' SELECT IF(l%2$s_Name = "", l%3$s_Name, l%2$s_Name) AS Name, IsoCode FROM ' . TABLE_PREFIX . 'CountryStates WHERE Type = ' . DESTINATION_TYPE_COUNTRY . ' ORDER BY Name', 'option_key_field' => 'IsoCode', 'option_title_field' => 'Name', 'default' => '', ), 'ResourceId' => Array ('default' => 0), 'Status' => Array ( 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Enabled', 0 => 'la_Disabled', 2 => 'la_Pending'), 'use_phrases' => 1, 'default' => 1 ), 'Modified' => Array ('formatter' => 'kDateFormatter', 'default' => NULL), 'dob' => Array ('formatter' => 'kDateFormatter', 'default' => NULL), 'TimeZone' => Array ('default' => ''), 'IPAddress' => Array ('default' => ''), 'IsBanned' => Array ('default' => 0), 'PwResetConfirm' => Array ('default' => ''), 'PwRequestTime' => Array ('formatter' => 'kDateFormatter', 'default' => NULL), 'FrontLanguage' => Array ( 'formatter' => 'kOptionsFormatter', 'options_sql' => 'SELECT %s FROM ' . TABLE_PREFIX . 'Languages ORDER BY PackName', 'option_key_field' => 'LanguageId', 'option_title_field' => 'LocalName', 'default' => NULL ), 'AdminLanguage' => Array ( 'formatter' => 'kOptionsFormatter', 'options_sql' => 'SELECT %s FROM ' . TABLE_PREFIX . 'Languages ORDER BY PackName', 'option_key_field' => 'LanguageId', 'option_title_field' => 'LocalName', 'default' => NULL ), 'DisplayToPublic' => Array ( 'formatter' => 'kOptionsFormatter', 'options' => Array ( 'FirstName' => 'lu_fld_FirstName', 'LastName' => 'lu_fld_LastName', 'dob' => 'lu_fld_BirthDate', 'Email' => 'lu_fld_Email', 'Phone' => 'lu_fld_Phone', 'Street' => 'lu_fld_AddressLine1', 'Street2' => 'lu_fld_AddressLine2', 'City' => 'lu_fld_City', 'State' => 'lu_fld_State', 'Zip' => 'lu_fld_Zip', 'Country' => 'lu_fld_Country', ), 'use_phrases' => 1, 'multiple' => 1, 'default' => NULL ), 'UserType' => Array ( 'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_opt_UserTypeUser', 1 => 'la_opt_UserTypeAdmin'), 'use_phrases' => 1, 'default' => 0 ), 'PrimaryGroupId' => Array ( 'formatter' => 'kOptionsFormatter', 'options_sql' => 'SELECT %1$s FROM ' . TABLE_PREFIX . 'UserGroups WHERE Enabled = 1 AND FrontRegistration = 1', 'option_key_field' => 'GroupId', 'option_title_field' => 'Name', 'default' => NULL ), 'OldStyleLogin' => Array ( 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1, 'default' => 0 ), 'IPRestrictions' => Array ('default' => NULL), 'EmailVerified' => Array ( 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1, 'default' => 0 ), 'PrevEmails' => Array ('default' => NULL), ), 'VirtualFields' => Array ( 'PrimaryGroup' => Array ('default' => ''), 'RootPassword' => Array ( 'formatter' => 'kPasswordFormatter', 'hashing_method' => PasswordHashingMethod::PHPPASS, 'salt' => 'b38', 'verify_field' => 'VerifyRootPassword', 'default' => 'b38:d41d8cd98f00b204e9800998ecf8427e' ), 'EmailPassword' => Array ('default' => ''), 'FullName' => Array ('default' => ''), 'AltName' => Array ('default' => ''), 'SameImages' => Array ('default' => ''), 'LocalThumb' => Array ('default' => ''), 'ThumbPath' => Array ('default' => ''), 'ThumbUrl' => Array ('default' => ''), 'LocalImage' => Array ('default' => ''), 'LocalPath' => Array ('default' => ''), 'FullUrl' => Array ('default' => ''), 'SubscribeToMailing' => Array ( 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'lu_Yes', 0 => 'lu_No'), 'use_phrases' => 1, 'default' => 0, ), ), ), 'registration' => Array ( // Front-End user registration form /*'Fields' => Array ( 'FirstName' => Array ('required' => 1), ),*/ ), 'recommend' => Array ( 'VirtualFields' => Array ( 'RecommendEmail' => Array ( 'type' => 'string', 'formatter' => 'kFormatter', 'regexp' => '/^(' . REGEX_EMAIL_USER . '@' . REGEX_EMAIL_DOMAIN . ')$/i', 'error_msgs' => Array ('required' => '!lu_InvalidEmail!', 'invalid_format' => '!lu_InvalidEmail!', 'send_error' => '!lu_email_send_error!'), 'sample_value' => 'email@domain.com', 'required' => 1, 'default' => '' ), ), ), 'subscription' => Array ( 'VirtualFields' => Array ( 'SubscriberEmail' => Array ( 'type' => 'string', 'formatter' => 'kFormatter', 'regexp' => '/^(' . REGEX_EMAIL_USER . '@' . REGEX_EMAIL_DOMAIN . ')$/i', 'error_msgs' => Array ('required' => '!lu_InvalidEmail!', 'invalid_format' => '!lu_InvalidEmail!'), 'sample_value' => 'email@domain.com', 'required' => 1, 'default' => '' ), ), ), 'forgot_password' => Array ( 'VirtualFields' => Array ( 'ForgotLogin' => Array ( 'type' => 'string', 'error_msgs' => Array ( 'required' => '!lu_ferror_forgotpw_nodata!', 'unknown_username' => '!lu_ferror_unknown_username!', 'unknown_email' => '!lu_ferror_unknown_email!', 'reset_denied' => '!lu_ferror_reset_denied!', ), 'required' => 1, 'default' => '' ), ), ), 'login' => Array ( 'Fields' => Array ( // make sure all fields, required by default are not required on this form 'Email' => Array ('required' => 0), ), 'VirtualFields' => Array ( 'UserLogin' => Array ( 'type' => 'string', 'error_msgs' => Array ( 'no_permission' => '!la_no_permissions!', 'invalid_password' => '!la_invalid_password!' ), 'default' => '' ), 'UserPassword' => Array ('type' => 'string', 'default' => ''), 'UserRememberLogin' => Array ('type' => 'int', 'default' => 0), ), ), ), 'Fields' => Array ( 'PortalUserId' => Array ('type' => 'int', 'not_null' => 1), 'Username' => Array ('type' => 'string', 'not_null' => 1), 'Password' => Array ('type' => 'string', 'skip_empty' => 1), 'PasswordHashingMethod' => Array ('type' => 'int'), 'FirstName' => Array ('type' => 'string', 'not_null' => 1), 'LastName' => Array ('type' => 'string', 'not_null' => 1), 'Company' => Array ('type' => 'string', 'not_null' => 1), 'Email' => Array ('type' => 'string', 'not_null' => 1), 'CreatedOn' => Array ('type' => 'int'), 'Phone' => Array ('type' => 'string', 'not_null' => 1), 'Fax' => Array ('type' => 'string', 'not_null' => 1), 'Street' => Array ('type' => 'string', 'not_null' => 1), 'Street2' => Array ('type' => 'string', 'not_null' => 1), 'City' => Array ('type' => 'string', 'not_null' => 1), 'State' => Array ('type' => 'string', 'not_null' => 1), 'Zip' => Array ('type' => 'string', 'not_null' => 1), 'Country' => Array ('type' => 'string', 'not_null' => 1), 'ResourceId' => Array ('type' => 'int', 'not_null' => 1), 'Status' => Array ('type' => 'int', 'not_null' => 1), 'Modified' => Array ('type' => 'int'), 'dob' => Array ('type' => 'int'), 'TimeZone' => Array ('type' => 'string', 'not_null' => 1), 'IPAddress' => Array ('type' => 'string', 'not_null' => 1), 'IsBanned' => Array ('type' => 'int', 'not_null' => 1), 'PwResetConfirm' => Array ('type' => 'string', 'not_null' => 1), 'PwRequestTime' => Array ('type' => 'int'), 'FrontLanguage' => Array ('type' => 'int'), 'AdminLanguage' => Array ('type' => 'int'), 'DisplayToPublic' => Array ('type' => 'string'), 'UserType' => Array ('type' => 'int', 'not_null' => 1), 'PrimaryGroupId' => Array ('type' => 'int'), 'OldStyleLogin' => Array ('type' => 'int', 'not_null' => 1), 'IPRestrictions' => Array ('type' => 'string'), 'EmailVerified' => Array ('type' => 'int', 'not_null' => 1), 'PrevEmails' => Array ('type' => 'string'), ), 'VirtualFields' => Array ( 'PrimaryGroup' => Array ('type' => 'string'), 'RootPassword' => Array ('type' => 'string', 'skip_empty' => 1), 'EmailPassword' => Array ('type' => 'string'), 'FullName' => Array ('type' => 'string'), 'AltName' => Array ('type' => 'string'), 'SameImages' => Array ('type' => 'string'), 'LocalThumb' => Array ('type' => 'string'), 'ThumbPath' => Array ('type' => 'string'), 'ThumbUrl' => Array ('type' => 'string'), 'LocalImage' => Array ('type' => 'string'), 'LocalPath' => Array ('type' => 'string'), 'FullUrl' => Array ('type' => 'string'), 'SubscribeToMailing' => Array ('type' => 'int'), ), 'Grids' => Array ( // not in use 'Default' => Array ( 'Icons' => Array ( 0 => 'icon16_user_disabled.png', 1 => 'icon16_user.png', 2 => 'icon16_user_pending.png' ), 'Fields' => Array ( 'Username' => Array ('data_block' => 'grid_checkbox_td', 'filter_block' => 'grid_like_filter'), 'LastName' => Array ('filter_block' => 'grid_like_filter'), 'FirstName' => Array ('filter_block' => 'grid_like_filter'), 'Email' => Array ('filter_block' => 'grid_like_filter'), 'PrimaryGroup' => Array ('title' => 'la_col_PrimaryGroup', 'filter_block' => 'grid_like_filter'), 'CreatedOn' => Array ('filter_block' => 'grid_date_range_filter'), 'Modified' => Array ('filter_block' => 'grid_date_range_filter'), 'Status' => Array ('filter_block' => 'grid_options_filter', 'width' => 100, ), 'IPAddress' => Array ('filter_block' => 'grid_like_filter', 'width' => 100, 'hidden' => 1), ), ), // used 'UserSelector' => Array ( 'Icons' => Array ( 0 => 'icon16_user_disabled.png', 1 => 'icon16_user.png', 2 => 'icon16_user_pending.png' ), 'Selector' => 'radio', 'Fields' => Array ( 'Username' => Array ('data_block' => 'grid_login_td', 'filter_block' => 'grid_like_filter', 'width' => 150, ), 'FirstName' => Array ('filter_block' => 'grid_like_filter', 'width' => 150, ), 'LastName' => Array ('filter_block' => 'grid_like_filter', 'width' => 150, ), 'Email' => Array ('filter_block' => 'grid_like_filter', 'width' => 200, ), 'PrimaryGroup' => Array ('title' => 'la_col_PrimaryGroup', 'filter_block' => 'grid_like_filter', 'width' => 100, ), 'CreatedOn' => Array ('filter_block' => 'grid_date_range_filter', 'width' => 150, ), 'Modified' => Array ('filter_block' => 'grid_date_range_filter', 'width' => 150, ), 'Status' => Array ('filter_block' => 'grid_options_filter', 'width' => 100, ), 'IPAddress' => Array ('filter_block' => 'grid_like_filter', 'width' => 100, 'hidden' => 1), ), ), // used 'Admins' => Array ( 'Icons' => Array ( 0 => 'icon16_admin_disabled.png', 1 => 'icon16_admin.png', 2 => 'icon16_admin_disabled.png', ), 'Fields' => Array ( 'PortalUserId' => Array ('title' => 'column:la_fld_Id', 'data_block' => 'grid_checkbox_td', 'filter_block' => 'grid_range_filter', 'width' => 70), 'Username' => Array ('filter_block' => 'grid_like_filter', 'width' => 150, ), 'FirstName' => Array ('filter_block' => 'grid_like_filter', 'width' => 150, ), 'LastName' => Array ('filter_block' => 'grid_like_filter', 'width' => 150, ), 'Email' => Array ('filter_block' => 'grid_like_filter', 'width' => 200, ), 'Status' => Array ('filter_block' => 'grid_options_filter', 'width' => 100, ), ), ), // used 'RegularUsers' => Array ( 'Icons' => Array ( 0 => 'icon16_user_disabled.png', 1 => 'icon16_user.png', 2 => 'icon16_user_pending.png' ), 'Fields' => Array ( 'PortalUserId' => Array ('title' => 'column:la_fld_Id', 'data_block' => 'grid_checkbox_td', 'filter_block' => 'grid_range_filter', 'width' => 70), 'Username' => Array ('filter_block' => 'grid_like_filter', 'width' => 150, ), 'FirstName' => Array ('filter_block' => 'grid_like_filter', 'width' => 150, ), 'LastName' => Array ('filter_block' => 'grid_like_filter', 'width' => 150, ), 'Email' => Array ('filter_block' => 'grid_like_filter', 'width' => 200, ), 'PrimaryGroup' => Array ('title' => 'la_col_PrimaryGroup', 'filter_block' => 'grid_like_filter', 'width' => 140), 'Status' => Array ('filter_block' => 'grid_options_filter', 'width' => 100, ), 'CreatedOn' => Array ('filter_block' => 'grid_date_range_filter', 'width' => 100), 'Modified' => Array ('filter_block' => 'grid_date_range_filter', 'width' => 100), 'IPAddress' => Array ('filter_block' => 'grid_like_filter', 'width' => 100, 'hidden' => 1), 'EmailVerified' => Array ('filter_block' => 'grid_options_filter', 'width' => 100, 'hidden' => 1), ), ), ), -); \ No newline at end of file +); Index: branches/5.3.x/core/units/helpers/curl_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/curl_helper.php (revision 16394) +++ branches/5.3.x/core/units/helpers/curl_helper.php (revision 16395) @@ -1,571 +1,571 @@ <?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, // Prevents CURL from adding "Expect: 100-continue" header for POST requests. 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 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) { 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/search_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/search_helper.php (revision 16394) +++ branches/5.3.x/core/units/helpers/search_helper.php (revision 16395) @@ -1,806 +1,838 @@ <?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 kSearchHelper extends kHelper { /** * Perform Exact Search flag * * @var bool * @access protected */ protected $_performExactSearch = true; public function __construct() { parent::__construct(); $this->_performExactSearch = $this->Application->ConfigValue('PerformExactSearch'); } /** * Splits search phrase into keyword using quotes,plus and minus sings and spaces as split criteria * * @param string $keyword * @return Array * @access public */ public function splitKeyword($keyword) { if ( $this->Application->ConfigValue('CheckStopWords') ) { $keyword_after_remove = $this->_removeStopWords($keyword); if ( $keyword_after_remove ) { // allow to search through stop word grid $keyword = $keyword_after_remove; } } $final = Array (); $quotes_re = '/([+\-]?)"(.*?)"/'; $no_quotes_re = '/([+\-]?)([^ ]+)/'; preg_match_all($quotes_re, $keyword, $res); foreach ($res[2] as $index => $kw) { $final[$kw] = $res[1][$index]; } $keyword = preg_replace($quotes_re, '', $keyword); preg_match_all($no_quotes_re, $keyword, $res); foreach ($res[2] as $index => $kw) { $final[$kw] = $res[1][$index]; } if ( $this->_performExactSearch ) { foreach ($final AS $kw => $plus_minus) { if ( !$plus_minus ) { $final[$kw] = '+'; } } } return $final; } function getPositiveKeywords($keyword) { $keywords = $this->splitKeyword($keyword); $ret = Array(); foreach ($keywords as $keyword => $sign) { if ($sign == '+' || $sign == '') { $ret[] = $keyword; } } return $ret; } /** * Replace wildcards to match MySQL * * @param string $keyword * @return string */ function transformWildcards($keyword) { return str_replace(Array ('%', '_', '*', '?') , Array ('\%', '\_', '%', '_'), $keyword); } function buildWhereClause($keyword, $fields) { $keywords = $this->splitKeyword( $this->transformWildcards($keyword) ); $normal_conditions = $plus_conditions = $minus_conditions = Array(); foreach ($keywords as $keyword => $sign) { $keyword = $this->Conn->escape($keyword); switch ($sign) { case '+': $plus_conditions[] = implode(" LIKE '%" . $keyword . "%' OR ", $fields) . " LIKE '%" . $keyword . "%'"; break; case '-': $condition = Array (); foreach ($fields as $field) { $condition[] = $field . " NOT LIKE '%" . $keyword . "%' OR " . $field . ' IS NULL'; } $minus_conditions[] = '(' . implode(') AND (', $condition) . ')'; break; case '': $normal_conditions[] = implode(" LIKE '%" . $keyword . "%' OR ", $fields) . " LIKE '%" . $keyword . "%'"; break; } } // building where clause if ($normal_conditions) { $where_clause = '(' . implode(') OR (', $normal_conditions) . ')'; } else { $where_clause = '1'; } if ($plus_conditions) { $where_clause = '(' . $where_clause . ') AND (' . implode(') AND (', $plus_conditions) . ')'; } if ($minus_conditions) { $where_clause = '(' . $where_clause . ') AND (' . implode(') AND (', $minus_conditions) . ')'; } return $where_clause; } /** * Returns additional information about search field * * @param kDBList $object * @param string $field_name * @return Array */ function _getFieldInformation(&$object, $field_name) { $sql_filter_type = $object->isVirtualField($field_name) ? 'having' : 'where'; $field_options = $object->GetFieldOptions($field_name); $table_name = ''; $field_type = isset($field_options['type']) ? $field_options['type'] : 'string'; if (preg_match('/(.*)\.(.*)/', $field_name, $regs)) { $table_name = '`'.$regs[1].'`.'; // field from external table $field_name = $regs[2]; } elseif ($sql_filter_type == 'where') { $table_name = '`'.$object->TableName.'`.'; // field from local table } $table_name = ($sql_filter_type == 'where') ? $table_name : ''; // replace wid inside table name to WID_MARK constant value $is_temp_table = preg_match('/(.*)'.TABLE_PREFIX.'ses_'.$this->Application->GetSID().'(_[\d]+){0,1}_edit_(.*)/', $table_name, $regs); if ($is_temp_table) { $table_name = $regs[1].TABLE_PREFIX.'ses_'.EDIT_MARK.'_edit_'.$regs[3]; // edit_mark will be replaced with sid[_main_wid] in AddFilters } return Array ($field_name, $field_type, $table_name, $sql_filter_type); } /** * Removes stop words from keyword * * @param string $keyword * @return string */ function _removeStopWords($keyword) { static $stop_words = Array (); if (!$stop_words) { $sql = 'SELECT StopWord FROM ' . $this->Application->getUnitConfig('stop-word')->getTableName() . ' ORDER BY LENGTH(StopWord) DESC, StopWord ASC'; $stop_words = $this->Conn->GetCol($sql); foreach ($stop_words as $index => $stop_word) { $stop_words[$index] = '/(^| )' . preg_quote($stop_word, '/') . '( |$)/'; } } $keyword = preg_replace($stop_words, ' ', $keyword); return trim( preg_replace('/[ ]+/', ' ', $keyword) ); } /** * Performs new search on a given grid * * @param kEvent $event * @return void * @access public */ public function performSearch($event) { $object = $event->getObject(); /* @var $object kDBItem */ // process search keyword $search_keyword = $this->Application->GetVar($event->getPrefixSpecial(true) . '_search_keyword'); $this->Application->StoreVar($event->getPrefixSpecial() . '_search_keyword', $search_keyword); $custom_filter = $this->processCustomFilters($event); if ( !$search_keyword && $custom_filter === false ) { $this->resetSearch($event); return ; } if ( $search_keyword ) { $this->processAutomaticFilters($event, $search_keyword, $custom_filter); } } /** * Creates filtering sql clauses based on given search restrictions * * @param kEvent $event * @param string $search_keyword * @param Array $custom_filter * @return void */ function processAutomaticFilters($event, $search_keyword, $custom_filter) { $grid_name = $this->Application->GetVar('grid_name'); $grid = $event->getUnitConfig()->getGridByName($grid_name); $search_fields = array_keys($grid['Fields']); $search_filter = Array(); $object = $event->getObject(); /* @var $object kDBList */ foreach ($search_fields as $search_field) { $custom_search = isset($custom_filter[$search_field]); $filter_data = $this->getSearchClause($object, $search_field, $search_keyword, $custom_search); if ($filter_data) { $search_filter[$search_field] = $filter_data; } else { unset($search_filter[$search_field]); } } $this->Application->StoreVar($event->getPrefixSpecial().'_search_filter', serialize($search_filter) ); } /** * Returns search clause for any particular field * * @param kDBList $object * @param string $field_name * @param string $search_keyword what we are searching (false, when building custom filter clause) * @param string $custom_search already found using custom filter * @return Array */ function getSearchClause(&$object, $field_name, $search_keyword, $custom_search) { if ($object->isVirtualField($field_name) && !$object->isCalculatedField($field_name)) { // Virtual field, that is shown in grid, but it doesn't have corresponding calculated field. // Happens, when field value is calculated on the fly (during grid display) and it is not searchable. return ''; } $search_keywords = $this->splitKeyword($search_keyword); list ($field_name, $field_type, $table_name, $sql_filter_type) = $this->_getFieldInformation($object, $field_name); $filter_value = ''; // get field clause by formatter name and/or parameters $field_options = $object->GetFieldOptions($field_name); $formatter = getArrayValue($field_options, 'formatter'); switch ($formatter) { case 'kOptionsFormatter': $search_keys = Array(); if ($custom_search === false) { // if keywords passed through simple search filter (on each grid) $use_phrases = getArrayValue($field_options, 'use_phrases'); $multiple = array_key_exists('multiple', $field_options) && $field_options['multiple']; foreach ($field_options['options'] as $key => $val) { $match_to = mb_strtolower($use_phrases ? $this->Application->Phrase($val) : $val); foreach ($search_keywords as $keyword => $sign) { // doesn't support wildcards if (strpos($match_to, mb_strtolower($keyword)) === false) { if ($sign == '+') { $filter_value = $table_name.'`'.$field_name.'` = NULL'; break; } else { continue; } } if ($sign == '+' || $sign == '') { // don't add single quotes to found option ids when multiselect (but escape string anyway) $search_keys[$key] = $multiple ? $this->Conn->escape($key) : $this->Conn->qstr($key); } elseif($sign == '-') { // if same value if found as exclusive too, then remove from search result unset($search_keys[$key]); } } } } if ($search_keys) { if ($multiple) { $filter_value = $table_name.'`'.$field_name.'` LIKE "%|' . implode('|%" OR ' . $table_name.'`'.$field_name.'` LIKE "%|', $search_keys) . '|%"'; } else { $filter_value = $table_name.'`'.$field_name.'` IN ('.implode(',', $search_keys).')'; } } $field_processed = true; break; case 'kDateFormatter': // if date is searched using direct filter, then do nothing here, otherwise search using LIKE clause $field_processed = ($custom_search !== false) ? true : false; break; default: $field_processed = false; break; } // if not already processed by formatter, then get clause by field type if (!$field_processed && $search_keywords) { switch($field_type) { case 'int': case 'integer': case 'numeric': $search_keys = Array(); foreach ($search_keywords as $keyword => $sign) { if (!is_numeric($keyword) || ($sign == '-')) { continue; } $search_keys[] = $this->Conn->qstr($keyword); } if ($search_keys) { $filter_value = $table_name.'`'.$field_name.'` IN ('.implode(',', $search_keys).')'; } break; case 'double': case 'float': case 'real': $search_keys = Array(); foreach ($search_keywords as $keyword => $sign) { $keyword = str_replace(',', '.', $keyword); if (!is_numeric($keyword) || ($sign == '-')) continue; $search_keys[] = 'ABS('.$table_name.'`'.$field_name.'` - '.$this->Conn->qstr($keyword).') <= 0.0001'; } if ($search_keys) { $filter_value = '('.implode(') OR (', $search_keys).')'; } break; case 'string': $filter_value = $this->buildWhereClause($search_keyword, Array($table_name.'`'.$field_name.'`')); break; } } if ($filter_value) { return Array('type' => $sql_filter_type, 'value' => $filter_value); } return false; } /** * Processes custom filters from submit * * @param kEvent $event * @return Array|bool */ function processCustomFilters($event) { $grid_name = $this->Application->GetVar('grid_name'); // update "custom filter" with values from submit: begin $view_name = $this->Application->RecallVar($event->getPrefixSpecial().'_current_view'); $custom_filters = $this->Application->RecallPersistentVar($event->getPrefixSpecial().'_custom_filter.'.$view_name/*, ALLOW_DEFAULT_SETTINGS*/); if ($custom_filters) { $custom_filters = unserialize($custom_filters); $custom_filter = isset($custom_filters[$grid_name]) ? $custom_filters[$grid_name] : Array (); } else { $custom_filter = Array (); } // submit format custom_filters[prefix_special][field] $submit_filters = $this->Application->GetVar('custom_filters'); if ($submit_filters) { $submit_filters = getArrayValue($submit_filters, $event->getPrefixSpecial(), $grid_name); if ($submit_filters) { foreach ($submit_filters as $field_name => $field_options) { list ($filter_type, $field_value) = each($field_options); $is_empty = strlen(is_array($field_value) ? implode('', $field_value) : $field_value) == 0; if ($is_empty) { if (isset($custom_filter[$field_name])) { // use isset, because non-existing key will cause "php notice"! unset($custom_filter[$field_name][$filter_type]); // remove filter if (!$custom_filter[$field_name]) { // if no filters left for field, then delete record at all unset($custom_filter[$field_name]); } } } else { $custom_filter[$field_name][$filter_type]['submit_value'] = $field_value; } } } } if ($custom_filter) { $custom_filters[$grid_name] = $custom_filter; } else { unset($custom_filters[$grid_name]); } // update "custom filter" with values from submit: end if (!$custom_filter) { // in case when no filters specified, there are nothing to process $this->Application->StorePersistentVar($event->getPrefixSpecial().'_custom_filter.'.$view_name, serialize($custom_filters) ); return false; } $object = $event->getObject(); // don't recall it each time in getCustomFilterSearchClause $grid_info = $event->getUnitConfig()->getGridByName($grid_name); foreach ($custom_filter as $field_name => $field_options) { list ($filter_type, $field_options) = each($field_options); $field_options['grid_options'] = $grid_info['Fields'][$field_name]; $field_options = $this->getCustomFilterSearchClause($object, $field_name, $filter_type, $field_options); if ($field_options['value']) { unset($field_options['grid_options']); $custom_filter[$field_name][$filter_type] = $field_options; } } $custom_filters[$grid_name] = $custom_filter; $this->Application->StorePersistentVar($event->getPrefixSpecial().'_custom_filter.'.$view_name, serialize($custom_filters) ); return $custom_filter; } /** * Checks, that range filters "To" part is defined for given grid * * @param string $prefix_special * @param string $grid_name * @return bool */ function rangeFiltersUsed($prefix_special, $grid_name) { static $cache = Array (); $cache_key = $prefix_special . $grid_name; if (array_key_exists($cache_key, $cache)) { return $cache[$cache_key]; } $view_name = $this->Application->RecallVar($prefix_special . '_current_view'); $custom_filters = $this->Application->RecallPersistentVar($prefix_special . '_custom_filter.' . $view_name/*, ALLOW_DEFAULT_SETTINGS*/); if (!$custom_filters) { // filters not defined for given prefix $cache[$cache_key] = false; return false; } $custom_filters = unserialize($custom_filters); if (!is_array($custom_filters) || !array_key_exists($grid_name, $custom_filters)) { // filters not defined for given grid $cache[$cache_key] = false; return false; } $range_filter_defined = false; $custom_filter = $custom_filters[$grid_name]; foreach ($custom_filter as $field_name => $field_options) { list ($filter_type, $field_options) = each($field_options); if (strpos($filter_type, 'range') === false) { continue; } $to_value = (string)$field_options['submit_value']['to']; if ($to_value !== '') { $range_filter_defined = true; break; } } $cache[$cache_key] = $range_filter_defined; return $range_filter_defined; } /** * Return numeric range filter value + checking that it's number * * @param Array $value array containing range filter value * @return unknown */ function getRangeValue($value) { // fix user typing error, since MySQL only sees "." as decimal separator $value = str_replace(',', '.', $value); return strlen($value) && is_numeric($value) ? $this->Conn->qstr($value) : false; } /** * Returns filter clause * * @param kDBItem $object * @param string $field_name * @param string $filter_type * @param Array $field_options * @return Array */ function getCustomFilterSearchClause(&$object, $field_name, $filter_type, $field_options) { // this is usually used for mutlilingual fields and date fields if (isset($field_options['grid_options']['sort_field'])) { $field_name = $field_options['grid_options']['sort_field']; } list ($field_name, $field_type, $table_name, $sql_filter_type) = $this->_getFieldInformation($object, $field_name); $filter_value = ''; switch ($filter_type) { case 'range': $from = $this->getRangeValue($field_options['submit_value']['from']); $to = $this->getRangeValue($field_options['submit_value']['to']); if ( $from !== false && $to !== false ) { // add range filter $filter_value = $table_name . '`' . $field_name . '` >= ' . $from . ' AND ' . $table_name . '`' . $field_name . '` <= ' . $to; } elseif ( $field_type == 'int' || $field_type == 'integer' ) { if ( $from !== false ) { // add equals filter on $from $filter_value = $table_name . '`' . $field_name . '` = ' . $from; } elseif ( $to !== false ) { // add equals filter on $to $filter_value = $table_name . '`' . $field_name . '` = ' . $to; } } else { // MySQL can't compare values in "float" type columns using "=" operator if ( $from !== false ) { // add equals filter on $from $filter_value = 'ABS(' . $table_name . '`' . $field_name . '` - ' . $from . ') <= 0.0001'; } elseif ( $to !== false ) { // add equals filter on $to $filter_value = 'ABS(' . $table_name . '`' . $field_name . '` - ' . $to . ') <= 0.0001'; } } break; case 'date_range': $from = $this->processRangeField($object, $field_name, $field_options['submit_value'], 'from'); $to = $this->processRangeField($object, $field_name, $field_options['submit_value'], 'to'); $day_seconds = 23 * 60 * 60 + 59 * 60 + 59; if ($from !== false && $to === false) { $from = strtotime(date('Y-m-d', $from) . ' 00:00:00', $from); // reset to morning $to = $from + $day_seconds; } elseif ($from === false && $to !== false) { $to = strtotime(date('Y-m-d', $to) . ' 23:59:59', $to); // reset to evening $from = $to - $day_seconds; } if ($from !== false && $to !== false) { $filter_value = $table_name.'`'.$field_name.'` >= '.$from.' AND '.$table_name.'`'.$field_name.'` <= '.$to; } break; case 'equals': case 'options': $field_value = strlen($field_options['submit_value']) ? $this->Conn->qstr($field_options['submit_value']) : false; if ($field_value) { $filter_value = $table_name.'`'.$field_name.'` = '.$field_value; } break; case 'picker': $field_value = strlen($field_options['submit_value']) ? $this->Conn->escape($field_options['submit_value']) : false; if ($field_value) { $filter_value = $table_name.'`'.$field_name.'` LIKE "%|'.$field_value.'|%"'; } break; case 'multioptions': $field_value = $field_options['submit_value']; if ( $field_value ) { $field_value = explode('|', substr($field_value, 1, -1)); $multiple = $object->GetFieldOption($field_name, 'multiple'); $field_value = $this->Conn->qstrArray($field_value, $multiple ? 'escape' : 'qstr'); if ( $multiple ) { $filter_value = $table_name . '`' . $field_name . '` LIKE "%|' . implode('|%" OR ' . $table_name . '`' . $field_name . '` LIKE "%|', $field_value) . '|%"'; } else { $filter_value = $table_name . '`' . $field_name . '` IN (' . implode(',', $field_value) . ')'; } } break; case 'like': $filter_value = $this->buildWhereClause($field_options['submit_value'], Array($table_name.'`'.$field_name.'`')); break; default: break; } $field_options['sql_filter_type'] = $sql_filter_type; $field_options['value'] = $filter_value; return $field_options; } /** * Enter description here... * * @param kdbItem $object * @param string $search_field * @param string $value * @param string $type */ function processRangeField(&$object, $search_field, $value, $type) { if ( !strlen($value[$type]) ) { return false; } $options = $object->GetFieldOptions($search_field); $dt_separator = array_key_exists('date_time_separator', $options) ? $options['date_time_separator'] : ' '; $value[$type] = trim($value[$type], $dt_separator); // trim any $tmp_value = explode($dt_separator, $value[$type], 2); if ( count($tmp_value) == 1 ) { $time_format = $this->_getInputTimeFormat($options); if ( $time_format ) { // time is missing, but time format available -> guess time and add to date $time = ($type == 'from') ? mktime(0, 0, 0) : mktime(23, 59, 59); $time = date($time_format, $time); $value[$type] .= $dt_separator . $time; } } $formatter = $this->Application->recallObject($options['formatter']); /* @var $formatter kFormatter */ $value_ts = $formatter->Parse($value[$type], $search_field, $object); if ( $object->GetErrorPseudo($search_field) ) { // invalid format -> ignore this date in search $object->RemoveError($search_field); return false; } return $value_ts; } /** * Returns InputTimeFormat using given field options * * @param Array $field_options * @return string */ function _getInputTimeFormat($field_options) { if ( array_key_exists('input_time_format', $field_options) ) { return $field_options['input_time_format']; } $lang_current = $this->Application->recallObject('lang.current'); /* @var $lang_current LanguagesItem */ return $lang_current->GetDBField('InputTimeFormat'); } /** * Resets current search * * @param kEvent $event */ function resetSearch($event) { $this->Application->RemoveVar($event->getPrefixSpecial().'_search_filter'); $this->Application->RemoveVar($event->getPrefixSpecial().'_search_keyword'); $view_name = $this->Application->RecallVar($event->getPrefixSpecial().'_current_view'); $this->Application->RemovePersistentVar($event->getPrefixSpecial().'_custom_filter.'.$view_name); } /** * Creates filters based on "types" & "except" parameters from PrintList * * @param kEvent $event * @param Array $type_clauses * @param string $types * @param string $except_types */ function SetComplexFilter($event, &$type_clauses, $types, $except_types) { $includes_or_filter = $this->Application->makeClass('kMultipleFilter', Array (kDBList::FLT_TYPE_OR)); /* @var $includes_or_filter kMultipleFilter */ $excepts_and_filter = $this->Application->makeClass('kMultipleFilter', Array (kDBList::FLT_TYPE_AND)); /* @var $excepts_and_filter kMultipleFilter */ $includes_or_filter_h = $this->Application->makeClass('kMultipleFilter', Array (kDBList::FLT_TYPE_OR)); /* @var $includes_or_filter_h kMultipleFilter */ $excepts_and_filter_h = $this->Application->makeClass('kMultipleFilter', Array (kDBList::FLT_TYPE_AND)); /* @var $excepts_and_filter_h kMultipleFilter */ if ( $types ) { $types = explode(',', $types); foreach ($types as $type) { $type = trim($type); if ( isset($type_clauses[$type]) ) { if ( $type_clauses[$type]['having_filter'] ) { $includes_or_filter_h->addFilter('filter_' . $type, $type_clauses[$type]['include']); } else { $includes_or_filter->addFilter('filter_' . $type, $type_clauses[$type]['include']); } } } } if ( $except_types ) { $except_types = explode(',', $except_types); foreach ($except_types as $type) { $type = trim($type); if ( isset($type_clauses[$type]) ) { if ( $type_clauses[$type]['having_filter'] ) { $excepts_and_filter_h->addFilter('filter_' . $type, $type_clauses[$type]['except']); } else { $excepts_and_filter->addFilter('filter_' . $type, $type_clauses[$type]['except']); } } } } $object = $event->getObject(); /* @var $object kDBList */ $object->addFilter('includes_filter', $includes_or_filter); $object->addFilter('excepts_filter', $excepts_and_filter); $object->addFilter('includes_filter_h', $includes_or_filter_h, kDBList::HAVING_FILTER); $object->addFilter('excepts_filter_h', $excepts_and_filter_h, kDBList::HAVING_FILTER); } - } \ No newline at end of file + + /** + * Ensures empty search table + * + * @return void + */ + public function ensureEmptySearchTable() + { + $search_table = $this->getSearchTable(); + $this->Conn->Query('DROP TABLE IF EXISTS ' . $search_table); + $sql = 'CREATE TABLE ' . $search_table . ' ( + `Relevance` decimal(8,5) DEFAULT NULL, + `ItemId` int(11) NOT NULL DEFAULT 0, + `ResourceId` int(11) DEFAULT NULL, + `ItemType` int(1) NOT NULL DEFAULT 0, + `EdPick` tinyint(4) NOT NULL DEFAULT 0, + KEY `ResourceId` (`ResourceId`), + KEY `Relevance` (`Relevance`) + )'; + $this->Conn->Query($sql); + } + + /** + * Search table name + * + * @return string + */ + public function getSearchTable() + { + return TABLE_PREFIX . 'ses_' . $this->Application->GetSID() . '_' . TABLE_PREFIX . 'Search'; + } + + } Index: branches/5.3.x/core/units/helpers/page_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/page_helper.php (revision 16394) +++ branches/5.3.x/core/units/helpers/page_helper.php (revision 16395) @@ -1,439 +1,441 @@ <?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 PageHelper extends kHelper { /** * Returns page info * * @param int $page_id * @return Array */ function getPageInfo($page_id) { list ($user_id, $history_permission) = $this->getHistoryPermissionAndUser($page_id); $users = $this->getEditors($page_id, $user_id); return array( 'current_revision' => $this->getCurrentRevisionInfo(), 'editors' => $users, 'editors_warning' => $this->getEditorsWarning($users), 'revisions' => $history_permission ? $this->getPageRevisions($page_id) : array(), ); } /** * Returns current admin user id (even, when called from front-end) and it's revision history view permission * * @param int $page_id * @return Array */ protected function getHistoryPermissionAndUser($page_id) { $perm_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ $user_id = (int)$this->Application->RecallVar($this->Application->isAdmin ? 'user_id' : 'admin_user_id'); $history_permission = $perm_helper->CheckUserPermission($user_id, 'CATEGORY.REVISION.HISTORY.VIEW', 0, $page_id); return Array ($user_id, $history_permission); } /** * Returns information about given page editors. * * @param integer $page_id Page, that is being edited. * @param integer $user_id User, who is editing a page. * * @return array */ protected function getEditors($page_id, $user_id) { $where_clause = array( 'pr.PageId = ' . $page_id, 'pr.CreatedById <> ' . $user_id, 'pr.IsDraft = 1', ); $sql = 'SELECT CASE pr.CreatedById WHEN ' . USER_ROOT . ' THEN "root" WHEN ' . USER_GUEST . ' THEN "Guest" ELSE IF(u.Username = "", u.Email, u.Username) END FROM ' . $this->Application->getUnitConfig('page-revision')->getTableName() . ' pr LEFT JOIN ' . TABLE_PREFIX . 'Users u ON u.PortalUserId = pr.CreatedById WHERE (' . implode(') AND (', $where_clause) . ')'; return $this->Conn->GetCol($sql); } /** * Returns information about current revision. * * @return array */ protected function getCurrentRevisionInfo() { $revision = $this->Application->recallObject('page-revision.current'); /* @var $revision kDBItem */ $status_label = $this->getRevisionStatusText($revision); $draft = $revision->GetDBField('IsDraft'); $title = $this->getAdminPhrase($draft ? 'la_title_EditingDraft' : 'la_title_ViewingRevision'); $current_revision_info = array( 'title' => sprintf($title, $revision->GetDBField('RevisionNumber'), $status_label), 'status' => $revision->GetDBField('Status'), 'saved' => '', 'toolbar_state' => $this->getToolbarButtonsState($revision), ); $auto_save_time = $revision->GetDBField('AutoSavedOn'); if ( $auto_save_time ) { $phrase = $this->getAdminPhrase($draft ? 'la_DraftSavedAt' : 'la_SavedAt'); $current_revision_info['saved'] = sprintf($phrase, $revision->GetField('AutoSavedOn_time') . ' (' . $this->getAgoTime($auto_save_time) . ')'); } return $current_revision_info; } /** * Returns state of all CMS revision toolbar buttons. * * @param kDBItem $revision Page Revision. * * @return array */ public function getToolbarButtonsState(kDBItem $revision) { $ret = array(); foreach ( $this->getToolbarButtons() as $toolbar_button ) { $ret[$toolbar_button] = $this->isToolbarButtonEnabled($toolbar_button, $revision); } return $ret; } /** * Returns list of CMS revision toolbar buttons. * * @return array */ protected function getToolbarButtons() { return array('select', 'delete', 'approve', 'decline', 'preview', 'history'); } /** * Checks if given CMS revision toolbar button is enabled for given revision. * * @param string $button_name Toolbar button name. * @param kDBItem $revision Revision to check against. * * @return boolean */ protected function isToolbarButtonEnabled($button_name, kDBItem $revision) { $is_draft = $revision->GetDBField('IsDraft'); if ( $button_name == 'select' || $button_name == 'delete' || $button_name == 'preview' ) { return (bool)$is_draft; } if ( $button_name == 'approve' ) { return $revision->GetDBField('Status') != STATUS_ACTIVE && !$is_draft; } if ( $button_name == 'decline' ) { return $revision->GetDBField('Status') != STATUS_DISABLED && !$revision->GetDBField('IsLive') && !$is_draft; } return true; } /** * Returns warning to be shown in case of parallel editing attempts. * * @param array $users Users, that are editing a page. * * @return string */ protected function getEditorsWarning(array $users) { $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ $ret = $ml_helper->getPluralPhrase( count($users), array( 'phrase1' => 'la_PageCurrentlyEditing1', 'phrase2' => 'la_PageCurrentlyEditing2', 'phrase5' => 'la_PageCurrentlyEditing5', ), false, true ); return sprintf($ret, implode(', ', $users)); } /** * Returns information about given page revisions. * * @param integer $page_id Page, that is being edited. * * @return array */ protected function getPageRevisions($page_id) { $ret = Array (); $tag_params = Array ('per_page' => -1, 'skip_parent_filter' => 1, 'requery' => 1, 'page_id' => $page_id); $revisions = $this->Application->recallObject('page-revision.list', 'page-revision_List', $tag_params); /* @var $revisions kDBList */ $revisions->Query(); $revisions->GoFirst(); while ( !$revisions->EOL() ) { $ret[ 'r' . $revisions->GetDBField('RevisionNumber') ] = array( 'title' => $this->getRevisionTitle($revisions), 'status' => $revisions->GetDBField('Status'), 'status_label' => $this->getRevisionStatusText($revisions), 'datetime' => $revisions->GetField('CreatedOn'), 'author' => $this->getRevisionAuthor($revisions), 'draft' => (int)$revisions->GetDBField('IsDraft'), ); $revisions->GoNext(); } return $ret; } /** * Returns title for given revision. * * @param kDBBase $revision Page Revision. * * @return string */ protected function getRevisionTitle(kDBBase $revision) { if ( $revision->GetDBField('IsDraft') ) { return $this->getAdminPhrase('la_Draft'); } $title = $this->getAdminPhrase('la_RevisionNumber'); return sprintf($title, $revision->GetDBField('RevisionNumber')); } /** * Returns status text for given revision. * * @param kDBBase $revision Page Revision. * * @return string */ protected function getRevisionStatusText(kDBBase $revision) { $status = $revision->GetDBField('Status'); $options = $revision->GetFieldOptions('Status'); return mb_strtolower($this->getAdminPhrase($options['options'][$status])); } /** * Returns author of given revision. * * @param kDBBase $revision Page Revision. * * @return string */ protected function getRevisionAuthor(kDBBase $revision) { $by_label = $this->getAdminPhrase('la_By'); return $by_label . ': ' . $revision->GetField('CreatedById'); } /** * Returns Admin's non-editable translation of given phrase. * * @param string $label Phrase label. * * @return string */ protected function getAdminPhrase($label) { return $this->Application->Phrase($label, false, true); } /** * Returns time passed between 2 given dates in "X minutes Y seconds ago" format * * @param int $from_date * @param int $to_date * @param integer $max_levels * * @return string */ public function getAgoTime($from_date, $to_date = null, $max_levels = 1) { $blocks = Array ( Array ('name' => 'year', 'amount' => 60*60*24*365), Array ('name' => 'month' ,'amount' => 60*60*24*31), Array ('name' => 'week', 'amount' => 60*60*24*7), Array ('name' => 'day', 'amount' => 60*60*24), Array ('name' => 'hour', 'amount' => 60*60), Array ('name' => 'minute', 'amount' => 60), Array ('name' => 'second', 'amount' => 1), ); if ( !isset($to_date) ) { $to_date = time(); } $diff = abs($to_date - $from_date); if ( $diff == 0 ) { return 'now'; } $current_level = 1; $result = Array (); foreach ($blocks as $block) { if ($current_level > $max_levels) { break; } if ( $diff / $block['amount'] >= 1 ) { $amount = floor($diff / $block['amount']); $plural = $amount > 1 ? 's' : ''; $result[] = $amount . ' ' . $block['name'] . $plural; $diff -= $amount * $block['amount']; $current_level++; } } return implode(' ', $result) . ' ago'; } /** * Returns where clause for loading correct revision for a given page * * @param int $page_id * @param int $live_revision_number * @param string $table_name * @return string */ public function getRevsionWhereClause($page_id, $live_revision_number, $table_name = '') { $revision = (int)$this->Application->GetVar('revision'); list ($user_id, $has_permission) = $this->getHistoryPermissionAndUser($page_id); if ( $has_permission && $revision ) { $revision_clause = $table_name . 'RevisionNumber = ' . $revision . ' AND ' . $table_name . 'IsDraft = 0'; } else { $editing_mode = $this->Application->GetVar('editing_mode'); // not in a EDITING_MODE constant, while in admin console $revision_clause = $table_name . 'RevisionNumber = ' . $live_revision_number . ' AND ' . $table_name . 'IsDraft = 0'; - if ( $this->Application->GetVar('preview') || $editing_mode == EDITING_MODE_CONTENT ) { - $revision_clause = '(' . $table_name . 'CreatedById = ' . $user_id . ' AND ' . $table_name . 'IsDraft = 1) OR (' . $revision_clause . ')'; + if ( $this->Application->ConfigValue('EnablePageContentRevisionControl') ) { + if ( $this->Application->GetVar('preview') || $editing_mode == EDITING_MODE_CONTENT ) { + $revision_clause = '(' . $table_name . 'CreatedById = ' . $user_id . ' AND ' . $table_name . 'IsDraft = 1) OR (' . $revision_clause . ')'; + } } } return $revision_clause; } /** * Creates new content block in every revision that misses it. Plus creates first page revision * * @param int $page_id * @param int $num */ public function createNewContentBlock($page_id, $num) { $sql = 'SELECT pc.PageContentId, pr.RevisionId FROM ' . TABLE_PREFIX . 'PageRevisions pr LEFT JOIN ' . TABLE_PREFIX . 'PageContent pc ON pc.RevisionId = pr.RevisionId AND pc.ContentNum = ' . $num . ' WHERE pr.PageId = ' . $page_id; $revisions = $this->Conn->GetCol($sql, 'RevisionId'); if ( !$revisions ) { // no revisions for a page -> create a live revision $revision = $this->Application->recallObject('page-revision.live', null, Array ('skip_autoload' => true)); /* @var $revision kDBItem */ $revision->SetDBField('PageId', $page_id); $revision->SetDBField('RevisionNumber', 1); $revision->SetDBField('Status', STATUS_ACTIVE); $revision->Create(); $revisions[ $revision->GetID() ] = NULL; } $content_block = $this->Application->recallObject('content.new', null, Array ('skip_autoload' => true)); /* @var $content_block kDBItem */ $content_block->SetDBField('PageId', $page_id); $content_block->SetDBField('ContentNum', $num); foreach ($revisions as $revision_id => $content_block_id) { if ( is_numeric($content_block_id) ) { continue; } $content_block->SetDBField('RevisionId', $revision_id); $content_block->Create(); } } /** * Loads content block by it's number * * @param kDBItem $content_block * @param CategoriesItem $page * @param int $num * * @return bool */ public function loadContentBlock(&$content_block, &$page, $num) { $page_id = $page->GetID(); if ( !EDITING_MODE && !$this->Application->GetVar('preview') ) { $revision_clause = 'pr.RevisionNumber = ' . $page->GetDBField('LiveRevisionNumber') . ' AND pr.IsDraft = 0'; } else { $revision_clause = $this->getRevsionWhereClause($page_id, $page->GetDBField('LiveRevisionNumber'), 'pr.'); } $sql = $content_block->GetSelectSQL() . ' WHERE (' . $content_block->TableName . '.PageId = ' . $page_id . ') AND (' . $content_block->TableName . '.ContentNum = ' . $num . ') AND (' . $revision_clause . ') ORDER BY pr.IsDraft DESC, pr.RevisionNumber DESC'; $content_data = $this->Conn->GetRow($sql); $content_block->LoadFromHash($content_data); return $content_block->isLoaded(); } } Index: branches/5.3.x/core/units/helpers/category_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/category_helper.php (revision 16394) +++ branches/5.3.x/core/units/helpers/category_helper.php (revision 16395) @@ -1,381 +1,361 @@ <?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 CategoryHelper extends kHelper { /** * Structure tree for ParentId field in category or category items * * @var Array */ var $_structureTree = null; /** * ID of primary language (only for caching) * * @var int */ var $_primaryLanguageId = false; /** - * Returns module information based on given module name or current category (relative to module root categories) + * Returns module information based on given module name or current category + * (relative to module root categories). * - * @param Array $params - * @param Array $category_ids category parent path (already as array) - * @return Array - * @access public + * @param array $params Tag params. + * @param array $category_path Category parent path. + * + * @return array */ - public function getCategoryModule($params, $category_ids) + public function getCategoryModule(array $params, array $category_path) { - $module_info = Array (); - + // Get module by name. if ( isset($params['module']) ) { - // get module by name specified - $module_info = $this->Application->findModule('Name', $params['module']); - - } - elseif ( $category_ids ) { - // get module by category path - $module_root_categories = $this->getModuleRootCategories(); - $common_categories = array_intersect($category_ids, $module_root_categories); - $module_category_id = array_shift($common_categories); // get 1st common category - $module_info = $this->Application->findModule('RootCat', $module_category_id); + return $this->Application->findModule('Name', $params['module']); } - return $module_info; - } - - /** - * Returns root categories from all modules - * - * @return Array - * @access protected - */ - protected function getModuleRootCategories() - { - static $root_categories = null; + // Get module by category path. + if ( $category_path ) { + foreach ( array_reverse($category_path) as $module_category_id ) { + $module_info = $this->Application->findModule('RootCat', $module_category_id); - if ( !isset($root_categories) ) { - $root_categories = Array (); - foreach ($this->Application->ModuleInfo as $module_info) { - array_push($root_categories, $module_info['RootCat']); + if ( $module_info && $module_info['Var'] != 'adm' ) { + return $module_info; + } } - - $root_categories = array_unique($root_categories); } - return $root_categories; + return array(); } /** * Converts multi-dimensional category structure in one-dimensional option array (category_id=>category_name) * * @param Array $data * @param int $parent_category_id * @param int $language_id * @param int $theme_id * @param int $level * @return Array * @access protected */ protected function _printChildren(&$data, $parent_category_id, $language_id, $theme_id, $level = 0) { if ( $data['ThemeId'] != $theme_id && $data['ThemeId'] != 0 ) { // don't show system templates from different themes return Array (); } $category_language = $data['l' . $language_id . '_Name'] ? $language_id : $this->_primaryLanguageId; $ret = Array ($parent_category_id => str_repeat('—', $level) . ' ' . $data['l' . $category_language . '_Name']); if ( $data['children'] ) { $level++; foreach ($data['children'] as $category_id => $category_data) { // numeric keys $ret = kUtil::array_merge_recursive($ret, $this->_printChildren($data['children'][$category_id], $category_id, $language_id, $theme_id, $level)); } } return $ret; } /** * Returns information about children under parent path (recursive) * * @param int $parent_category_id * @param Array $languages * @return Array * @access protected */ protected function _getChildren($parent_category_id, $languages) { static $items_by_parent = null, $parent_mapping = null; if ( !isset($items_by_parent) ) { $fields = $items_by_parent = Array (); foreach ($languages as $language_id) { $fields[] = 'l' . $language_id . '_Name'; } $sql = 'SELECT CategoryId AS id, ' . implode(', ', $fields) . ', ParentId, Template, ThemeId FROM ' . TABLE_PREFIX . 'Categories ORDER BY Priority DESC'; $items = $this->Conn->Query($sql, 'id'); foreach ($items as $item_id => $item_data) { $item_parent_id = $item_data['ParentId']; unset($item_data['ParentId']); if ( !array_key_exists($item_parent_id, $items_by_parent) ) { $items_by_parent[$item_parent_id] = Array (); } $item_data['children'] = false; $parent_mapping[$item_id] = $item_parent_id; $items_by_parent[$item_parent_id][$item_id] = $item_data; } $base_category = $this->Application->getBaseCategory(); // "Content" category if ( isset($items_by_parent[$base_category]) ) { $index_category = $this->findIndexCategoryId($items_by_parent[$base_category]); // rename "Content" into "Home" keeping it's ID $items_by_parent[$parent_mapping[$base_category]][$base_category]['l1_Name'] = $this->Application->Phrase('la_rootcategory_name'); if ( $index_category !== false ) { // remove category of "index.tpl" template unset($items_by_parent[$base_category][$index_category]); unset($parent_mapping[$index_category]); } } } $data = $items_by_parent[$parent_mapping[$parent_category_id]][$parent_category_id]; $categories = array_key_exists($parent_category_id, $items_by_parent) ? $items_by_parent[$parent_category_id] : Array (); foreach ($categories as $category_id => $category_data) { if ( $category_id == $parent_category_id ) { // don't process myself - prevents recursion continue; } $data['children'][$category_id] = $this->_getChildren($category_id, $languages); } return $data; } /** * Finds "Home" category among given top level categories * * @param Array $top_categories * @return bool|int * @access protected */ protected function findIndexCategoryId($top_categories) { foreach ($top_categories as $category_id => $category_info) { if ($category_info['Template'] == 'index') { return $category_id; } } return false; } /** * Generates OR retrieves from cache structure tree * * @return Array * @access protected */ protected function &_getStructureTree() { // get cached version of structure tree if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) { $data = $this->Application->getCache('master:StructureTree', false, CacheSettings::$structureTreeRebuildTime); } else { $data = $this->Application->getDBCache('StructureTree', CacheSettings::$structureTreeRebuildTime); } if ( $data ) { $data = unserialize($data); return $data; } // generate structure tree from scratch $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ $languages = $ml_helper->getLanguages(); $root_category = $this->Application->getBaseCategory(); $data = $this->_getChildren($root_category, $languages); if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) { $this->Application->setCache('master:StructureTree', serialize($data)); } else { $this->Application->setDBCache('StructureTree', serialize($data)); } return $data; } /** * Returns template mapping (between physical and virtual pages) * * @return Array * @access public */ public function getTemplateMapping() { if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) { $data = $this->Application->getCache('master:template_mapping', false, CacheSettings::$templateMappingRebuildTime); } else { $data = $this->Application->getDBCache('template_mapping', CacheSettings::$templateMappingRebuildTime); } if ( $data ) { return unserialize($data); } $sql = 'SELECT IF(c.`Type` = ' . PAGE_TYPE_TEMPLATE . ', CONCAT(c.Template, ":", c.ThemeId), CONCAT("id:", c.CategoryId)) AS SrcTemplate, LOWER( IF( c.SymLinkCategoryId IS NOT NULL, (SELECT cc.NamedParentPath FROM ' . TABLE_PREFIX . 'Categories AS cc WHERE cc.CategoryId = c.SymLinkCategoryId), c.NamedParentPath ) ) AS DstTemplate, c.UseExternalUrl, c.ExternalUrl FROM ' . TABLE_PREFIX . 'Categories AS c WHERE c.Status = ' . STATUS_ACTIVE; $pages = $this->Conn->Query($sql, 'SrcTemplate'); $mapping = Array (); $base_url = $this->Application->BaseURL(); foreach ($pages as $src_template => $page) { // process external url, before placing in cache if ( $page['UseExternalUrl'] ) { $external_url = $page['ExternalUrl']; if ( !preg_match('/^(.*?):\/\/(.*)$/', $external_url) ) { // url without protocol will be relative url to our site $external_url = $base_url . $external_url; } $dst_template = 'external:' . $external_url; } else { $dst_template = preg_replace('/^Content\//i', '', $page['DstTemplate']); } $mapping[$src_template] = $dst_template; } if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) { $data = $this->Application->setCache('master:template_mapping', serialize($mapping)); } else { $this->Application->setDBCache('template_mapping', serialize($mapping)); } return $mapping; } /** * Returns category structure as field option list * * @return Array * @access public */ public function getStructureTreeAsOptions() { if ( (defined('IS_INSTALL') && IS_INSTALL) || !$this->Application->isAdmin ) { // no need to create category structure during install // OR on Front-End, because it's not used there return Array (); } if ( isset($this->_structureTree) ) { return $this->_structureTree; } $themes_helper = $this->Application->recallObject('ThemesHelper'); /* @var $themes_helper kThemesHelper */ $data = $this->_getStructureTree(); $theme_id = (int)$themes_helper->getCurrentThemeId(); $this->_primaryLanguageId = $this->Application->GetDefaultLanguageId(); $this->_structureTree = $this->_printChildren($data, $data['id'], $this->Application->GetVar('m_lang'), $theme_id); return $this->_structureTree; } /** * Replace links like "@@ID@@" to actual template names in given text * * @param string $text * @return string * @access public */ public function replacePageIds($text) { if ( !preg_match_all('/@@(\\d+)@@/', $text, $regs) ) { return $text; } $page_ids = $regs[1]; $sql = 'SELECT NamedParentPath, CategoryId FROM ' . TABLE_PREFIX . 'Categories WHERE CategoryId IN (' . implode(',', $page_ids) . ')'; $templates = $this->Conn->GetCol($sql, 'CategoryId'); $base_category = $this->Application->getBaseCategory(); if ( isset($templates[$base_category]) ) { // "Content" category will act as "Home Page" $templates[$base_category] .= '/Index'; } foreach ($page_ids as $page_id) { if ( !isset($templates[$page_id]) ) { // internal page was deleted, but link to it was found in given content block data continue; } $url_params = Array ('m_cat_id' => $page_id == $base_category ? 0 : $page_id, 'pass' => 'm'); $page_url = $this->Application->HREF(strtolower($templates[$page_id]), '', $url_params); /*if ($this->Application->isAdmin) { $page_url = preg_replace('/&(admin|editing_mode)=[\d]/', '', $page_url); }*/ $text = str_replace('@@' . $page_id . '@@', $page_url, $text); } return $text; } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/helpers/language_import_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/language_import_helper.php (revision 16394) +++ branches/5.3.x/core/units/helpers/language_import_helper.php (revision 16395) @@ -1,1307 +1,1315 @@ <?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. */ /** * Language pack format version description * * v1 * ========== * All language properties are separate nodes inside <LANGUAGE> node. There are * two more nodes PHRASES and EVENTS for phrase and email event translations. * * v2 * ========== * All data, that will end up in Language table is now attributes of LANGUAGE node * and is name exactly as field name, that will be used to store that data. * * v4 * ========== * Hint & Column translation added to each phrase translation * * v5 * ========== * Use separate xml nodes for subject, headers, html & plain translations * * v6 * ========== * Added e-mail design templates * */ defined('FULL_PATH') or die('restricted access!'); class LanguageImportHelper extends kHelper { /** * Overwrite existing translations during import */ const OVERWRITE_EXISTING = 1; /** * New translations are added as synced with other languages */ const SYNC_ADDED = 2; /** * Current Language in import * * @var LanguagesItem */ var $lang_object = null; /** * Current user's IP address * * @var string */ var $ip_address = ''; /** * Event type + name mapping to id (from system) * * @var Array */ var $events_hash = Array (); /** * Language pack import mode * * @var int */ var $_importOptions = 0; /** * Language IDs, that were imported * * @var Array */ var $_languages = Array (); /** * Temporary table names to perform import on * * @var Array */ var $_tables = Array (); /** * Phrase types allowed for import/export operations * * @var Array */ var $phrase_types_allowed = Array (); /** * Encoding, used for language pack exporting * * @var string */ var $_exportEncoding = 'base64'; /** * Exported data limits (all or only specified ones) * * @var Array */ var $_exportLimits = Array ( 'phrases' => false, 'email-template' => false, 'country-state' => false, ); /** * Debug language pack import process * * @var bool */ var $_debugMode = false; /** * Latest version of language pack format. Versions are not backwards compatible! * * @var int */ var $_latestVersion = 6; /** * Prefix-based serial numbers, that should be changed after import is finished * * @var Array */ var $changedPrefixes = Array (); public function __construct() { parent::__construct(); // "core/install/english.lang", phrase count: 3318, xml parse time on windows: 10s, insert time: 0.058s kUtil::setResourceLimit(); $this->lang_object = $this->Application->recallObject('lang.import', null, Array ('skip_autoload' => true)); if (!(defined('IS_INSTALL') && IS_INSTALL)) { // perform only, when not in installation mode $this->_updateEventsCache(); } $this->ip_address = $this->Application->getClientIp(); // $this->_debugMode = $this->Application->isDebugMode(); } /** * Tells if given option is enabled * * @param int $option_bit * @return bool * @access protected */ protected function hasOption($option_bit) { return ($this->_importOptions & $option_bit) == $option_bit; } /** * Sets import option * * @param int $option_bit * @param bool $enabled * @return void * @access public */ public function setOption($option_bit, $enabled = true) { if ( $enabled ) { $this->_importOptions |= $option_bit; } else { $this->_importOptions = $this->_importOptions & ~$option_bit; } } /** * Performs import of given language pack (former Parse method) * * @param string $filename * @param string $phrase_types * @param Array $module_ids * @return bool */ function performImport($filename, $phrase_types, $module_ids) { // define the XML parsing routines/functions to call based on the handler path if (!file_exists($filename) || !$phrase_types /*|| !$module_ids*/) { return false; } if ($this->_debugMode) { $start_time = microtime(true); $this->Application->Debugger->appendHTML(__CLASS__ . '::' . __FUNCTION__ . '("' . $filename . '")'); } if (defined('IS_INSTALL') && IS_INSTALL) { // new events could be added during module upgrade $this->_updateEventsCache(); } $phrase_types = explode('|', substr($phrase_types, 1, -1) ); // $module_ids = explode('|', substr($module_ids, 1, -1) ); $this->phrase_types_allowed = array_flip($phrase_types); $this->_parseXML($filename); // copy data from temp tables to live foreach ($this->_languages as $language_id) { $this->_performUpgrade($language_id, 'phrases', 'PhraseKey', Array ('l%s_Translation', 'l%s_HintTranslation', 'l%s_ColumnTranslation', 'l%s_TranslateFrom', 'PhraseType')); $this->_performUpgrade($language_id, 'email-template', 'TemplateId', Array ('l%s_Subject', 'Headers', 'l%s_HtmlBody', 'l%s_PlainTextBody', 'l%s_TranslateFrom')); $this->_performUpgrade($language_id, 'country-state', 'CountryStateId', Array ('l%s_Name')); } $this->_initImportTables(true); $this->changedPrefixes = array_unique($this->changedPrefixes); foreach ($this->changedPrefixes as $prefix) { $this->Application->incrementCacheSerial($prefix); } if ($this->_debugMode) { $this->Application->Debugger->appendHTML(__CLASS__ . '::' . __FUNCTION__ . '("' . $filename . '"): ' . (microtime(true) - $start_time)); } return true; } /** * Creates XML file with exported language data (former Create method) * * @param string $filename filename to export into * @param Array $phrase_types phrases types to export from modules passed in $module_ids * @param Array $language_ids IDs of languages to export * @param Array $module_ids IDs of modules to export phrases from */ function performExport($filename, $phrase_types, $language_ids, $module_ids) { $fp = fopen($filename,'w'); if (!$fp || !$phrase_types || !$module_ids || !$language_ids) { return false; } $phrase_types = explode('|', substr($phrase_types, 1, -1) ); $module_ids = explode('|', substr($module_ids, 1, -1) ); $ret = '<?xml version="1.0" encoding="utf-8"?>' . "\n"; $ret .= '<LANGUAGES Version="' . $this->_latestVersion . '">' . "\n"; $export_fields = $this->_getExportFields(); // get languages $sql = 'SELECT * FROM ' . $this->Application->getUnitConfig('lang')->getTableName() . ' WHERE LanguageId IN (' . implode(',', $language_ids) . ')'; $languages = $this->Conn->Query($sql, 'LanguageId'); // get phrases $phrase_modules = $module_ids; array_push($phrase_modules, ''); // for old language packs without module $phrase_modules = $this->Conn->qstrArray($phrase_modules); // apply phrase selection limit if ($this->_exportLimits['phrases']) { $escaped_phrases = $this->Conn->qstrArray($this->_exportLimits['phrases']); $limit_where = 'Phrase IN (' . implode(',', $escaped_phrases) . ')'; } else { $limit_where = 'TRUE'; } $sql = 'SELECT * FROM ' . $this->Application->getUnitConfig('phrases')->getTableName() . ' WHERE PhraseType IN (' . implode(',', $phrase_types) . ') AND Module IN (' . implode(',', $phrase_modules) . ') AND ' . $limit_where . ' ORDER BY Phrase'; $phrases = $this->Conn->Query($sql, 'PhraseId'); // email events $module_sql = preg_replace('/(.*),/U', 'INSTR(Module,\'\\1\') OR ', implode(',', $module_ids) . ','); // apply event selection limit if ($this->_exportLimits['email-template']) { $escaped_email_templates = $this->Conn->qstrArray($this->_exportLimits['email-template']); $limit_where = 'TemplateName IN (' . implode(',', $escaped_email_templates) . ')'; } else { $limit_where = 'TRUE'; } $sql = 'SELECT * FROM ' . $this->Application->getUnitConfig('email-template')->getTableName() . ' WHERE `Type` IN (' . implode(',', $phrase_types) . ') AND (' . substr($module_sql, 0, -4) . ') AND ' . $limit_where . ' ORDER BY TemplateName, `Type`'; $email_templates = $this->Conn->Query($sql, 'TemplateId'); if ( in_array('Core', $module_ids) ) { if ($this->_exportLimits['country-state']) { $escaped_countries = $this->Conn->qstrArray($this->_exportLimits['country-state']); $limit_where = '`IsoCode` IN (' . implode(',', $escaped_countries) . ')'; } else { $limit_where = 'TRUE'; } $country_table = $this->Application->getUnitConfig('country-state')->getTableName(); // countries $sql = 'SELECT * FROM ' . $country_table . ' WHERE Type = ' . DESTINATION_TYPE_COUNTRY . ' AND ' . $limit_where . ' ORDER BY `IsoCode`'; $countries = $this->Conn->Query($sql, 'CountryStateId'); // states $sql = 'SELECT state.* FROM ' . $country_table . ' state JOIN ' . $country_table . ' country ON country.CountryStateId = state.StateCountryId WHERE state.Type = ' . DESTINATION_TYPE_STATE . ' AND ' . str_replace('`IsoCode`', 'country.`IsoCode`', $limit_where) . ' ORDER BY state.`IsoCode`'; $states = $this->Conn->Query($sql, 'CountryStateId'); foreach ($states as $state_id => $state_data) { $country_id = $state_data['StateCountryId']; if ( !array_key_exists('States', $countries[$country_id]) ) { $countries[$country_id]['States'] = Array (); } $countries[$country_id]['States'][] = $state_id; } } foreach ($languages as $language_id => $language_info) { // language $ret .= "\t" . '<LANGUAGE Encoding="' . $this->_exportEncoding . '"'; foreach ($export_fields as $export_field) { $ret .= ' ' . $export_field . '="' . kUtil::escape($language_info[$export_field], kUtil::ESCAPE_HTML) . '"'; } $ret .= '>' . "\n"; // filename replacements $replacements = $language_info['FilenameReplacements']; if ( $replacements ) { $ret .= "\t\t" . '<REPLACEMENTS>' . $this->_exportConvert($replacements) . '</REPLACEMENTS>' . "\n"; } // e-mail design templates if ( $language_info['HtmlEmailTemplate'] || $language_info['TextEmailTemplate'] ) { $ret .= "\t\t" . '<EMAILDESIGNS>' . "\n"; if ( $language_info['HtmlEmailTemplate'] ) { $ret .= "\t\t\t" . '<HTML>' . $this->_exportConvert($language_info['HtmlEmailTemplate']) . '</HTML>' . "\n"; } if ( $language_info['TextEmailTemplate'] ) { $ret .= "\t\t\t" . '<TEXT>' . $this->_exportConvert($language_info['TextEmailTemplate']) . '</TEXT>' . "\n"; } $ret .= "\t\t" . '</EMAILDESIGNS>' . "\n"; } // phrases if ($phrases) { $ret .= "\t\t" . '<PHRASES>' . "\n"; foreach ($phrases as $phrase_id => $phrase) { $translation = $phrase['l' . $language_id . '_Translation']; $hint_translation = $phrase['l' . $language_id . '_HintTranslation']; $column_translation = $phrase['l' . $language_id . '_ColumnTranslation']; if (!$translation) { // phrase is not translated on given language continue; } if ( $this->_exportEncoding == 'base64' ) { $hint_translation = base64_encode($hint_translation); $column_translation = base64_encode($column_translation); } else { $hint_translation = kUtil::escape($hint_translation, kUtil::ESCAPE_HTML); $column_translation = kUtil::escape($column_translation, kUtil::ESCAPE_HTML); } $attributes = Array ( 'Label="' . $phrase['Phrase'] . '"', 'Module="' . $phrase['Module'] . '"', 'Type="' . $phrase['PhraseType'] . '"' ); if ( $phrase['l' . $language_id . '_HintTranslation'] ) { $attributes[] = 'Hint="' . $hint_translation . '"'; } if ( $phrase['l' . $language_id . '_ColumnTranslation'] ) { $attributes[] = 'Column="' . $column_translation . '"'; } $ret .= "\t\t\t" . '<PHRASE ' . implode(' ', $attributes) . '>' . $this->_exportConvert($translation) . '</PHRASE>' . "\n"; } $ret .= "\t\t" . '</PHRASES>' . "\n"; } // email events if ($email_templates) { $ret .= "\t\t" . '<EVENTS>' . "\n"; foreach ($email_templates as $template_data) { $fields_hash = Array ( 'HEADERS' => $template_data['Headers'], 'SUBJECT' => $template_data['l' . $language_id . '_Subject'], 'HTMLBODY' => $template_data['l' . $language_id . '_HtmlBody'], 'PLAINTEXTBODY' => $template_data['l' . $language_id . '_PlainTextBody'], ); $data = ''; foreach ($fields_hash as $xml_node => $xml_content) { if ( $xml_content ) { $data .= "\t\t\t\t" . '<' . $xml_node . '>' . $this->_exportConvert($xml_content) . '</' . $xml_node . '>' . "\n"; } } if ( $data ) { $ret .= "\t\t\t" . '<EVENT Event="' . $template_data['TemplateName'] . '" Type="' . $template_data['Type'] . '">' . "\n" . $data . "\t\t\t" . '</EVENT>' . "\n"; } } $ret .= "\t\t" . '</EVENTS>' . "\n"; } if (in_array('Core', $module_ids) && $countries) { $ret .= "\t\t" . '<COUNTRIES>' . "\n"; foreach ($countries as $country_data) { $translation = $country_data['l' . $language_id . '_Name']; if (!$translation) { // country is not translated on given language continue; } $data = $this->_exportEncoding == 'base64' ? base64_encode($translation) : $translation; if (array_key_exists('States', $country_data)) { $ret .= "\t\t\t" . '<COUNTRY Iso="' . $country_data['IsoCode'] . '" Translation="' . $data . '">' . "\n"; foreach ($country_data['States'] as $state_id) { $translation = $states[$state_id]['l' . $language_id . '_Name']; if (!$translation) { // state is not translated on given language continue; } $data = $this->_exportEncoding == 'base64' ? base64_encode($translation) : $translation; $ret .= "\t\t\t\t" . '<STATE Iso="' . $states[$state_id]['IsoCode'] . '" Translation="' . $data . '"/>' . "\n"; } $ret .= "\t\t\t" . '</COUNTRY>' . "\n"; } else { $ret .= "\t\t\t" . '<COUNTRY Iso="' . $country_data['IsoCode'] . '" Translation="' . $data . '"/>' . "\n"; } } $ret .= "\t\t" . '</COUNTRIES>' . "\n"; } $ret .= "\t" . '</LANGUAGE>' . "\n"; } $ret .= '</LANGUAGES>'; fwrite($fp, $ret); fclose($fp); return true; } /** * Converts string before placing into export file * * @param string $string * @return string * @access protected */ protected function _exportConvert($string) { return $this->_exportEncoding == 'base64' ? base64_encode($string) : '<![CDATA[' . $string . ']]>'; } /** * Sets language pack encoding (not charset) used during export * * @param string $encoding */ function setExportEncoding($encoding) { $this->_exportEncoding = $encoding; } /** * Sets language pack data limit for export * * @param string $prefix * @param string $data */ function setExportLimit($prefix, $data = null) { if ( !isset($data) ) { $key_field = $prefix == 'phrases' ? 'Phrase' : 'TemplateName'; $ids = $this->getExportIDs($prefix); $config = $this->Application->getUnitConfig($prefix); $sql = 'SELECT ' . $key_field . ' FROM ' . $config->getTableName() . ' WHERE ' . $config->getIDField() . ' IN (' . $ids . ')'; $rows = $this->Conn->GetIterator($sql); if ( count($rows) ) { $data = ''; foreach ($rows as $row) { $data .= ',' . $row[$key_field]; } $data = substr($data, 1); } } if ( !is_array($data) ) { $data = str_replace(',', "\n", $data); $data = preg_replace("/\n+/", "\n", str_replace("\r", '', trim($data))); $data = $data ? array_map('trim', explode("\n", $data)) : Array (); } $this->_exportLimits[$prefix] = $data; } /** * Performs upgrade of given language pack part * * @param int $language_id * @param string $prefix * @param string $unique_field * @param Array $data_fields */ function _performUpgrade($language_id, $prefix, $unique_field, $data_fields) { $live_records = $this->_getTableData($language_id, $prefix, $unique_field, $data_fields[0], false); $temp_records = $this->_getTableData($language_id, $prefix, $unique_field, $data_fields[0], true); if ( !$temp_records ) { // no data for given language return; } // perform insert for records, that are missing in live table $config = $this->Application->getUnitConfig($prefix); $to_insert = array_diff($temp_records, $live_records); if ( $to_insert ) { $to_insert = $this->Conn->qstrArray($to_insert); $sql = 'INSERT INTO ' . $config->getTableName() . ' SELECT * FROM ' . $this->_tables[$prefix] . ' WHERE ' . $unique_field . ' IN (' . implode(',', $to_insert) . ')'; $this->Conn->Query($sql); // new records were added $this->changedPrefixes[] = $prefix; } // perform update for records, that are present in live table $to_update = array_diff($temp_records, $to_insert); if ( $to_update ) { $to_update = $this->Conn->qstrArray($to_update); $sql = 'UPDATE ' . $config->getTableName() . ' live SET '; foreach ($data_fields as $index => $data_field) { $data_field = sprintf($data_field, $language_id); $sql .= ' live.' . $data_field . ' = ( SELECT temp' . $index . '.' . $data_field . ' FROM ' . $this->_tables[$prefix] . ' temp' . $index . ' WHERE temp' . $index . '.' . $unique_field . ' = live.' . $unique_field . ' ),'; } $sql = substr($sql, 0, -1); // cut last comma $where_clause = Array ( // this won't make any difference, but just in case $unique_field . ' IN (' . implode(',', $to_update) . ')', ); if ( !$this->hasOption(self::OVERWRITE_EXISTING) ) { // empty OR not set $data_field = sprintf($data_fields[0], $language_id); $where_clause[] = '(' . $data_field . ' = "") OR (' . $data_field . ' IS NULL)'; } if ( $where_clause ) { $sql .= "\n" . 'WHERE (' . implode(') AND (', $where_clause) . ')'; } $this->Conn->Query($sql); if ( $this->Conn->getAffectedRows() > 0 ) { // existing records were updated $this->changedPrefixes[] = $prefix; } } } /** * Returns data from given table used for language pack upgrade * * @param int $language_id * @param string $prefix * @param string $unique_field * @param string $data_field * @param bool $temp_mode * @return Array */ function _getTableData($language_id, $prefix, $unique_field, $data_field, $temp_mode = false) { $data_field = sprintf($data_field, $language_id); $table_name = $this->Application->getUnitConfig($prefix)->getTableName(); if ($temp_mode) { // for temp table get only records, that have contents on given language (not empty and isset) $sql = 'SELECT ' . $unique_field . ' FROM ' . $this->Application->GetTempName($table_name, 'prefix:' . $prefix) . ' WHERE (' . $data_field . ' <> "") AND (' . $data_field . ' IS NOT NULL)'; } else { // for live table get all records, no matter on what language $sql = 'SELECT ' . $unique_field . ' FROM ' . $table_name; } return $this->Conn->GetCol($sql); } function _parseXML($filename) { if ( $this->_debugMode ) { $start_time = microtime(true); $this->Application->Debugger->appendHTML(__CLASS__ . '::' . __FUNCTION__ . '("' . $filename . '")'); } $languages = simplexml_load_file($filename); if ( $languages === false) { // invalid language pack contents return false; } // PHP 5.3 version would be: $languages->count() if ( count($languages->children()) ) { $this->_processLanguages($languages); $this->_processLanguageData($languages); } if ( $this->_debugMode ) { $this->Application->Debugger->appendHTML(__CLASS__ . '::' . __FUNCTION__ . '("' . $filename . '"): ' . (microtime(true) - $start_time)); } return true; } /** * Creates temporary tables, used during language import * * @param bool $drop_only */ function _initImportTables($drop_only = false) { $this->_tables['phrases'] = $this->_prepareTempTable('phrases', $drop_only); $this->_tables['email-template'] = $this->_prepareTempTable('email-template', $drop_only); $this->_tables['country-state'] = $this->_prepareTempTable('country-state', $drop_only); } /** * Create temp table for prefix, if table already exists, then delete it and create again * * @param string $prefix * @param bool $drop_only * @return string Name of created temp table * @access protected */ protected function _prepareTempTable($prefix, $drop_only = false) { $config = $this->Application->getUnitConfig($prefix); $id_field = $config->getIDField(); $table = $config->getTableName(); $temp_table = $this->Application->GetTempName($table); $sql = 'DROP TABLE IF EXISTS %s'; $this->Conn->Query( sprintf($sql, $temp_table) ); if (!$drop_only) { $sql = 'CREATE TABLE ' . $temp_table . ' SELECT * FROM ' . $table . ' WHERE 0'; $this->Conn->Query($sql); $sql = 'ALTER TABLE %1$s CHANGE %2$s %2$s INT(11) NOT NULL DEFAULT "0"'; $this->Conn->Query( sprintf($sql, $temp_table, $id_field) ); switch ($prefix) { case 'phrases': $unique_field = 'PhraseKey'; break; case 'email-template': $unique_field = 'TemplateId'; break; case 'country-state': $unique_field = 'CountryStateId'; break; default: throw new Exception('Unknown prefix "<strong>' . $prefix . '</strong>" during language pack import'); break; } $sql = 'ALTER TABLE ' . $temp_table . ' ADD UNIQUE (' . $unique_field . ')'; $this->Conn->Query($sql); } return $temp_table; } /** * Prepares mapping between event name+type and their ids in database * */ function _updateEventsCache() { - $sql = 'SELECT TemplateId, CONCAT(TemplateName,"_",Type) AS EventMix - FROM ' . $this->Application->getUnitConfig('email-template')->getTableName(); + $table_name = $this->Application->getUnitConfig('email-template')->getTableName(); + + // During upgrade from 5.1.x to 5.2.x without this there will be tons of notices. + if ( defined('IS_INSTALL') && IS_INSTALL && !$this->Conn->TableFound($table_name, true) ) { + $this->events_hash = array(); + + return; + } + $sql = 'SELECT TemplateId, CONCAT(TemplateName,"_",Type) AS EventMix + FROM ' . $table_name; $this->events_hash = $this->Conn->GetCol($sql, 'EventMix'); } /** * Returns language fields to be exported * * @return Array */ function _getExportFields() { return Array ( 'PackName', 'LocalName', 'DateFormat', 'ShortDateFormat', 'TimeFormat', 'ShortTimeFormat', 'InputDateFormat', 'InputTimeFormat', 'DecimalPoint', 'ThousandSep', 'UnitSystem', 'Locale', 'UserDocsUrl' ); } /** * Processes parsed XML * * @param SimpleXMLElement $languages */ function _processLanguages($languages) { $version = (int)$languages['Version']; if ( !$version ) { // version missing -> guess it if ( $languages->DATEFORMAT->getName() ) { $version = 1; } elseif ( (string)$languages->LANGUAGE['Charset'] != '' ) { $version = 2; } } if ( $version == 1 ) { $field_mapping = Array ( 'DATEFORMAT' => 'DateFormat', 'TIMEFORMAT' => 'TimeFormat', 'INPUTDATEFORMAT' => 'InputDateFormat', 'INPUTTIMEFORMAT' => 'InputTimeFormat', 'DECIMAL' => 'DecimalPoint', 'THOUSANDS' => 'ThousandSep', 'CHARSET' => 'Charset', 'UNITSYSTEM' => 'UnitSystem', 'DOCS_URL' => 'UserDocsUrl', ); } else { $export_fields = $this->_getExportFields(); } foreach ($languages as $language_node) { $fields_hash = Array ( 'PackName' => (string)$language_node['PackName'], 'LocalName' => (string)$language_node['PackName'], 'Encoding' => (string)$language_node['Encoding'], 'SynchronizationModes' => Language::SYNCHRONIZE_DEFAULT, ); if ( $version > 1 ) { foreach ($export_fields as $export_field) { if ( (string)$language_node[$export_field] ) { $fields_hash[$export_field] = (string)$language_node[$export_field]; } } } $container_nodes = Array ('PHRASES', 'EVENTS', 'COUNTRIES'); foreach ($language_node as $sub_node) { /* @var $sub_node SimpleXMLElement */ if ( in_array($sub_node->getName(), $container_nodes) ) { continue; } switch ($sub_node->getName()) { case 'REPLACEMENTS': // added since v2 $replacements = (string)$sub_node; if ( $fields_hash['Encoding'] != 'plain' ) { $replacements = base64_decode($replacements); } $fields_hash['FilenameReplacements'] = $replacements; break; case 'EMAILDESIGNS': // added since v6 $this->_decodeEmailDesignTemplate($fields_hash, 'HtmlEmailTemplate', (string)$sub_node->HTML); $this->_decodeEmailDesignTemplate($fields_hash, 'TextEmailTemplate', (string)$sub_node->TEXT); break; default: if ( $version == 1 ) { $fields_hash[$field_mapping[$sub_node->Name]] = (string)$sub_node; } break; } } $this->_processLanguage($fields_hash); } if ( !defined('IS_INSTALL') || !IS_INSTALL ) { $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ // create ML columns for new languages $ml_helper->resetState(); $ml_helper->massCreateFields(); } // create temp tables after new language columns were added $this->_initImportTables(); } /** * Processes parsed XML * * @param SimpleXMLElement $languages */ function _processLanguageData($languages) { foreach ($languages as $language_node) { $encoding = (string)$language_node['Encoding']; $language_id = $this->_languages[kUtil::crc32((string)$language_node['PackName'])]; $container_nodes = Array ('PHRASES', 'EVENTS', 'COUNTRIES'); foreach ($language_node as $sub_node) { /* @var $sub_node SimpleXMLElement */ if ( !in_array($sub_node->getName(), $container_nodes) || !count($sub_node->children()) ) { // PHP 5.3 version would be: !$sub_node->count() continue; } switch ($sub_node->getName()) { case 'PHRASES': $this->_processPhrases($sub_node, $language_id, $encoding); break; case 'EVENTS': $this->_processEvents($sub_node, $language_id, $encoding); break; case 'COUNTRIES': $this->_processCountries($sub_node, $language_id, $encoding); break; } } } } /** * Decodes e-mail template design from language pack * * @param Array $fields_hash * @param string $field * @param string $design_template */ protected function _decodeEmailDesignTemplate(&$fields_hash, $field, $design_template) { if ( $fields_hash['Encoding'] != 'plain' ) { $design_template = base64_decode($design_template); } if ( $design_template ) { $fields_hash[$field] = $design_template; } } /** * Performs phases import * * @param SimpleXMLElement $phrases * @param int $language_id * @param string $language_encoding */ function _processPhrases($phrases, $language_id, $language_encoding) { static $other_translations = Array (); $primary_language = $this->Application->GetDefaultLanguageId(); $translate_from = $this->hasOption(self::SYNC_ADDED) || $primary_language == $language_id ? 0 : $primary_language; if ( $this->Application->isDebugMode() ) { $this->Application->Debugger->profileStart('L[' . $language_id . ']P', 'Language: ' . $language_id . '; Phrases Import'); } foreach ($phrases as $phrase_node) { /* @var $phrase_node SimpleXMLElement */ $phrase_key = mb_strtoupper($phrase_node['Label']); $fields_hash = Array ( 'Phrase' => (string)$phrase_node['Label'], 'PhraseKey' => $phrase_key, 'PhraseType' => (int)$phrase_node['Type'], 'Module' => (string)$phrase_node['Module'] ? (string)$phrase_node['Module'] : 'Core', 'LastChanged' => TIMENOW, 'LastChangeIP' => $this->ip_address, ); $translation = (string)$phrase_node; $hint_translation = (string)$phrase_node['Hint']; $column_translation = (string)$phrase_node['Column']; if ( array_key_exists($fields_hash['PhraseType'], $this->phrase_types_allowed) ) { if ( $language_encoding != 'plain' ) { $translation = base64_decode($translation); $hint_translation = base64_decode($hint_translation); $column_translation = base64_decode($column_translation); } if ( !array_key_exists($phrase_key, $other_translations) ) { // ensure translation in every language to make same column count in every insert $other_translations[$phrase_key] = Array (); foreach ($this->_languages as $other_language_id) { $other_translations[$phrase_key]['l' . $other_language_id . '_Translation'] = ''; $other_translations[$phrase_key]['l' . $other_language_id . '_HintTranslation'] = ''; $other_translations[$phrase_key]['l' . $other_language_id . '_ColumnTranslation'] = ''; $other_translations[$phrase_key]['l' . $other_language_id . '_TranslateFrom'] = 0; } } $other_translations[$phrase_key]['l' . $language_id . '_Translation'] = $translation; $other_translations[$phrase_key]['l' . $language_id . '_HintTranslation'] = $hint_translation; $other_translations[$phrase_key]['l' . $language_id . '_ColumnTranslation'] = $column_translation; $other_translations[$phrase_key]['l' . $language_id . '_TranslateFrom'] = $translate_from; $fields_hash = array_merge($fields_hash, $other_translations[$phrase_key]); $this->Conn->doInsert($fields_hash, $this->_tables['phrases'], 'REPLACE', false); } } if ( $this->Application->isDebugMode() ) { $this->Application->Debugger->profileFinish('L[' . $language_id . ']P', 'Language: ' . $language_id . '; Phrases Import'); } $this->Conn->doInsert($fields_hash, $this->_tables['phrases'], 'REPLACE'); } /** * Performs email event import * * @param SimpleXMLElement $events * @param int $language_id * @param string $language_encoding */ function _processEvents($events, $language_id, $language_encoding) { static $other_translations = Array (); $primary_language = $this->Application->GetDefaultLanguageId(); $translate_from = $this->hasOption(self::SYNC_ADDED) || $primary_language == $language_id ? 0 : $primary_language; if ( $this->Application->isDebugMode() ) { $this->Application->Debugger->profileStart('L[' . $language_id . ']E', 'Language: ' . $language_id . '; Events Import'); } $email_template_helper = $this->Application->recallObject('kEmailTemplateHelper'); /* @var $email_template_helper kEmailTemplateHelper */ foreach ($events as $event_node) { /* @var $event_node SimpleXMLElement */ $message_type = (string)$event_node['MessageType']; $email_template_id = $this->_getEmailTemplateId((string)$event_node['Event'], (int)$event_node['Type']); if ( !$email_template_id ) { continue; } $fields_hash = Array ( 'TemplateId' => $email_template_id, 'TemplateName' => (string)$event_node['Event'], 'Type' => (int)$event_node['Type'], ); if ( $message_type == '' ) { $parsed = $email_template_helper->parseTemplate($event_node, ''); $parsed = array_map($language_encoding == 'plain' ? 'rtrim' : 'base64_decode', $parsed); } else { $template = $language_encoding == 'plain' ? rtrim($event_node) : base64_decode($event_node); $parsed = $email_template_helper->parseTemplate($template, $message_type); } if ( !array_key_exists($email_template_id, $other_translations) ) { // ensure translation in every language to make same column count in every insert $other_translations[$email_template_id] = Array (); foreach ($this->_languages as $other_language_id) { $other_translations[$email_template_id]['l' . $other_language_id . '_Subject'] = ''; $other_translations[$email_template_id]['l' . $other_language_id . '_HtmlBody'] = ''; $other_translations[$email_template_id]['l' . $other_language_id . '_PlainTextBody'] = ''; $other_translations[$email_template_id]['l' . $other_language_id . '_TranslateFrom'] = 0; } } $other_translations[$email_template_id]['l' . $language_id . '_Subject'] = $parsed['Subject']; $other_translations[$email_template_id]['l' . $language_id . '_HtmlBody'] = $parsed['HtmlBody']; $other_translations[$email_template_id]['l' . $language_id . '_PlainTextBody'] = $parsed['PlainTextBody']; $other_translations[$email_template_id]['l' . $language_id . '_TranslateFrom'] = $translate_from; if ( $parsed['Headers'] ) { $other_translations[$email_template_id]['Headers'] = $parsed['Headers']; } elseif ( !$parsed['Headers'] && !array_key_exists('Headers', $other_translations[$email_template_id]) ) { $other_translations[$email_template_id]['Headers'] = $parsed['Headers']; } $fields_hash = array_merge($fields_hash, $other_translations[$email_template_id]); $this->Conn->doInsert($fields_hash, $this->_tables['email-template'], 'REPLACE', false); } if ( $this->Application->isDebugMode() ) { $this->Application->Debugger->profileFinish('L[' . $language_id . ']E', 'Language: ' . $language_id . '; Events Import'); } if ( isset($fields_hash) ) { // at least one email event in language pack was found in database $this->Conn->doInsert($fields_hash, $this->_tables['email-template'], 'REPLACE'); } } /** * Performs country_state translation import * * @param SimpleXMLElement $country_states * @param int $language_id * @param string $language_encoding * @param bool $process_states * @return void */ function _processCountries($country_states, $language_id, $language_encoding, $process_states = false) { static $other_translations = Array (); foreach ($country_states as $country_state_node) { /* @var $country_state_node SimpleXMLElement */ if ( $process_states ) { $country_state_id = $this->_getStateId((string)$country_states['Iso'], (string)$country_state_node['Iso']); } else { $country_state_id = $this->_getCountryId((string)$country_state_node['Iso']); } if ( !$country_state_id ) { continue; } if ( $language_encoding == 'plain' ) { $translation = rtrim($country_state_node['Translation']); } else { $translation = base64_decode($country_state_node['Translation']); } $fields_hash = Array ('CountryStateId' => $country_state_id); if ( !array_key_exists($country_state_id, $other_translations) ) { // ensure translation in every language to make same column count in every insert $other_translations[$country_state_id] = Array (); foreach ($this->_languages as $other_language_id) { $other_translations[$country_state_id]['l' . $other_language_id . '_Name'] = ''; } } $other_translations[$country_state_id]['l' . $language_id . '_Name'] = $translation; $fields_hash = array_merge($fields_hash, $other_translations[$country_state_id]); $this->Conn->doInsert($fields_hash, $this->_tables['country-state'], 'REPLACE', false); // PHP 5.3 version would be: $country_state_node->count() if ( !$process_states && count($country_state_node->children()) ) { $this->_processCountries($country_state_node, $language_id, $language_encoding, true); } } $this->Conn->doInsert($fields_hash, $this->_tables['country-state'], 'REPLACE'); } /** * Creates/updates language based on given fields and returns it's id * * @param Array $fields_hash * @return int */ function _processLanguage($fields_hash) { // 1. get language from database $sql = 'SELECT ' . $this->lang_object->IDField . ' FROM ' . $this->lang_object->TableName . ' WHERE PackName = ' . $this->Conn->qstr($fields_hash['PackName']); $language_id = $this->Conn->GetOne($sql); if ( $language_id ) { // 2. language found -> update, when allowed $this->lang_object->Load($language_id); if ( $this->hasOption(self::OVERWRITE_EXISTING) ) { // update live language record based on data from xml $this->lang_object->SetFieldsFromHash($fields_hash); $this->lang_object->Update(); } } else { // 3. language not found -> create $this->lang_object->SetFieldsFromHash($fields_hash); $this->lang_object->SetDBField('Enabled', STATUS_ACTIVE); if ( $this->lang_object->Create() ) { $language_id = $this->lang_object->GetID(); if ( defined('IS_INSTALL') && IS_INSTALL ) { // language created during install becomes admin interface language $this->lang_object->setPrimary(true, true); } } } // 4. collect ID of every processed language if ( !in_array($language_id, $this->_languages) ) { $this->_languages[kUtil::crc32($fields_hash['PackName'])] = $language_id; } return $language_id; } /** * Returns e-mail template id based on it's name and type * * @param string $template_name * @param string $template_type * @return int */ function _getEmailTemplateId($template_name, $template_type) { $cache_key = $template_name . '_' . $template_type; return array_key_exists($cache_key, $this->events_hash) ? $this->events_hash[$cache_key] : 0; } /** * Returns country id based on it's 3letter ISO code * * @param string $iso * @return int */ function _getCountryId($iso) { static $cache = null; if (!isset($cache)) { $sql = 'SELECT CountryStateId, IsoCode FROM ' . TABLE_PREFIX . 'CountryStates WHERE Type = ' . DESTINATION_TYPE_COUNTRY; $cache = $this->Conn->GetCol($sql, 'IsoCode'); } return array_key_exists($iso, $cache) ? $cache[$iso] : false; } /** * Returns state id based on 3letter country ISO code and 2letter state ISO code * * @param string $country_iso * @param string $state_iso * @return int */ function _getStateId($country_iso, $state_iso) { static $cache = null; if (!isset($cache)) { $sql = 'SELECT CountryStateId, CONCAT(StateCountryId, "-", IsoCode) AS IsoCode FROM ' . TABLE_PREFIX . 'CountryStates WHERE Type = ' . DESTINATION_TYPE_STATE; $cache = $this->Conn->GetCol($sql, 'IsoCode'); } $country_id = $this->_getCountryId($country_iso); return array_key_exists($country_id . '-' . $state_iso, $cache) ? $cache[$country_id . '-' . $state_iso] : false; } /** * Returns comma-separated list of IDs, that will be exported * * @param string $prefix * @return string * @access public */ public function getExportIDs($prefix) { $ids = $this->Application->RecallVar($prefix . '_selected_ids'); if ( $ids ) { // some records were selected in grid return $ids; } $tag_params = Array ( 'grid' => $prefix == 'phrases' ? 'Phrases' : 'Emails', 'skip_counting' => 1, 'per_page' => -1 ); $list = $this->Application->recallObject($prefix, $prefix . '_List', $tag_params); /* @var $list kDBList */ $sql = $list->getCountSQL($list->GetSelectSQL()); $sql = str_replace('COUNT(*) AS count', $list->TableName . '.' . $list->IDField, $sql); $ids = ''; $rows = $this->Conn->GetIterator($sql); if ( count($rows) ) { foreach ($rows as $row) { $ids .= ',' . $row[$list->IDField]; } $ids = substr($ids, 1); } return $ids; } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/helpers/priority_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/priority_helper.php (revision 16394) +++ branches/5.3.x/core/units/helpers/priority_helper.php (revision 16395) @@ -1,250 +1,259 @@ <?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 kPriorityHelper extends kHelper { /** * Prepares options for priority dropdown * * @param kEvent $event * @param bool $is_new for newly created items add new priority to the end * @param string $constrain constrain for priority selection (if any) * @param string $joins left joins, used by constrain (if any) * */ function preparePriorities($event, $is_new = false, $constrain = '', $joins = '') { $object = $event->getObject(); /* @var $object kDBItem */ $field_options = $object->GetFieldOptions('Priority'); $table_name = $event->getUnitConfig()->getTableName(); $sql = 'SELECT COUNT(*) FROM ' . $table_name . ' item_table ' . $joins; if ( $constrain ) { $sql .= ' WHERE ' . $this->normalizeConstrain($constrain); } if ( !$object->isField('OldPriority') ) { // make sure, then OldPriority field is defined $virtual_fields = $object->getVirtualFields(); $virtual_fields['OldPriority'] = Array ('type' => 'int', 'default' => 0); $object->setVirtualFields($virtual_fields); } $items_count = $this->Conn->GetOne($sql); $current_priority = $object instanceof kDBList ? 0 : $object->GetDBField('Priority'); - if ( $is_new || $current_priority == -($items_count + 1) ) { + if ( $is_new || $current_priority == -($items_count + 1) || $this->isTempTableOnly($object) ) { $items_count++; } if ( $is_new ) { // add new item to the end of list $object->SetDBField('Priority', -$items_count); $object->SetDBField('OldPriority', -$items_count); } else { // storing priority right after load for comparing when updating $object->SetDBField('OldPriority', $current_priority); } for ($i = 1; $i <= $items_count; $i++) { $field_options['options'][-$i] = $i; } $object->SetFieldOptions('Priority', $field_options); } /** + * Determines if an item only exists in temp table. + * + * @param kDBBase $object Object. + * + * @return boolean + */ + protected function isTempTableOnly(kDBBase $object) + { + if ( !$object->IsTempTable() || ($object instanceof kDBList) ) { + return false; + } + + return $object->GetID() <= 0; + } + + /** * Updates priorities for changed items * * @param kEvent $event * @param Array $changes = Array (ID => Array ('constrain' => ..., 'new' => ..., 'old' => ...), ...) * @param Array $new_ids = Array (temp_id => live_id) * @param string $constrain * @param string $joins * @return Array */ function updatePriorities($event, $changes, $new_ids, $constrain = '', $joins = '') { // TODO: no need pass external $constrain, since the one from $pair is used if ( !$changes ) { // no changes to process return Array (); } - list ($id, $pair) = each($changes); - - if ( !$id && !isset($pair['constrain']) ) { - // adding new item without constrain -> priority stays the same - return Array ($id); - } - $config = $event->getUnitConfig(); $id_field = $config->getIDField(); $table_name = $config->getTableName(); if ( $this->Application->IsTempMode($event->Prefix, $event->Special) ) { $table_name = $this->Application->GetTempName($table_name, 'prefix:' . $event->Prefix); } $ids = Array (); $not_processed = array_keys($changes); foreach ($changes as $id => $pair) { array_push($ids, $id); $constrain = isset($pair['constrain']) ? $this->normalizeConstrain($pair['constrain']) . ' AND ' : ''; if ( $pair['old'] == 'new' ) { // replace 0 with newly created item id (from $new_ids mapping) $not_processed[array_search($id, $not_processed)] = $new_ids[$id]; $id = $new_ids[$id]; $sql = 'SELECT MIN(item_table.Priority) FROM ' . $table_name . ' item_table ' . $joins . ' WHERE ' . $constrain . ' item_table.' . $id_field . ' NOT IN (' . implode(',', $not_processed) . ')'; $min_priority = (int)$this->Conn->GetOne($sql) - 1; if ( $pair['new'] < $min_priority ) { $pair['new'] = $min_priority; } $pair['old'] = $min_priority; } if ( $pair['new'] < $pair['old'] ) { $set = ' SET item_table.Priority = item_table.Priority + 1'; $where = ' WHERE ' . $constrain . ' item_table.Priority >= ' . $pair['new'] . ' AND item_table.Priority < ' . $pair['old'] . ' AND ' . $id_field . ' NOT IN (' . implode(',', $not_processed) . ')'; } elseif ( $pair['new'] > $pair['old'] ) { $set = ' SET item_table.Priority = item_table.Priority - 1'; $where = ' WHERE ' . $constrain . ' item_table.Priority > ' . $pair['old'] . ' AND item_table.Priority <= ' . $pair['new'] . ' AND ' . $id_field . ' NOT IN (' . implode(',', $not_processed) . ')'; } else { $set = ' SET item_table.Priority = ' . $pair['new']; $where = ' WHERE ' . $id_field . ' = ' . $id; } $sql = 'SELECT item_table.' . $id_field . ' FROM ' . $table_name . ' item_table ' . $joins . ' ' . $where; $ids = array_merge($ids, $this->Conn->GetCol($sql)); $q = 'UPDATE ' . $table_name . ' item_table ' . $joins . ' ' . $set . $where; $this->Conn->Query($q); unset($not_processed[array_search($id, $not_processed)]); } return $ids; } /** * Recalculates priorities * * @param kEvent $event * @param string $constrain * @param string $joins * @return Array */ function recalculatePriorities($event, $constrain = '', $joins = '') { $config = $event->getUnitConfig(); $id_field = $config->getIDField(); $table_name = $config->getTableName(); if ( $constrain ) { $constrain = $this->normalizeConstrain($constrain); } if ( $this->Application->IsTempMode($event->Prefix, $event->Special) ) { $table_name = $this->Application->GetTempName($table_name, 'prefix:' . $event->Prefix); } $sql = 'SELECT ' . $id_field . ' FROM ' . $table_name . ' item_table ' . $joins . ' ' . ($constrain ? ' WHERE ' . $constrain : '') . ' ORDER BY item_table.Priority DESC'; $items = $this->Conn->GetCol($sql); foreach ($items as $item_number => $item_id) { $sql = 'UPDATE ' . $table_name . ' SET Priority = ' . -($item_number + 1) . ' WHERE ' . $id_field . ' = ' . $item_id; $this->Conn->Query($sql); } return $items; } /** * Adds current table name into constrain if doesn't have it already (to prevent ambiguous columns during joins) * * @param string $constrain * @return string */ function normalizeConstrain($constrain) { if ( strpos($constrain, '.') === false ) { return 'item_table.' . $constrain; } return $constrain; } /** * Performs fake kDBItem::Update call, so any OnBefore/OnAfter events would be notified of priority change * * @param string $prefix * @param Array $ids */ function massUpdateChanged($prefix, $ids) { $ids = array_unique($ids); $dummy = $this->Application->recallObject($prefix . '.-dummy', null, Array ('skip_autoload' => true)); /* @var $dummy kDBItem */ $sql = $dummy->GetSelectSQL() . ' WHERE ' . $dummy->TableName . '.' . $dummy->IDField . ' IN (' . implode(',', $ids) . ')'; $records = $this->Conn->Query($sql); foreach ($records as $record) { $dummy->LoadFromHash($record); $dummy->Update(); } } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/helpers/image_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/image_helper.php (revision 16394) +++ branches/5.3.x/core/units/helpers/image_helper.php (revision 16395) @@ -1,759 +1,770 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class ImageHelper extends kHelper { /** * File helper reference * * @var FileHelper */ var $fileHelper = null; public function __construct() { parent::__construct(); ini_set('gd.jpeg_ignore_warning', 1); $this->fileHelper = $this->Application->recallObject('FileHelper'); } /** * Parses format string into array * * @param string $format sample format: "resize:300x500;wm:inc/wm.png|c|-20" * @return Array sample result: Array('max_width' => 300, 'max_height' => 500, 'wm_filename' => 'inc/wm.png', 'h_margin' => 'c', 'v_margin' => -20) */ function parseFormat($format) { $res = Array (); $format_parts = explode(';', $format); foreach ($format_parts as $format_part) { if (preg_match('/^resize:(\d*)x(\d*)$/', $format_part, $regs)) { $res['max_width'] = $regs[1]; $res['max_height'] = $regs[2]; } elseif (preg_match('/^wm:([^\|]*)\|([^\|]*)\|([^\|]*)$/', $format_part, $regs)) { $res['wm_filename'] = FULL_PATH.THEMES_PATH.'/'.$regs[1]; $res['h_margin'] = strtolower($regs[2]); $res['v_margin'] = strtolower($regs[3]); } elseif (preg_match('/^crop:([^\|]*)\|([^\|]*)$/', $format_part, $regs)) { $res['crop_x'] = strtolower($regs[1]); $res['crop_y'] = strtolower($regs[2]); } elseif ($format_part == 'img_size' || $format_part == 'img_sizes') { $res['image_size'] = true; } elseif (preg_match('/^fill:(.*)$/', $format_part, $regs)) { $res['fill'] = $regs[1]; - } elseif (preg_match('/^default:(.*)$/', $format_part, $regs)) { - $res['default'] = FULL_PATH.THEMES_PATH.'/'.$regs[1]; + } + elseif ( preg_match('/^default:(.*)$/', $format_part, $regs) ) { + $default_image = FULL_PATH . THEMES_PATH . '/' . $regs[1]; + + if ( strpos($default_image, '../') !== false ) { + $default_image = realpath($default_image); + } + + $res['default'] = $default_image; } 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. */ function ResizeImage($src_image, $max_width, $max_height = false) { $image_size = false; if (is_numeric($max_width)) { $params['max_width'] = $max_width; $params['max_height'] = $max_height; } else { $params = $this->parseFormat($max_width); if (array_key_exists('image_size', $params)) { // image_size param shouldn't affect resized file name (crc part) $image_size = $params['image_size']; unset($params['image_size']); } } if ((!$src_image || !file_exists($src_image)) && array_key_exists('default', $params) && !(defined('DBG_IMAGE_RECOVERY') && DBG_IMAGE_RECOVERY)) { $src_image = $params['default']; } if ( !strlen($src_image) || !file_exists($src_image) ) { throw new RuntimeException(sprintf('Image "%s" doesn\'t exist', $src_image)); } if ($params['max_width'] > 0 || $params['max_height'] > 0) { list ($params['target_width'], $params['target_height'], $needs_resize) = $this->GetImageDimensions($src_image, $params['max_width'], $params['max_height'], $params); if (!is_numeric($params['max_width'])) { $params['max_width'] = $params['target_width']; } if (!is_numeric($params['max_height'])) { $params['max_height'] = $params['target_height']; } $src_path = dirname($src_image); $transform_keys = Array ('crop_x', 'crop_y', 'fill', 'wm_filename', 'filter_type'); // Resize required OR watermarking required -> change resulting image name ! if ( $needs_resize || array_intersect(array_keys($params), $transform_keys) ) { // Escape replacement patterns, like "\<number>". $src_path_escaped = preg_replace('/(\\\[\d]+)/', '\\\\\1', $src_path); ksort($params); $params_hash = kUtil::crc32(serialize($this->fileHelper->makeRelative($params))); $dst_image = preg_replace( '/^' . preg_quote($src_path, '/') . '(.*)\.(.*)$/', - $src_path_escaped . DIRECTORY_SEPARATOR . 'resized\\1_' . $params_hash . '.\\2', + $src_path_escaped . '\\1_' . $params_hash . '.\\2', $src_image ); + // Keep resized version of all images under "/system/thumbs/" folder. + $dst_image = preg_replace('/^' . preg_quote(FULL_PATH, '/') . '/', '', $dst_image, 1); + $dst_image = FULL_PATH . THUMBS_PATH . $dst_image; + $this->fileHelper->CheckFolder( dirname($dst_image) ); if (!file_exists($dst_image) || filemtime($src_image) > filemtime($dst_image)) { // resized image not available OR should be recreated due source image change $params['dst_image'] = $dst_image; $image_resized = $this->ScaleImage($src_image, $params); if (!$image_resized) { // resize failed, because of server error $dst_image = $src_image; } } // resize/watermarking ok $src_image = $dst_image; } } if ($image_size) { // return only image size (resized or not) $image_info = $this->getImageInfo($src_image); return $image_info ? $image_info[3] : ''; } return $this->fileHelper->pathToUrl($src_image); } /** * Proportionally resizes given image to destination dimensions * * @param string $src_image full path to source image (already existing) * @param Array $params * @return bool */ function ScaleImage($src_image, $params) { $image_info = $this->getImageInfo($src_image); if (!$image_info) { return false; } /*list ($params['max_width'], $params['max_height'], $resized) = $this->GetImageDimensions($src_image, $params['max_width'], $params['max_height'], $params); if (!$resized) { // image dimensions are smaller or equals to required dimensions return false; }*/ if (!$this->Application->ConfigValue('ForceImageMagickResize') && function_exists('imagecreatefromjpeg')) { // try to resize using GD $resize_map = Array ( 'image/jpeg' => 'imagecreatefromjpeg:imagejpeg:jpg', 'image/gif' => 'imagecreatefromgif:imagegif:gif', 'image/png' => 'imagecreatefrompng:imagepng:png', 'image/bmp' => 'imagecreatefrombmp:imagejpeg:bmp', 'image/x-ms-bmp' => 'imagecreatefrombmp:imagejpeg:bmp', ); $mime_type = $image_info['mime']; if (!isset($resize_map[$mime_type])) { return false; } list ($read_function, $write_function, $file_extension) = explode(':', $resize_map[$mime_type]); // when source image has large dimensions (over 1MB filesize), then 16M is not enough kUtil::setResourceLimit(); $src_image_rs = @$read_function($src_image); if ($src_image_rs) { $dst_image_rs = imagecreatetruecolor($params['target_width'], $params['target_height']); // resize target size $preserve_transparency = ($file_extension == 'gif') || ($file_extension == 'png'); if ($preserve_transparency) { // preserve transparency of PNG and GIF images $dst_image_rs = $this->_preserveTransparency($src_image_rs, $dst_image_rs, $image_info[2]); } // 1. resize imagecopyresampled($dst_image_rs, $src_image_rs, 0, 0, 0, 0, $params['target_width'], $params['target_height'], $image_info[0], $image_info[1]); $watermark_size = 'target'; if (array_key_exists('crop_x', $params) || array_key_exists('crop_y', $params)) { // 2.1. crop image to given size $dst_image_rs =& $this->_cropImage($dst_image_rs, $params, $preserve_transparency ? $image_info[2] : false); $watermark_size = 'max'; } elseif (array_key_exists('fill', $params)) { // 2.2. fill image margins from resize with given color $dst_image_rs =& $this->_applyFill($dst_image_rs, $params, $preserve_transparency ? $image_info[2] : false); $watermark_size = 'max'; } // 3. apply watermark $dst_image_rs =& $this->_applyWatermark($dst_image_rs, $params[$watermark_size . '_width'], $params[$watermark_size . '_height'], $params); if ($write_function == 'imagegif') { return @$write_function($dst_image_rs, $params['dst_image']); } // 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); } } Index: branches/5.3.x/core/units/helpers/upload_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/upload_helper.php (revision 16394) +++ branches/5.3.x/core/units/helpers/upload_helper.php (revision 16395) @@ -1,332 +1,335 @@ <?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); } $value = $this->Application->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/'); + $thumbs_path = preg_replace('/^' . preg_quote(FULL_PATH, '/') . '/', '', $tmp_path, 1); + $thumbs_path = FULL_PATH . THUMBS_PATH . $thumbs_path; + + if ( file_exists($thumbs_path) ) { + $this->deleteTempFiles($thumbs_path); } 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) { + 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'); $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/cat_dbitem_export_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/cat_dbitem_export_helper.php (revision 16394) +++ branches/5.3.x/core/units/helpers/cat_dbitem_export_helper.php (revision 16395) @@ -1,1581 +1,1587 @@ <?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' ) { $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); } } } /** * 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 */ + /** @var kDBList $items_list */ + $items_list = $this->Application->recallObject( + $this->curItem->Prefix . '.' . $this->exportOptions['export_special'], + $this->curItem->Prefix . '_List', + array('grid' => $this->exportOptions['export_grid']) + ); $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 + $sql .= ' ORDER BY ci.PrimaryCat DESC, c.TreeLeft ASC, item_table.' . $this->curItem->IDField . ' ASC'; } 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['export_special'] = $this->Application->RecallVar('export_special'); + $field_values['export_grid'] = $this->Application->RecallVar('export_grid'); } $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/multilanguage_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/multilanguage_helper.php (revision 16394) +++ branches/5.3.x/core/units/helpers/multilanguage_helper.php (revision 16395) @@ -1,595 +1,600 @@ <?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!'); /** * Performs action on multilingual fields * */ class kMultiLanguageHelper extends kHelper { /** * Determines, that language info should be requeried * * @var bool * @access protected */ protected $initMade = false; /** * Maximal language id * * @var int */ protected $languageCount = 0; /** * Languages created in system * * @var Array */ protected $languagesIDs = Array (); /** * Structure of table, that is currently processed * * @var Array */ var $curStructure = Array(); /** * Field, to get structure information from * * @var string */ var $curSourceField = false; /** * Indexes used in table of 32 * * @var int */ var $curIndexCount = 0; /** * Fields from config, that are currently used * * @var Array */ var $curFields = Array(); public function resetState() { $this->initMade = false; } /** * Updates language count in system (always is divisible by 5) * */ protected function _queryLanguages() { if ( !$this->initMade ) { $this->languagesIDs = $this->getActualLanguages(); $this->languageCount = max(max($this->languagesIDs), 5); $this->initMade = true; } } /** * Returns language ids, that can be used * * @return Array */ protected function getActualLanguages() { $cache_key = 'actual_language_ids[%LangSerial%]'; $ret = $this->Application->getCache($cache_key); if ( $ret === false ) { $this->Conn->nextQueryCachable = true; $config = $this->Application->getUnitConfig('lang'); $sql = 'SELECT ' . $config->getIDField() . ' FROM ' . $config->getTableName(); $ret = $this->Conn->GetCol($sql); $this->Application->setCache($cache_key, $ret); } return $ret; } /** * Checks if language with specified id is created * * @param int $language_id * @return bool */ protected function LanguageFound($language_id) { return in_array($language_id, $this->languagesIDs) || $language_id <= 5; } /** * Returns list of processable languages * * @return Array */ public function getLanguages() { $cache_key = 'processable_language_ids[%LangSerial%]'; $ret = $this->Application->getCache($cache_key); if ( $ret === false ) { $ret = Array (); $this->_queryLanguages(); for ($language_id = 1; $language_id <= $this->languageCount; $language_id++) { if ( $this->LanguageFound($language_id) ) { $ret[] = $language_id; } } $this->Application->setCache($cache_key, $ret); } return $ret; } function scanTable($mask) { $i = 0; $fields_found = 0; $fields = array_keys($this->curStructure); foreach ($fields as $field_name) { if (preg_match($mask, $field_name)) { $fields_found++; } } return $fields_found; } function readTableStructure($table_name, $refresh = false) { // if ($refresh || !getArrayValue($structure_status, $prefix.'.'.$table_name)) { $this->curStructure = $this->Conn->Query('DESCRIBE '.$table_name, 'Field'); $this->curIndexCount = count($this->Conn->Query('SHOW INDEXES FROM '.$table_name)); // } } /** * Creates missing multilingual fields for all unit configs, registered in system * * @param bool $reread_configs * @return void * @access public */ public function massCreateFields($reread_configs = true) { if ( $reread_configs ) { $this->Application->UnitConfigReader->ReReadConfigs(); } foreach ($this->Application->UnitConfigReader->getPrefixes() as $prefix) { $this->createFields($prefix); } } /** * Creates missing multilanguage fields in table by specified prefix * * @param string $prefix * @param bool $refresh Forces config field structure to be re-read from database * @return void */ function createFields($prefix, $refresh = false) { if ( $refresh && preg_match('/(.*)-cdata$/', $prefix, $regs) ) { // call main item config to clone cdata table $this->Application->UnitConfigReader->loadConfig($regs[1]); $this->Application->UnitConfigReader->runAfterConfigRead($prefix); } $config = $this->Application->getUnitConfig($prefix); $table_name = $config->getTableName(); $this->curFields = $config->getFields(); if ( !($table_name && $this->curFields) || ($table_name && !$this->Conn->TableFound($table_name, kUtil::constOn('IS_INSTALL'))) ) { // invalid config found or prefix not found return ; } $this->_queryLanguages(); $sqls = Array (); $this->readTableStructure($table_name, $refresh); foreach ($this->curFields as $field_name => $field_options) { if ( getArrayValue($field_options, 'formatter') == 'kMultiLanguage' ) { if ( isset($field_options['master_field']) ) { unset($this->curFields[$field_name]); continue; } $this->setSourceField($field_name); if ( $this->languageCount > 0 ) { // `l77_Name` VARCHAR( 255 ) NULL DEFAULT '0'; $field_mask = Array (); $field_mask['name'] = 'l%s_' . $field_name; $field_mask['null'] = getArrayValue($field_options, 'not_null') ? 'NOT NULL' : 'NULL'; if ( $this->curSourceField ) { $default_value = $this->getFieldParam('Default') != 'NULL' ? $this->Conn->qstr($this->getFieldParam('Default')) : $this->getFieldParam('Default'); $field_mask['type'] = $this->getFieldParam('Type'); } else { $default_value = is_null($field_options['default']) ? 'NULL' : $this->Conn->qstr($field_options['default']); $field_mask['type'] = $field_options['db_type']; } $field_mask['default'] = ($field_mask['null'] == 'NOT NULL' && $default_value == 'NULL') ? '' : 'DEFAULT ' . $default_value; if ( strtoupper($field_mask['type']) == 'TEXT' ) { // text fields in mysql doesn't have default value $field_mask = $field_mask['name'] . ' ' . $field_mask['type'] . ' ' . $field_mask['null']; } else { $field_mask = $field_mask['name'] . ' ' . $field_mask['type'] . ' ' . $field_mask['null'] . ' ' . $field_mask['default']; } $alter_sqls = $this->generateAlterSQL($field_mask, 1, $this->languageCount); if ( $alter_sqls ) { $sqls[] = 'ALTER TABLE ' . $table_name . ' ' . $alter_sqls; } } } } foreach ($sqls as $sql_query) { $this->Conn->Query($sql_query); } } /** * Creates missing multilanguage fields in table by specified prefix * * @param string $prefix * @param int $src_language * @param int $dst_language * @return void * @access public */ public function copyMissingData($prefix, $src_language, $dst_language) { $config = $this->Application->getUnitConfig($prefix); $table_name = $config->getTableName(); $this->curFields = $config->getFields(); if ( !($table_name && $this->curFields) || ($table_name && !$this->Conn->TableFound($table_name, kUtil::constOn('IS_INSTALL'))) ) { // invalid config found or prefix not found return ; } foreach ($this->curFields as $field_name => $field_options) { $formatter = isset($field_options['formatter']) ? $field_options['formatter'] : ''; if ( ($formatter == 'kMultiLanguage') && !isset($field_options['master_field']) ) { $sql = 'UPDATE ' . $table_name . ' SET l' . $dst_language . '_' . $field_name . ' = l' . $src_language . '_' . $field_name . ' WHERE l' . $dst_language . '_' . $field_name . ' = "" OR l' . $dst_language . '_' . $field_name . ' IS NULL'; $this->Conn->Query($sql); } } } function deleteField($prefix, $custom_id) { $table_name = $this->Application->getUnitConfig($prefix)->getTableName(); $sql = 'DESCRIBE '.$table_name.' "l%_cust_'.$custom_id.'"'; $fields = $this->Conn->GetCol($sql); $sql = 'ALTER TABLE '.$table_name.' '; $sql_template = 'DROP COLUMN %s, '; foreach ($fields as $field_name) { $sql .= sprintf($sql_template, $field_name); } $this->Conn->Query( substr($sql, 0, -2) ); } /** * Returns parameter requested of current source field * * @param string $param_name * @return string */ function getFieldParam($param_name) { return $this->curStructure[$this->curSourceField][$param_name]; } /** * Detects field name to create other fields from * * @param string $field_name */ function setSourceField($field_name) { $ret = $this->scanTable('/^l[\d]+_'.preg_quote($field_name, '/').'$/'); if (!$ret) { // no multilingual fields at all (but we have such field without language prefix) $original_found = $this->scanTable('/^'.preg_quote($field_name, '$/').'/'); $this->curSourceField = $original_found ? $field_name : false; } else { $this->curSourceField = 'l1_'.$field_name; } } /** * Returns ALTER statement part for adding required fields to table * * @param string $field_mask sql mask for creating field with correct definition (type & size) * @param int $start_index add new fields starting from this index * @param int $create_count create this much new multilingual field translations * @return string */ function generateAlterSQL($field_mask, $start_index, $create_count) { static $single_lang = null; if (!isset($single_lang)) { // if single language mode, then create indexes only on primary columns $table_name = $this->Application->getUnitConfig('lang')->getTableName(); $sql = 'SELECT COUNT(*) FROM '.$table_name.' WHERE Enabled = 1'; // if language count = 0, then assume it's multi language mode $single_lang = $this->Conn->GetOne($sql) == 1; } $ret = ''; $ml_field = preg_replace('/l(.*?)_(.*?) (.*)/', '\\2', $field_mask); $i_count = $start_index + $create_count; while ($start_index < $i_count) { if (isset($this->curStructure['l'.$start_index.'_'.$ml_field]) || (!$this->LanguageFound($start_index)) ) { $start_index++; continue; } $prev_index = $start_index - 1; do { list($prev_field,$type) = explode(' ', sprintf($field_mask, $prev_index) ); } while ($prev_index > 0 && !$this->LanguageFound($prev_index--)); if (substr($prev_field, 0, 3) == 'l0_') { $prev_field = substr($prev_field, 3, strlen($prev_field)); if (!$this->curSourceField) { // get field name before this one $fields = array_keys($this->curFields); // $prev_field = key(end($this->curStructure)); $prev_field = $fields[array_search($prev_field, $fields) - 1]; if (getArrayValue($this->curFields[$prev_field], 'formatter') == 'kMultiLanguage') { $prev_field = 'l'.$this->languageCount.'_'.$prev_field; } } } $field_expression = sprintf($field_mask, $start_index); $ret .= 'ADD COLUMN '.$field_expression.' AFTER `'.$prev_field.'`, '; if ($this->curIndexCount < 32 && ($start_index == $this->Application->GetDefaultLanguageId() || !$single_lang)) { // create index for primary language column + for all others (if multiple languages installed) list($field_name, $field_params) = explode(' ', $field_expression, 2); - $index_type = isset($this->curFields[$ml_field]['index_type']) ? $this->curFields[$prev_field]['index_type'] : 'string'; + if ( isset($this->curFields[$ml_field]['index_type']) ) { + $index_type = $this->curFields[$ml_field]['index_type']; + } + else { + $index_type = 'string'; + } $ret .= $index_type == 'string' ? 'ADD INDEX (`'.$field_name.'` (5) ), ' : 'ADD INDEX (`'.$field_name.'`), '; $this->curIndexCount++; } $start_index++; } return preg_replace('/, $/', ';', $ret); } /** * Returns phrase based on given number * * @param int $number * @param Array $forms * @param bool $allow_editing * @param bool $use_admin * @return string * @access public */ public function getPluralPhrase($number, $forms, $allow_editing = true, $use_admin = false) { // normalize given forms if ( !array_key_exists('phrase3', $forms) ) { $forms['phrase3'] = $forms['phrase2']; } if ( !array_key_exists('phrase4', $forms) ) { $forms['phrase4'] = $forms['phrase2']; } if ( !array_key_exists('phrase5', $forms) ) { $forms['phrase5'] = $forms['phrase2']; } $phrase_type = $this->getPluralPhraseType($number); return $this->Application->Phrase($forms['phrase' . $phrase_type], $allow_editing, $use_admin); } /** * Returns phrase type based on given number * * @param int $number * @return int * @access protected */ protected function getPluralPhraseType($number) { $last_digit = substr($number, -1); $last_but_one_digit = strlen($number) > 1 ? substr($number, -2, 1) : false; $phrase_type = 5; if ( $last_but_one_digit != 1 ) { if ( $last_digit >= 1 && $last_digit <= 4 ) { $phrase_type = $last_digit; } } return (string)$phrase_type; } /** * Allows usage of * * @param kEvent $event * @return void * @access public */ public function replaceMLCalculatedFields(kEvent $event) { $config = $event->getUnitConfig(); $editing_language = $this->getEditingLanguage(); $calculated_fields = $config->getSetting('CalculatedFields', Array ()); /* @var $calculated_fields Array */ foreach ($calculated_fields as $special => $fields) { foreach ($fields as $field_name => $field_expression) { $calculated_fields[$special][$field_name] = str_replace('%5$s', $editing_language, $field_expression); } } $config->setSetting('CalculatedFields', $calculated_fields); } /** * Returns language, that is being edited or current language * * @return int * @access public */ public function getEditingLanguage() { $language_id = $this->Application->GetVar('lang_id'); if ( !$language_id ) { $language_id = $this->Application->GetVar('m_lang'); } return $language_id; } /** * Determines if we're editing phrase/e-mail event on it's source language * * @param int $source_language * @return bool * @access public */ public function editingInSourceLanguage($source_language) { return $this->getSourceLanguage($source_language) == $this->getEditingLanguage(); } /** * Replaces source language in given label translation * * @param kDBItem $object * @param string $label * @return string * @access public */ public function replaceSourceLanguage(kDBItem $object, $label) { $ret = $this->Application->Phrase($label); $options = $object->GetFieldOption('TranslateFromLanguage', 'options'); $source_language = $this->getSourceLanguage($object->GetDBField('TranslateFromLanguage')); return sprintf($ret, $options[$source_language]); } /** * Ensures, that primary language is used, when no translation is needed * * @param int $source_language * @return bool * @access public */ public function getSourceLanguage($source_language) { if ( !$source_language ) { $source_language = $this->Application->GetDefaultLanguageId(); } return $source_language; } /** * Translation synchronization state management * * @param kEvent $event * @return void * @access public * @throws InvalidArgumentException */ public function updateTranslationState(kEvent $event) { if ( $event->Name != 'OnBeforeCopyToLive' ) { throw new InvalidArgumentException('Unsupported "' . (string)$event . '" event'); } $object = $event->getObject(Array ('skip_autoload' => true)); /* @var $object kDBItem */ $object->SwitchToTemp(); $object->Load($event->getEventParam('id')); $save_mode = $this->Application->GetVar('translation_save_mode'); if ( $save_mode === false ) { return; } $editing_language = $this->getEditingLanguage(); if ( $save_mode == TranslationSaveMode::SYNC_WITH_PRIMARY ) { $object->SetDBField('l' . $editing_language . '_TranslateFrom', 0); } else { $languages = $this->getLanguages(); foreach ($languages as $language_id) { $object->SetDBField('l' . $language_id . '_TranslateFrom', $language_id == $editing_language ? 0 : $editing_language); } } if ( $object->GetChangedFields() ) { $object->Update(); } } } Index: branches/5.3.x/core/units/helpers/cron_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/cron_helper.php (revision 16394) +++ branches/5.3.x/core/units/helpers/cron_helper.php (revision 16395) Property changes on: branches/5.3.x/core/units/helpers/cron_helper.php ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +LF \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +Id \ No newline at end of property Index: branches/5.3.x/core/units/helpers/modules_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/modules_helper.php (revision 16394) +++ branches/5.3.x/core/units/helpers/modules_helper.php (revision 16395) @@ -1,504 +1,497 @@ <?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 kModulesHelper extends kHelper { /** * Identifies new module, that isn't installed yet */ const NOT_INSTALLED = 1; /** * Identifies installed module */ const INSTALLED = 2; /** * Identifies both installed & new modules */ const ANY = 3; function getWhereClause() { $where_clause = Array('Loaded = 1'); if (!$this->Application->isAdmin) { // no license checks on front-end return implode(' AND ', $where_clause); } $modules = $this->_GetModules(); if ($modules) { foreach ($modules as $module_index => $module) { $modules[$module_index] = $this->Conn->qstr($module); } $where_clause[] = 'Name IN ('.implode(',', $modules).')'; } return implode(' AND ', $where_clause); } function _EnableCookieSID() { $session = $this->Application->recallObject('Session'); /* @var $session Session */ return $session->CookiesEnabled; } function _IsSpider($UserAgent) { global $robots; $lines = file(FULL_PATH.'/robots_list.txt'); if (!is_array($robots)) { $robots = Array(); for($i = 0; $i < count($lines); $i++) { $l = $lines[$i]; $p = explode("\t", $l, 3); $robots[] = $p[2]; } } return in_array($UserAgent, $robots); } function _MatchIp($ip1, $ip2) { $matched = TRUE; $ip = explode('.', $ip1); $MatchIp = explode('.', $ip2); for ($i = 0; $i < count($ip); $i++) { if($i == count($MatchIp)) break; if (trim($ip[$i]) != trim($MatchIp[$i]) || trim($ip[$i]) == '*') { $matched = FALSE; break; } } return $matched; } function _IpAccess($IpAddress, $AllowList, $DenyList) { $allowed = explode(',', $AllowList); $denied = explode(',', $DenyList); $MatchAllowed = FALSE; for ($x = 0; $x < count($allowed); $x++) { $ip = explode('.', $allowed[$x]); $MatchAllowed = $this->_MatchIp($IpAddress, $allowed[$x]); if ($MatchAllowed) break; } $MatchDenied = FALSE; for ($x = 0; $x < count($denied); $x++) { $ip = explode('.', $denied[$x]); $MatchDenied = $this->_MatchIp($IpAddress, $denied[$x]); if ($MatchDenied) break; } $Result = (($MatchAllowed && !$MatchDenied) || (!$MatchAllowed && !$MatchDenied) || ($MatchAllowed && $MatchDenied)); return $Result; } /** * Leaves only domain part from hostname (e.g. extract "intechnic.lv" from "test.intechnic.lv") * Used for admin login license check * * @param string $d * @return string */ function _StripDomainHost($d) { $IsIp = false; $dotcount = substr_count($d, '.'); if ($dotcount == 3) { $IsIp = true; for ($x = 0; $x < strlen($d); $x++) { if (!is_numeric(substr($d, $x, 1)) && substr($d, $x, 1) != '.') { $IsIp = false; break; } } } if ($dotcount > 1 && !$IsIp) { $p = explode('.', $d); $ret = $p[count($p) - 2].'.'.$p[count($p) - 1]; } else { $ret = $d; } return $ret; } /** * When logging into admin then check only last 2 parts of host name VS domain in license * * @param string $user_domain * @param string $license_domain * @return int */ function _CheckDomain($user_domain, $license_domain) { if ($this->Application->isAdmin) { $user_domain = $this->_StripDomainHost($user_domain); return preg_match('/(.*)'.preg_quote($user_domain, '/').'$/', $license_domain); } else { return preg_match('/(.*)'.preg_quote($license_domain, '/').'$/', $user_domain); } } /** * Returns modules list, that are in license * * @return Array */ function _GetModules() { static $modules = null; if (isset($modules)) { return $modules; } $modules = Array(); $system_config = kUtil::getSystemConfig(); $license = $system_config->get('License') ? base64_decode($system_config->get('License')) : false; if ($license) { list ( , , $i_Keys) = $this->_ParseLicense($license); $domain = $this->_GetDomain($system_config); if (!$this->_IsLocalSite($domain)) { for ($x = 0; $x < count($i_Keys); $x++) { $key = $i_Keys[$x]; if ($this->_CheckDomain($domain, $key['domain'])) { // used hostname is subdomain or matches domain from license $modules = explode(',', $key['mod']); } } } else { // all already installed modules are licensed for localhost $modules = array_keys($this->Application->ModuleInfo); } } // all modules starting from "in-" doesn't require license $base_modules = Array ('Core', 'In-Portal', 'Custom'); $modules = array_merge($modules, $base_modules, $this->_getFreeModules($system_config)); $modules = array_unique( array_map('strtolower', $modules) ); return $modules; } /** * Get all modules, that don't require licensing * * @param kSystemConfig $system_config * @return Array * @access protected */ protected function _getFreeModules(kSystemConfig $system_config) { - $domain = $this->_GetDomain($system_config); - $modules = array_map('strtolower', $this->getModules()); - - if ( !$this->_IsLocalSite($domain) ) { - return array_diff($modules, Array ('in-commerce', 'in-auction')); - } - - return $modules; + return array_map('strtolower', $this->getModules()); } /** * Allows to determine if module is licensed * * @param string $name * @return bool */ function _ModuleLicensed($name) { $modules = $this->_GetModules(); return in_array($name, $modules); } /** * Returns domain from licences (and direct in case of install script) * * @param kSystemConfig $system_config * @return string */ function _GetDomain(kSystemConfig $system_config) { return $system_config->get('Domain', SERVER_NAME); } function _keyED($txt, $encrypt_key) { $encrypt_key = md5($encrypt_key); $ctr = 0; $tmp = ''; for ($i = 0; $i < strlen($txt); $i++) { if ($ctr == strlen($encrypt_key)) $ctr = 0; $tmp .= substr($txt, $i, 1) ^ substr($encrypt_key, $ctr, 1); $ctr++; } return $tmp; } function _decrypt($txt, $key) { $txt = $this->_keyED($txt,$key); $tmp = ''; for ($i = 0; $i < strlen($txt); $i++) { $md5 = substr($txt, $i, 1); $i++; $tmp .= (substr($txt, $i, 1) ^ $md5); } return $tmp; } function LoadFromRemote() { return ''; } function DLid() { die($GLOBALS['lid']."\n"); } function _LoadLicense($LoadRemote = false) { $f = FULL_PATH.'/intechnic.php'; if ($this->_falseIsLocalSite($f)) $ret = true; if (file_exists($f)) { $contents = file($f); $data = base64_decode($contents[1]); } else { if ($LoadRemote) return $LoadFromRemote; } return $data; } function _VerifyKey($domain, $k) { $key = md5($domain); $lkey = substr($key, 0, strlen($key) / 2); $rkey = substr($key, strlen($key) / 2); $r = $rkey.$lkey; if ($k == $r) return true; return false; } function _ParseLicense($txt) { // global $i_User, $i_Pswd, $i_Keys; if (!$this->_falseIsLocalSite($txt)) { $nah = false; } $data = $this->_decrypt($txt, 'beagle'); $i_User = $i_Pswd = ''; $i_Keys = Array(); $lines = explode("\n", $data); for ($x = 0; $x < count($lines); $x++) { $l = $lines[$x]; $p = explode('=', $l, 2); switch($p[0]) { case 'Username': $i_User = $p[1]; break; case 'UserPass': $i_Pswd = $p[1]; break; default: if (substr($p[0], 0, 3) == 'key') { $parts = explode('|', $p[1]); if ($this->_VerifyKey($parts[0], $parts[1])) { unset($K); $k['domain'] = $parts[0]; $k['key'] = $parts[1]; $k['desc'] = $parts[2]; $k['mod'] = $parts[3]; $i_Keys[] = $k; } } break; } } return Array ($i_User, $i_Pswd, $i_Keys); } function _GetObscureValue($i) { if ($i == 'x') return 0254; $z = ''; if ($i == 'z') return 0x7F.'.'; if ($i == 'c') return '--code--'; if ($i >= 5 && $i < 7) return $this->_GetObscureValue($z)*$this->_GetObscureValue('e'); if ($i > 30) return Array(0x6c,0x6f,0x63,0x61,0x6c,0x68,0x6f,0x73,0x74); if ($i > 20) return 99; if ($i > 10) return '.'.($this->_GetObscureValue(6.5)+1); if ($i == 'a') return 0xa; return 0; } function _Chr($val) { $x = $this->_GetObscureValue(25); $f = chr($x).chr($x+5).chr($x+15); return $f($val); } function _IsLocalSite($domain) { $ee = $this->_GetObscureValue(35); $yy = ''; foreach ($ee as $e) $yy .= $this->_Chr($e); $localb = FALSE; if(substr($domain,0,3)==$this->_GetObscureValue('x')) { $b = substr($domain,0,6); $p = explode(".",$domain); $subnet = $p[1]; if($p[1]>15 && $p[1]<32) $localb=TRUE; } $zz = $this->_GetObscureValue('z').$this->_GetObscureValue(5).'.'.(int)$this->_GetObscureValue(7).$this->_GetObscureValue(12); $ff = $this->_GetObscureValue('z')+65; $hh = $ff-0x18; if($domain==$yy || $domain==$zz || substr($domain,0,7)==$ff.$this->_Chr(46).$hh || substr($domain,0,3)==$this->_GetObscureValue('a').$this->_Chr(46) || $localb || strpos($domain,".")==0) { return TRUE; } return FALSE; } function _falseIsLocalSite($domain) { $localb = FALSE; if(substr($domain,0,3)=="172") { $b = substr($domain,0,6); $p = explode(".",$domain); $subnet = $p[1]; if($p[1]>15 && $p[1]<32) $localb=TRUE; } if($domain=="localhost" || $domain=="127.0.0.1" || substr($domain,0,7)=="192.168" || substr($domain,0,3)=="10." || $localb || strpos($domain,".")==0) { return TRUE; } return FALSE; } function verifyLicense($license_hash) { $license_hash = base64_decode($license_hash); list ($license_user, $license_password, ) = $this->_ParseLicense($license_hash); return strlen($license_user) && strlen($license_password); } function moduleInstalled($module_name) { static $modules = null; if ( is_null($modules) ) { $sql = 'SELECT LOWER(Name) FROM ' . $this->Application->getUnitConfig('mod')->getTableName(); $modules = $this->Conn->GetCol($sql); } if ( $module_name == 'kernel' ) { $module_name = 'in-portal'; } return in_array(strtolower($module_name), $modules); } /** * Returns list of matching modules * * @param int $module_type * @return Array * @access public */ public function getModules($module_type = self::ANY) { $modules = Array (); try { $iterator = new DirectoryIterator(MODULES_PATH); /* @var $file_info DirectoryIterator */ } catch (UnexpectedValueException $e) { return $modules; } foreach ($iterator as $file_info) { $file_path = $file_info->getPathname(); if ( $file_info->isDir() && !$file_info->isDot() && $this->isInPortalModule($file_path) ) { $install_order = trim( file_get_contents($file_path . '/install/install_order.txt') ); $modules[$install_order] = $file_info->getFilename(); } } // allows to control module install order ksort($modules, SORT_NUMERIC); if ( $module_type == self::ANY ) { return $modules; } foreach ($modules as $install_order => $module_name) { $installed = $this->moduleInstalled($module_name); if ( ($module_type == self::INSTALLED && !$installed) || ($module_type == self::NOT_INSTALLED && $installed) ) { unset($modules[$install_order]); } } return $modules; } /** * Checks, that given folder is In-Portal module's root folder * * @param string $folder_path * @return bool * @access public */ public static function isInPortalModule($folder_path) { return file_exists($folder_path . '/install.php') && file_exists($folder_path . '/install/install_schema.sql'); } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/helpers/helpers_config.php =================================================================== --- branches/5.3.x/core/units/helpers/helpers_config.php (revision 16394) +++ branches/5.3.x/core/units/helpers/helpers_config.php (revision 16395) @@ -1,77 +1,80 @@ <?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' => 'helpers', 'EventHandlerClass' => Array ('class' => 'kEventHandler', 'file' => '', 'build_event' => 'OnBuild'), 'RegisterClasses' => Array ( Array ('pseudo' => 'kMultiLanguageHelper', 'class' => 'kMultiLanguageHelper', 'file' => 'multilanguage_helper.php', 'build_event' => ''), Array ('pseudo' => 'SearchHelper', 'class' => 'kSearchHelper', 'file' => 'search_helper.php', 'build_event' => ''), Array ('pseudo' => 'SectionsHelper', 'class' => 'kSectionsHelper', 'file' => 'sections_helper.php', 'build_event' => ''), Array ('pseudo' => 'PermissionsHelper', 'class' => 'kPermissionsHelper', 'file' => 'permissions_helper.php', 'build_event' => ''), Array ('pseudo' => 'kModulesHelper', 'class' => 'kModulesHelper', 'file' => 'modules_helper.php', 'build_event' => ''), Array ('pseudo' => 'RecursiveHelper', 'class' => 'kRecursiveHelper', 'file' => 'recursive_helper.php', 'build_event' => ''), Array ('pseudo' => 'FilenamesHelper', 'class' => 'kFilenamesHelper', 'file' => 'filenames_helper.php', 'build_event' => ''), Array ('pseudo' => 'ClipboardHelper', 'class' => 'kClipboardHelper', 'file' => 'clipboard_helper.php', 'build_event' => ''), Array ('pseudo' => 'ColumnPickerHelper', 'class' => 'kColumnPickerHelper', 'file' => 'col_picker_helper.php', 'build_event' => ''), Array ('pseudo' => 'ThemesHelper', 'class' => 'kThemesHelper', 'file' => 'themes_helper.php', 'build_event' => ''), Array ('pseudo' => 'CaptchaHelper', 'class' => 'kCaptchaHelper', 'file' => 'captcha_helper.php', 'build_event' => ''), Array ('pseudo' => 'PriorityHelper', 'class' => 'kPriorityHelper', 'file' => 'priority_helper.php', 'build_event' => ''), Array ('pseudo' => 'CurlHelper', 'class' => 'kCurlHelper', 'file' => 'curl_helper.php', 'build_event' => ''), Array ('pseudo' => 'CountHelper', 'class' => 'kCountHelper', 'file' => 'count_helper.php', 'build_event' => ''), Array ('pseudo' => 'ImageHelper', 'class' => 'ImageHelper', 'file' => 'image_helper.php', 'build_event' => ''), Array ('pseudo' => 'FileHelper', 'class' => 'FileHelper', 'file' => 'file_helper.php', 'build_event' => ''), Array ('pseudo' => 'CategoryHelper', 'class' => 'CategoryHelper', 'file' => 'category_helper.php', 'build_event' => ''), Array ('pseudo' => 'kNavigationBar', 'class' => 'kNavigationBar', 'file' => 'navigation_bar.php', 'build_event' => ''), Array ('pseudo' => 'CSVHelper', 'class' => 'kCSVHelper', 'file' => 'csv_helper.php', 'build_event' => ''), Array ('pseudo' => 'ChartHelper', 'class' => 'kChartHelper', 'file' => 'chart_helper.php', 'build_event' => ''), Array ('pseudo' => 'RatingHelper', 'class' => 'RatingHelper', 'file' => 'rating_helper.php', 'build_event' => ''), Array ('pseudo' => 'FCKHelper', 'class' => 'fckFCKHelper', 'file' => 'fck_helper.php', 'build_event' => ''), Array ('pseudo' => 'SpamHelper', 'class' => 'SpamHelper', 'file' => 'spam_helper.php', 'build_event' => ''), Array ('pseudo' => 'TemplateHelper', 'class' => 'TemplateHelper', 'file' => 'template_helper.php', 'build_event' => ''), Array ('pseudo' => 'MailingListHelper', 'class' => 'MailingListHelper', 'file' => 'mailing_list_helper.php', 'build_event' => ''), Array ('pseudo' => 'JSONHelper', 'class' => 'JSONHelper', 'file' => 'json_helper.php', 'build_event' => ''), Array ('pseudo' => 'LanguageImportHelper', 'class' => 'LanguageImportHelper', 'file' => 'language_import_helper.php', 'build_event' => ''), Array ('pseudo' => 'SkinHelper', 'class' => 'SkinHelper', 'file' => 'skin_helper.php', 'build_event' => ''), Array ('pseudo' => 'SiteConfigHelper', 'class' => 'SiteConfigHelper', 'file' => 'site_config_helper.php', 'build_event' => ''), Array ('pseudo' => 'MenuHelper', 'class' => 'MenuHelper', 'file' => 'menu_helper.php', 'build_event' => ''), Array ('pseudo' => 'InpCustomFieldsHelper', 'class' => 'InpCustomFieldsHelper', 'file' => 'custom_fields_helper.php', 'build_event' => ''), Array ('pseudo' => 'CountryStatesHelper', 'class' => 'kCountryStatesHelper', 'file' => 'country_states_helper.php', 'build_event' => ''), Array ('pseudo' => 'BracketsHelper', 'class' => 'kBracketsHelper', 'file' => 'brackets_helper.php', 'build_event' => ''), Array ('pseudo' => 'kXMLHelper', 'class' => 'kXMLHelper', 'file' => 'xml_helper.php', 'build_event' => ''), + Array ('pseudo' => 'kXMLNode', 'class' => 'kXMLNode', 'file' => 'xml_helper.php', 'build_event' => ''), + Array ('pseudo' => 'XMLIterator', 'class' => 'XMLIterator', 'file' => 'xml_helper5.php', 'build_event' => ''), + Array ('pseudo' => 'kXMLNode5', 'class' => 'kXMLNode5', 'file' => 'xml_helper5.php', 'build_event' => '', 'require_classes' => 'kXMLNode'), Array ('pseudo' => 'CatItemExportHelper', 'class' => 'kCatDBItemExportHelper', 'file' => 'cat_dbitem_export_helper.php', 'build_event' => ''), Array ('pseudo' => 'kEmailTemplateHelper', 'class' => 'kEmailTemplateHelper', 'file' => 'email_template_helper.php', 'build_event' => ''), Array ('pseudo' => 'ListHelper', 'class' => 'ListHelper', 'file' => 'list_helper.php', 'build_event' => ''), Array ('pseudo' => 'FormSubmissionHelper', 'class' => 'FormSubmissionHelper', 'file' => 'form_submission_helper.php', 'build_event' => ''), Array ('pseudo' => 'MailboxHelper', 'class' => 'MailboxHelper', 'file' => 'mailbox_helper.php', 'build_event' => ''), Array ('pseudo' => 'POP3Helper', 'class' => 'POP3Helper', 'file' => 'pop3_helper.php', 'build_event' => ''), Array ('pseudo' => 'MimeDecodeHelper', 'class' => 'MimeDecodeHelper', 'file' => 'mime_decode_helper.php', 'build_event' => ''), Array ('pseudo' => 'UserHelper', 'class' => 'UserHelper', 'file' => 'user_helper.php', 'build_event' => ''), Array ('pseudo' => 'SiteHelper', 'class' => 'SiteHelper', 'file' => 'site_helper.php', 'build_event' => ''), Array ('pseudo' => 'DeploymentHelper', 'class' => 'DeploymentHelper', 'file' => 'deployment_helper.php', 'build_event' => ''), Array ('pseudo' => 'PageHelper', 'class' => 'PageHelper', 'file' => 'page_helper.php', 'build_event' => ''), Array ('pseudo' => 'BackupHelper', 'class' => 'BackupHelper', 'file' => 'backup_helper.php', 'build_event' => ''), Array ('pseudo' => 'AjaxFormHelper', 'class' => 'AjaxFormHelper', 'file' => 'ajax_form_helper.php', 'build_event' => ''), Array ('pseudo' => 'kCronHelper', 'class' => 'kCronHelper', 'file' => 'cron_helper.php', 'build_event' => ''), Array ('pseudo' => 'kUploadHelper', 'class' => 'kUploadHelper', 'file' => 'upload_helper.php', 'build_event' => ''), ), ); Index: branches/5.3.x/core/units/helpers/site_config_helper.php =================================================================== --- branches/5.3.x/core/units/helpers/site_config_helper.php (revision 16394) +++ branches/5.3.x/core/units/helpers/site_config_helper.php (revision 16395) @@ -1,215 +1,215 @@ <?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 SiteConfigHelper extends kHelper { public function __construct() { parent::__construct(); $preset_name = $this->Application->ConfigValue('AdminConsoleInterface'); define('SYSTEM_PRESET_PATH', FULL_PATH . ADMIN_PRESETS_DIRECTORY . DIRECTORY_SEPARATOR . 'system_presets' . DIRECTORY_SEPARATOR . $preset_name); } /** * Returns settings for current admin console preset * * @return Array */ function getSettings() { static $settings = null; if (isset($settings)) { return $settings; } $default_settings = $this->_getDefaultSettings(); $preset_name = $this->Application->ConfigValue('AdminConsoleInterface'); $base_path = FULL_PATH . ADMIN_DIRECTORY . '/system_presets/' . $preset_name; if (file_exists($base_path . DIRECTORY_SEPARATOR . 'settings.php')) { include_once $base_path . DIRECTORY_SEPARATOR . 'settings.php'; // will get $settings array foreach ($default_settings as $setting_name => $setting_value) { if (!array_key_exists($setting_name, $settings)) { $settings[$setting_name] = $setting_value; } } return $settings; } else { $settings = $default_settings; } return $settings; } /** * Returns default settings for admin console presets * * @return Array */ function _getDefaultSettings() { $settings = Array ( 'default_editing_mode' => EDITING_MODE_BROWSE, 'visible_editing_modes' => Array ( EDITING_MODE_BROWSE, EDITING_MODE_CONTENT, EDITING_MODE_DESIGN, ), ); return $settings; } /** * Applies given changes to given prefix unit config * * @param string $prefix * @param Array $changes */ function processConfigChanges($prefix, $changes) { - extract($changes); + extract($changes, EXTR_SKIP); $config = $this->Application->getUnitConfig($prefix); $core_config = $this->Application->getUnitConfig('core-sections'); $section_adjustments = $core_config->getSectionAdjustments(Array ()); $title_presets = $config->getTitlePresets(Array ()); $fields = $config->getFields(Array ()); $virtual_fields = $config->getVirtualFields(Array ()); $edit_tab_presets = $config->getEditTabPresets(Array ()); $grids = $config->getGrids(Array ()); $field_names = array_keys($fields); $virtual_field_names = array_keys($virtual_fields); if (isset($remove_sections)) { // process sections foreach ($remove_sections as $remove_section) { $section_adjustments[$remove_section]['show_mode'] = smHIDE; } } if (isset($debug_only_sections)) { // process sections foreach ($debug_only_sections as $debug_only_section) { $section_adjustments[$debug_only_section]['show_mode'] = smDEBUG; } } if (isset($remove_buttons)) { // process toolbar buttons foreach ($remove_buttons as $title_preset => $toolbar_buttons) { $title_presets[$title_preset]['toolbar_buttons'] = array_diff($title_presets[$title_preset]['toolbar_buttons'], $toolbar_buttons); } } $reset_fields = true; $reset_virtual_fields = true; // process hidden fields if (isset($hidden_fields)) { $fields = $this->_setFieldOption($fields, $hidden_fields, 'show_mode', false, $reset_fields); $reset_fields = false; } if (isset($virtual_hidden_fields)) { $virtual_fields = $this->_setFieldOption($virtual_fields, $virtual_hidden_fields, 'show_mode', false, $reset_virtual_fields); $reset_virtual_fields = false; } // process debug only fields if (isset($debug_only_fields)) { $fields = $this->_setFieldOption($fields, $debug_only_fields, 'show_mode', smDEBUG, $reset_fields); $reset_fields = false; } if (isset($debug_only_virtual_fields)) { $virtual_fields = $this->_setFieldOption($virtual_fields, $debug_only_virtual_fields, 'show_mode', smDEBUG, $reset_virtual_fields); $reset_virtual_fields = false; } // process required fields if (isset($required_fields)) { $fields = $this->_setFieldOption($fields, $required_fields, 'required', 1); } if (isset($virtual_required_fields)) { $virtual_fields = $this->_setFieldOption($virtual_fields, $virtual_required_fields, 'required', 1); } if (isset($hide_edit_tabs)) { // hide edit tabs foreach ($hide_edit_tabs as $preset_name => $edit_tabs) { foreach ($edit_tabs as $edit_tab) { unset($edit_tab_presets[$preset_name][$edit_tab]); } } } if (isset($hide_columns)) { // hide columns in grids foreach ($hide_columns as $grid_name => $columns) { foreach ($columns as $column) { unset($grids[$grid_name]['Fields'][$column]); } } } // save changes $core_config->setSectionAdjustments($section_adjustments); $config->setTitlePresets($title_presets); $config->setFields($fields); $config->setVirtualFields($virtual_fields); $config->setEditTabPresets($edit_tab_presets); $config->setGrids($grids); } /** * Sets given option for given fields and unsets it for all other fields * * @param Array $fields * @param Array $set_fields * @param string $option_name * @param mixed $option_value * @param bool $reset */ function _setFieldOption($fields, $set_fields, $option_name, $option_value, $reset = true) { if ($reset) { // unset given option for rest of fields (all except $set_fields) $unset_fields = array_diff(array_keys($fields), $set_fields); foreach ($unset_fields as $unset_field) { if (array_key_exists($option_name, $fields[$unset_field])) { unset($fields[$unset_field][$option_name]); } } } // set given option to given fields foreach ($set_fields as $set_field) { $fields[$set_field][$option_name] = $option_value; } return $fields; } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/logs/system_logs/system_logs_config.php =================================================================== --- branches/5.3.x/core/units/logs/system_logs/system_logs_config.php (revision 16394) +++ branches/5.3.x/core/units/logs/system_logs/system_logs_config.php (revision 16395) @@ -1,205 +1,205 @@ <?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!'); $config = Array ( 'Prefix' => 'system-log', 'ItemClass' => Array ('class' => 'kDBItem', 'file' => '', 'build_event' => 'OnItemBuild'), 'ListClass' => Array ('class' => 'kDBList', 'file' => '', 'build_event' => 'OnListBuild'), 'EventHandlerClass' => Array ('class' => 'SystemLogEventHandler', 'file' => 'system_log_eh.php', 'build_event' => 'OnBuild'), 'TagProcessorClass' => Array ('class' => 'SystemLogTagProcessor', 'file' => 'system_log_tp.php', 'build_event' => 'OnBuild'), 'AutoLoad' => true, 'QueryString' => Array ( 1 => 'id', 2 => 'Page', 3 => 'PerPage', 4 => 'event', 5 => 'mode', ), 'ScheduledTasks' => Array ( 'system_log_notifications' => Array ('EventName' => 'OnSendNotifications', 'RunSchedule' => '0 * * * *'), 'rotate_system_logs' => Array ('EventName' => 'OnRotate', 'RunSchedule' => '0 0 * * *'), ), 'IDField' => 'LogId', // 'StatusField' => Array ('Status'), 'TableName' => TABLE_PREFIX . 'SystemLog', 'TitlePresets' => Array ( 'default' => Array ( 'edit_status_labels' => Array ('system-log' => '!la_title_ViewingSystemLog!'), ), 'system_log_list' => Array ( 'prefixes' => Array ('system-log_List'), 'format' => '!la_tab_SystemLog!', 'toolbar_buttons' => Array ('edit', 'delete', 'reset', 'view', 'dbl-click'), ), 'system_log_edit' => Array ( 'prefixes' => Array ('system-log'), 'format' => '#system-log_status# "#system-log_titlefield#"', 'toolbar_buttons' => Array ('select', 'cancel', 'reset_edit', 'prev', 'next'), ), ), 'PermSection' => Array ('main' => 'in-portal:system_logs'), 'Sections' => Array ( 'in-portal:system_logs' => Array ( 'parent' => 'in-portal:reports', 'icon' => 'system_log', 'label' => 'la_tab_SystemLog', 'url' => Array ('t' => 'logs/system_logs/system_log_list', 'pass' => 'm'), 'permissions' => Array ('view', 'edit', 'delete'), 'priority' => 6.1, 'type' => stTREE, ), ), 'TitleField' => 'LogMessage', 'ListSQLs' => Array ( '' => ' SELECT %1$s.* %2$s FROM %1$s LEFT JOIN '.TABLE_PREFIX.'Users AS u ON u.PortalUserId = %1$s.LogUserId', ), 'ListSortings' => Array ( '' => Array ( 'Sorting' => Array ('LogId' => 'desc'), ) ), 'CalculatedFields' => Array ( '' => Array ( 'Username' => 'CASE %1$s.LogUserId WHEN ' . USER_ROOT . ' THEN "root" WHEN ' . USER_GUEST . ' THEN "Guest" ELSE IF(CONCAT(u.FirstName, u.LastName) <> "", CONCAT(u.FirstName, " ", u.LastName), IF(u.Username = "", u.Email, u.Username)) END', ), ), 'ForceDontLogChanges' => true, 'Fields' => Array ( 'LogId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'LogUniqueId' => Array ('type' => 'int', 'default' => NULL), 'LogLevel' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array ( kLogger::LL_EMERGENCY => 'emergency', kLogger::LL_ALERT => 'alert', kLogger::LL_CRITICAL => 'critical', kLogger::LL_ERROR => 'error', kLogger::LL_WARNING => 'warning', kLogger::LL_NOTICE => 'notice', kLogger::LL_INFO => 'info', kLogger::LL_DEBUG => 'debug' ), 'not_null' => 1, 'default' => kLogger::LL_INFO ), 'LogType' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array ( kLogger::LT_PHP => 'la_opt_LogTypePhp', kLogger::LT_DATABASE => 'la_opt_LogTypeDatabase', kLogger::LT_OTHER => 'la_opt_LogTypeOther', ), 'use_phrases' => 1, 'not_null', 'default' => kLogger::LT_OTHER ), 'LogCode' => Array ('type' => 'int', 'default' => NULL), 'LogMessage' => Array ('type' => 'string', 'default' => NULL), 'LogTimestamp' => Array ( 'type' => 'int', 'formatter' => 'kDateFormatter', 'default' => NULL ), 'LogDate' => Array ('type' => 'string', 'default' => NULL), 'LogEventName' => Array ('type' => 'string', 'max_len' => 100, 'not_null' => 1, 'default' => ''), 'LogHostname' => Array ('type' => 'string', 'max_len' => 255, 'not_null' => 1, 'default' => ''), 'LogRequestSource' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'Web', 2 => 'CLI'), 'default' => NULL ), 'LogRequestURI' => Array ('type' => 'string', 'max_len' => 255, 'not_null' => 1, 'default' => ''), 'LogRequestData' => Array ('type' => 'string', 'default' => NULL), 'LogUserId' => Array ( 'type' => 'int', 'default' => NULL ), 'LogInterface' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array ( kLogger::LI_FRONT => 'Front', kLogger::LI_ADMIN => 'Admin', kLogger::LI_CRON_FRONT => 'Cron (Front)', kLogger::LI_CRON_ADMIN => 'Cron (Admin)', kLogger::LI_API => 'API', ), 'not_null' => 1, 'default' => 0 ), 'IpAddress' => Array ('type' => 'string', 'max_len' => 15, 'not_null' => 1, 'default' => ''), 'LogSessionKey' => Array ('type' => 'int', 'default' => NULL), 'LogSessionData' => Array ('type' => 'string', 'default' => NULL), 'LogBacktrace' => Array ('type' => 'string', 'default' => NULL), 'LogSourceFilename' => Array ('type' => 'string', 'max_len' => 255, 'not_null' => 1, 'default' => ''), 'LogSourceFileLine' => Array ('type' => 'int', 'default' => NULL), 'LogProcessId' => Array ('type' => 'int', 'default' => NULL), 'LogMemoryUsed' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'LogUserData' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''), 'LogNotificationStatus' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array ( kLogger::LNS_DISABLED => 'la_opt_LogNotificationDisabled', kLogger::LNS_PENDING => 'la_opt_LogNotificationPending', kLogger::LNS_SENT => 'la_opt_LogNotificationSent' ), 'use_phrases' => 1, 'not_null' => 1, 'default' => kLogger::LNS_DISABLED ) ), 'VirtualFields' => Array ( 'Username' => Array ('type' => 'string', 'default' => ''), ), 'Grids' => Array ( 'Default' => Array ( // 'Icons' => Array ('default' => 'icon16_item.png'), 'Fields' => Array ( 'LogId' => Array ('title' => 'column:la_fld_Id', 'filter_block' => 'grid_range_filter', 'width' => 80), 'LogLevel' => Array ('filter_block' => 'grid_options_filter', 'width' => 100), 'LogType' => Array ('filter_block' => 'grid_options_filter', 'width' => 80), 'LogCode' => Array ('filter_block' => 'grid_range_filter', 'width' => 80), 'LogMessage' => Array ('filter_block' => 'grid_like_filter', 'width' => 250), 'LogTimestamp' => Array ('filter_block' => 'grid_date_range_filter', 'width' => 170), 'LogEventName' => Array ('filter_block' => 'grid_like_filter'), 'LogHostname' => Array ('filter_block' => 'grid_like_filter'), 'LogRequestSource' => Array ('filter_block' => 'grid_options_filter'), 'LogRequestURI' => Array ('data_block' => 'grid_uri_td', 'filter_block' => 'grid_like_filter'), 'Username' => Array ('filter_block' => 'grid_range_filter', 'width' => 80), 'LogInterface' => Array ('filter_block' => 'grid_options_filter'), 'IpAddress' => Array ('filter_block' => 'grid_like_filter'), 'LogSessionKey' => Array ('filter_block' => 'grid_range_filter', 'width' => 80), 'LogSourceFilename' => Array ('data_block' => 'grid_filename_td', 'filter_block' => 'grid_like_filter', 'width' => 200), 'LogSourceFileLine' => Array ('filter_block' => 'grid_range_filter', 'width' => 80), 'LogProcessId' => Array ('filter_block' => 'grid_range_filter', 'width' => 80), 'LogMemoryUsed' => Array ('data_block' => 'grid_memory_td', 'filter_block' => 'grid_range_filter'), 'LogUserData' => Array ('filter_block' => 'grid_like_filter', 'nl2br' => 1), 'LogNotificationStatus' => Array ('filter_block' => 'grid_options_filter'), 'LogBacktrace' => Array ('data_block' => 'grid_backtrace_td', 'filter_block' => 'grid_empty_filter', 'width' => 500, 'hidden' => 1), ), ), ), -); +); \ No newline at end of file Index: branches/5.3.x/core/units/scheduled_tasks/scheduled_task_eh.php =================================================================== --- branches/5.3.x/core/units/scheduled_tasks/scheduled_task_eh.php (revision 16394) +++ branches/5.3.x/core/units/scheduled_tasks/scheduled_task_eh.php (revision 16395) @@ -1,274 +1,279 @@ <?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 ScheduledTaskEventHandler extends kDBEventHandler { /** * Allows to override standard permission mapping * * @return void * @access protected * @see kEventHandler::$permMapping */ protected function mapPermissions() { parent::mapPermissions(); $permissions = Array ( 'OnMassCancel' => Array ('self' => 'add|edit'), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Does custom validation * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemValidate(kEvent $event) { parent::OnBeforeItemValidate($event); $object = $event->getObject(); /* @var $object kDBItem */ $event_string = $object->GetDBField('Event'); if ( !$event_string ) { return; } try { $this->Application->eventImplemented(new kEvent($event_string)); } catch (Exception $e) { $object->SetError('Event', 'invalid_event', '+' . $e->getMessage()); } } /** * [HOOK] Refreshes scheduled task list in database based on cached data from unit configs * * @param kEvent $event */ function OnRefresh($event) { $scheduled_tasks_from_cache = $this->Application->EventManager->getScheduledTasks(true); $object = $event->getObject( Array ('skip_autoload' => true) ); /* @var $object kDBItem */ $processed_ids = Array (); $scheduled_tasks_from_db = $this->Conn->Query($object->GetSelectSQL(), 'Name'); $cron_helper = $this->Application->recallObject('kCronHelper'); /* @var $cron_helper kCronHelper */ foreach ($scheduled_tasks_from_cache as $scheduled_task_name => $scheduled_task_params) { if ( !isset($scheduled_tasks_from_db[$scheduled_task_name]) ) { $fields_hash = Array ( 'Event' => $scheduled_task_params['Event'], 'Name' => $scheduled_task_name, 'Type' => ScheduledTask::TYPE_SYSTEM, 'Module' => $scheduled_task_params['Module'], 'Status' => isset($scheduled_task_params['Status']) ? $scheduled_task_params['Status'] : STATUS_ACTIVE, 'RunSchedule' => $scheduled_task_params['RunSchedule'], ); $object->Clear(); $object->SetDBFieldsFromHash($fields_hash); $cron_helper->load($object, 'RunSchedule'); $object->Create(); } else { $object->LoadFromHash( $scheduled_tasks_from_db[$scheduled_task_name] ); } $processed_ids[] = $object->GetID(); } // delete all non-processed scheduled tasks (ones, that were deleted from unit configs) $sql = 'SELECT ' . $object->IDField . ' FROM ' . $object->TableName . ' WHERE (Type = ' . ScheduledTask::TYPE_SYSTEM . ') AND (' . $object->IDField . ' NOT IN (' . implode(',', $processed_ids) . '))'; $delete_ids = $this->Conn->GetCol($sql); if ($delete_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, $delete_ids); } $this->Application->removeObject($event->getPrefixSpecial()); } /** * Don't allow to delete other user's messages * * @param kEvent $event * @param string $type * @return void * @access protected */ protected function customProcessing(kEvent $event, $type) { if ( $event->Name == 'OnMassDelete' && $type == 'before' ) { if ( $this->Application->isDebugMode() ) { // allow to delete system scheduled tasks in debug mode return; } $ids = $event->getEventParam('ids'); if ( $ids ) { $config = $event->getUnitConfig(); $id_field = $config->getIDField(); $sql = 'SELECT ' . $id_field . ' FROM ' . $config->getTableName() . ' WHERE ' . $id_field . ' IN (' . implode(',', $ids) . ') AND Type <> ' . ScheduledTask::TYPE_SYSTEM; $event->setEventParam('ids', $this->Conn->GetCol($sql)); } } } /** * Cancels scheduled tasks, that are currently running * * @param kEvent $event */ function OnMassCancel($event) { $ids = $this->StoreSelectedIDs($event); if ($ids) { $object = $event->getObject( Array ('skip_autoload' => true) ); /* @var $object kDBItem */ foreach ($ids as $id) { $object->Load($id); if ($object->GetDBField('LastRunStatus') == ScheduledTask::LAST_RUN_RUNNING) { // only changes status, doesn't affect currency running scheduled tasks $object->SetDBField('LastRunStatus', ScheduledTask::LAST_RUN_FAILED); $object->Update(); } } } $this->clearSelectedIDs($event); } /** * Loads schedule from database to virtual fields * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemLoad(kEvent $event) { parent::OnAfterItemLoad($event); $object = $event->getObject(); /* @var $object kDBItem */ $cron_helper = $this->Application->recallObject('kCronHelper'); /* @var $cron_helper kCronHelper */ $cron_helper->load($object, 'RunSchedule'); } /** * Validates schedule * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemCreate(kEvent $event) { parent::OnBeforeItemCreate($event); $this->_itemChanged($event); } /** * Validates schedule * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(kEvent $event) { parent::OnBeforeItemUpdate($event); $this->_itemChanged($event); } /** * Validates schedule * * @param kEvent $event * @return void * @access protected */ protected function _itemChanged(kEvent $event) { $object = $event->getObject(); /* @var $object kDBItem */ $cron_helper = $this->Application->recallObject('kCronHelper'); /* @var $cron_helper kCronHelper */ if ( $cron_helper->validateAndSave($object, 'RunSchedule') && !$object->GetDBField('NextRunOn_date') ) { $next_run = $cron_helper->getMatch($object->GetDBField('RunSchedule')); $object->SetDBField('NextRunOn_date', $next_run); $object->SetDBField('NextRunOn_time', $next_run); } } /** * Creates schedule fields * * @param kEvent $event * @return void * @access protected */ protected function OnAfterConfigRead(kEvent $event) { parent::OnAfterConfigRead($event); + // Don't initialize 'scheduled-task' unit, because scheduled tasks are not run during install. + if ( defined('IS_INSTALL') && IS_INSTALL ) { + return; + } + if ( $this->Application->findModule('Name', 'Custom') ) { $config = $event->getUnitConfig(); $fields = $config->getFields(); $fields['Module']['default'] = 'Custom'; $config->setFields($fields); } $cron_helper = $this->Application->recallObject('kCronHelper'); /* @var $cron_helper kCronHelper */ $cron_helper->initUnit($event->Prefix, 'RunSchedule'); } } Index: branches/5.3.x/core/units/images/images_config.php =================================================================== --- branches/5.3.x/core/units/images/images_config.php (revision 16394) +++ branches/5.3.x/core/units/images/images_config.php (revision 16395) @@ -1,187 +1,186 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); $config = Array ( 'Prefix' => 'img', 'Clones' => Array ( 'bb-post-img'=> Array ('ParentPrefix' => 'bb-post'), ), 'ItemClass' => Array ('class' => 'kDBItem', 'file' => '', 'build_event' => 'OnItemBuild'), 'ListClass' => Array ('class' => 'kDBList', 'file' => '', 'build_event' => 'OnListBuild'), 'EventHandlerClass' => Array ('class' => 'ImageEventHandler', 'file' => 'image_event_handler.php', 'build_event' => 'OnBuild'), 'TagProcessorClass' => Array ('class' => 'ImageTagProcessor', 'file' => 'image_tag_processor.php', 'build_event' => 'OnBuild'), 'AutoLoad' => true, 'AggregateTags' => Array ( Array ( 'AggregateTo' => '#PARENT#', 'AggregatedTagName' => 'Image', 'LocalTagName' => 'ItemImageTag', 'LocalSpecial' => '-item', ), Array ( 'AggregateTo' => '#PARENT#', 'AggregatedTagName' => 'ImageSrc', 'LocalTagName' => 'ItemImageTag', 'LocalSpecial' => '-item', ), Array ( 'AggregateTo' => '#PARENT#', 'AggregatedTagName' => 'ImageSize', 'LocalTagName' => 'ItemImageTag', 'LocalSpecial' => '-item', ), Array ( 'AggregateTo' => '#PARENT#', 'AggregatedTagName' => 'ListImages', 'LocalTagName' => 'PrintList2', 'LocalSpecial' => 'list', ), Array ( 'AggregateTo' => '#PARENT#', 'AggregatedTagName' => 'LargeImageExists', 'LocalTagName' => 'LargeImageExists', ), ), 'QueryString' => Array ( 1 => 'id', 2 => 'Page', 3 => 'PerPage', 4 => 'event', ), 'ScheduledTasks' => Array ( 'clean_catalog_images' => Array ('EventName' => 'OnCleanImages', 'RunSchedule' => '0 0 * * 0', 'Status' => STATUS_DISABLED), - 'clean_resized_catalog_images' => Array ('EventName' => 'OnCleanResizedImages', 'RunSchedule' => '0 0 1 * *', 'Status' => STATUS_DISABLED), ), 'IDField' => 'ImageId', 'StatusField' => Array ('Enabled', 'DefaultImg'), // field, that is affected by Approve/Decline events 'TitleField' => 'Name', // field, used in bluebar when editing existing item 'TableName' => TABLE_PREFIX.'CatalogImages', 'ParentTableKey'=> 'ResourceId', // linked field in master table 'ForeignKey' => 'ResourceId', // linked field in subtable 'ParentPrefix' => 'p', 'AutoDelete' => true, 'AutoClone' => true, 'CalculatedFields' => Array ( '' => Array ( 'Preview' => '0', ), ), 'ListSQLs' => Array ( '' => ' SELECT * FROM %s', ), 'ListSortings' => Array ( '' => Array ( 'ForcedSorting' => Array ('Priority' => 'desc'), 'Sorting' => Array ('Name' => 'asc'), ) ), 'Fields' => Array ( 'ImageId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'ResourceId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'Url' => Array ('type' => 'string', 'max_len' => 255, 'default' => '', 'not_null' => 1), 'Name' => Array ('type' => 'string', 'max_len' => 255, 'required' => 1, 'not_null' => 1, 'default' => ''), 'AltName' => Array ('type' => 'string', 'max_len' => 255, 'not_null' => 1, 'default' => ''), 'ImageIndex' => Array ('type' => 'int', 'default' => 0, 'not_null'=>1), 'LocalImage' => Array ('type' => 'int', 'default' => 1, 'not_null'=>1), 'LocalPath' => Array ( 'type' => 'string', 'formatter' => 'kPictureFormatter', 'skip_empty' => 1, 'max_len' => 240, 'default' => '', 'not_null' => 1, 'include_path' => 1, 'allowed_types' => Array ( 'image/jpeg', 'image/pjpeg', 'image/png', 'image/x-png', 'image/gif', 'image/bmp' ), 'error_msgs' => Array ( 'bad_file_format' => '!la_error_InvalidFileFormat!', 'bad_file_size' => '!la_error_FileTooLarge!', 'cant_save_file' => '!la_error_cant_save_file!', ), ), 'Enabled' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Enabled', 0 => 'la_Disabled',), 'default' => 1, 'not_null' => 1, 'use_phrases' => 1, ), 'DefaultImg' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_No', 1 => 'la_Yes'), 'default' => 0, 'not_null' => 1, 'use_phrases' => 1, ), 'ThumbUrl' => Array ('type' => 'string', 'max_len' => 255, 'default' => null), 'Priority' => Array ('type' => 'int', 'default' => 0, 'not_null'=>1), 'ThumbPath' => Array ( 'type' => 'string', 'formatter' => 'kPictureFormatter', 'skip_empty' => 1, 'max_len' => 240, 'default' => NULL, 'include_path' => 1, 'allowed_types' => Array ( 'image/jpeg', 'image/pjpeg', 'image/png', 'image/x-png', 'image/gif', 'image/bmp' ), 'error_msgs' => Array ( 'bad_file_format' => '!la_error_InvalidFileFormat!', 'bad_file_size' => '!la_error_FileTooLarge!', 'cant_save_file' => '!la_error_cant_save_file!', ), ), 'LocalThumb' => Array ('type' => 'int', 'default' => 1, 'not_null'=>1), 'SameImages' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_No', 1 => 'la_Yes'), 'default' => 1, 'not_null' => 1, 'use_phrases' => 1, ), ), 'VirtualFields' => Array ( 'Preview' => Array ('type' => 'string', 'default' => ''), 'ImageUrl' => Array ('type' => 'string', 'default' => ''), ), 'Grids' => Array ( 'Default' => Array ( 'Icons' => Array ( 'default' => 'icon16_item.png', '0_0' => 'icon16_disabled.png', '0_1' => 'icon16_disabled.png', '1_0' => 'icon16_item.png', '1_1' => 'icon16_primary.png', ), 'Fields' => Array ( 'ImageId' => Array ( 'title' => 'column:la_fld_Id', 'filter_block' => 'grid_range_filter', 'width' => 50, ), 'Name' => Array ( 'title' => 'la_col_ImageName' , 'data_block' => 'image_caption_td', 'filter_block' => 'grid_like_filter', 'width' => 100, ), 'AltName' => Array ( 'title' => 'la_col_AltName', 'filter_block' => 'grid_like_filter', 'width' => 150, ), 'Url' => Array ( 'title' => 'la_col_ImageUrl', 'data_block' => 'image_url_td', 'filter_block' => 'grid_like_filter', 'width' => 200, ), 'Preview' => Array ( 'title' => 'la_col_Preview', 'data_block' => 'image_preview_td', 'filter_block' => 'grid_like_filter', 'width' => 200, ), 'Enabled' => Array ( 'title' => 'la_col_ImageEnabled', 'filter_block' => 'grid_options_filter', 'width' => 80, ), ), ), ), -); \ No newline at end of file +); Index: branches/5.3.x/core/units/images/image_tag_processor.php =================================================================== --- branches/5.3.x/core/units/images/image_tag_processor.php (revision 16394) +++ branches/5.3.x/core/units/images/image_tag_processor.php (revision 16395) @@ -1,506 +1,508 @@ <?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); $tag_name = $params['original_tag']; return $this->$tag_name($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(), 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) { $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) . '/' . 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']); + $this->Application->Parser->DataExists = true; + 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); } } Index: branches/5.3.x/core/units/images/image_event_handler.php =================================================================== --- branches/5.3.x/core/units/images/image_event_handler.php (revision 16394) +++ branches/5.3.x/core/units/images/image_event_handler.php (revision 16395) @@ -1,496 +1,474 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); class ImageEventHandler extends kDBEventHandler { /** * Allows to override standard permission mapping * * @return void * @access protected * @see kEventHandler::$permMapping */ protected function mapPermissions() { parent::mapPermissions(); $permissions = Array ( 'OnCleanImages' => Array ('subitem' => true), - 'OnCleanResizedImages' => Array ('subitem' => true), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Define alternative event processing method names * * @return void * @see kEventHandler::$eventMethods * @access protected */ protected function mapEvents() { parent::mapEvents(); // ensure auto-adding of approve/decline and so on events $image_events = Array ( 'OnAfterCopyToTemp'=>'ImageAction', 'OnBeforeDeleteFromLive'=>'ImageAction', 'OnBeforeCopyToLive'=>'ImageAction', 'OnBeforeItemDelete'=>'ImageAction', 'OnAfterClone'=>'ImageAction', ); $this->eventMethods = array_merge($this->eventMethods, $image_events); } /** * Returns special of main item for linking with sub-item * * @param kEvent $event * @return string * @access protected */ protected function getMainSpecial(kEvent $event) { if ( $event->Special == 'list' && !$this->Application->isAdmin ) { // ListImages aggregated tag uses this special return ''; } return parent::getMainSpecial($event); } /** * Don't allow to delete primary category item image, when there are no more images * * @param kEvent $event * @param string $type * @return void * @access protected */ protected function customProcessing(kEvent $event, $type) { $object = $event->getObject(); /* @var $object kDBItem */ if ( $event->Name == 'OnMassDelete' && $type == 'before' ) { $ids = $event->getEventParam('ids'); $parent_info = $object->getLinkedInfo($event->Special); $sql = 'SELECT ImageId FROM ' . $object->TableName . ' WHERE DefaultImg = 1 AND ' . $parent_info['ForeignKey'] . ' = ' . $parent_info['ParentId']; $primary_file_id = $this->Conn->GetOne($sql); if ( $primary_file_id ) { $file_id_index = array_search($primary_file_id, $ids); if ( $file_id_index ) { // allow deleting of primary product file, when there is another file to make primary $sql = 'SELECT COUNT(*) FROM ' . $object->TableName . ' WHERE DefaultImg = 0 AND ' . $parent_info['ForeignKey'] . ' = ' . $parent_info['ParentId']; $non_primary_file_count = $this->Conn->GetOne($sql); if ( $non_primary_file_count ) { unset($ids[$file_id_index]); } } } $event->setEventParam('ids', $ids); } switch ($type) { case 'before' : // empty unused fields $object->SetDBField($object->GetDBField('LocalImage') ? 'Url' : 'LocalPath', ''); $object->SetDBField($object->GetDBField('LocalThumb') ? 'ThumbUrl' : 'ThumbPath', ''); if ( $object->GetDBField('SameImages') ) { $object->SetDBField('LocalImage', 1); $object->SetDBField('LocalPath', ''); $object->SetDBField('Url', ''); } break; case 'after': // make sure, that there is only one primary image for the item if ( $object->GetDBField('DefaultImg') ) { $sql = 'UPDATE ' . $object->TableName . ' SET DefaultImg = 0 WHERE ResourceId = ' . $object->GetDBField('ResourceId') . ' AND ImageId <> ' . $object->GetID(); $this->Conn->Query($sql); } break; } } /** * Performs temp-table related action on current image record * * @param kEvent $event * @return void * @access protected */ protected function ImageAction($event) { $id = $event->getEventParam('id'); $object = $this->Application->recallObject($event->Prefix . '.-item', $event->Prefix, Array ('skip_autoload' => true)); /* @var $object kDBItem */ if ( in_array($event->Name, Array ('OnBeforeDeleteFromLive', 'OnAfterClone')) ) { $object->SwitchToLive(); } elseif ( $event->Name == 'OnBeforeItemDelete' ) { // keep current table } else { $object->SwitchToTemp(); } $object->Load($id); $file_helper = $this->Application->recallObject('FileHelper'); /* @var $file_helper FileHelper */ $fields = Array ('LocalPath' => 'LocalImage', 'ThumbPath' => 'LocalThumb'); foreach ($fields as $a_field => $mode_field) { $file = $object->GetDBField($a_field); if ( !$file ) { continue; } $source_file = FULL_PATH . $file; switch ($event->Name) { // Copy image files to pending dir and update corresponding fields in temp record // Checking for existing files and renaming if necessary - two users may upload same pending files at the same time! case 'OnAfterCopyToTemp': $file = preg_replace('/^' . preg_quote(IMAGES_PATH, '/') . '/', IMAGES_PENDING_PATH, $file, 1); $new_file = $file_helper->ensureUniqueFilename(FULL_PATH, $file); $dst_file = FULL_PATH . $new_file; copy($source_file, $dst_file); $object->SetFieldOption($a_field, 'skip_empty', false); $object->SetDBField($a_field, $new_file); break; // Copy image files to live dir (checking if file exists and renaming if necessary) // and update corresponding fields in temp record (which gets copied to live automatically) case 'OnBeforeCopyToLive': if ( $object->GetDBField($mode_field) ) { // if image is local -> rename file if it exists in live folder $file = preg_replace('/^' . preg_quote(IMAGES_PENDING_PATH, '/') . '/', IMAGES_PATH, $file, 1); $new_file = $file_helper->ensureUniqueFilename(FULL_PATH, $file); $dst_file = FULL_PATH . $new_file; rename($source_file, $dst_file); } else { // if image is remote url - remove local file (if any), update local file field with empty value if ( file_exists($source_file) ) { @unlink($source_file); } $new_file = ''; } $object->SetFieldOption($a_field, 'skip_empty', false); $object->SetDBField($a_field, $new_file); break; case 'OnBeforeDeleteFromLive': // Delete image files from live folder before copying over from temp case 'OnBeforeItemDelete': // Delete image files when deleting Image object @unlink(FULL_PATH . $file); break; case 'OnAfterClone': // Copy files when cloning objects, renaming it on the fly $new_file = $file_helper->ensureUniqueFilename(FULL_PATH, $file); $dst_file = FULL_PATH . $new_file; copy($source_file, $dst_file); $object->SetFieldOption($a_field, 'skip_empty', false); $object->SetDBField($a_field, $new_file); break; } } if ( in_array($event->Name, Array ('OnAfterClone', 'OnBeforeCopyToLive', 'OnAfterCopyToTemp')) ) { $object->Update(null, null, true); } } /** * Sets primary image of user/category/category item * * @param kEvent $event * @return void * @access protected */ protected function OnSetPrimary($event) { $object = $event->getObject(); /* @var $object kDBItem */ $object->SetDBField('DefaultImg', 1); $object->Update(); } /** * Occurs before updating item * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeItemUpdate(kEvent $event) { parent::OnBeforeItemUpdate($event); $this->processImageStatus($event); } /** * Occurs after creating item * * @param kEvent $event * @return void * @access protected */ protected function OnAfterItemCreate(kEvent $event) { parent::OnAfterItemCreate($event); $this->processImageStatus($event); $object = $event->getObject(); /* @var $object kDBItem */ $object->Update(); } /** * Occurs before item changed * * @param kEvent $event */ function processImageStatus($event) { $object = $event->getObject(); /* @var $object kDBItem */ $id = $object->GetDBField('ResourceId'); $sql = 'SELECT ImageId FROM ' . $object->TableName . ' WHERE ResourceId = ' . $id . ' AND DefaultImg = 1'; $primary_image_id = $this->Conn->GetOne($sql); if ( !$primary_image_id ) { $object->SetDBField('DefaultImg', 1); } if ( $object->GetDBField('DefaultImg') && $object->Validate() ) { $sql = 'UPDATE ' . $object->TableName . ' SET DefaultImg = 0 WHERE ResourceId = ' . $id . ' AND ImageId <> ' . $object->GetDBField('ImageId'); $this->Conn->Query($sql); $object->SetDBField('Enabled', 1); } } /** * Apply any custom changes to list's sql query * * @param kEvent $event * @return void * @access protected * @see kDBEventHandler::OnListBuild() */ protected function SetCustomQuery(kEvent $event) { parent::SetCustomQuery($event); $object = $event->getObject(); /* @var $object kDBList */ if ( !$this->Application->isAdminUser ) { $object->addFilter('active', '%1$s.Enabled = 1'); } $product_id = $event->getEventParam('product_id'); if ( $product_id ) { $object->removeFilter('parent_filter'); $sql = 'SELECT ResourceId FROM ' . $this->Application->getUnitConfig('p')->getTableName() . ' WHERE ProductId = ' . $product_id; $resource_id = (int)$this->Conn->GetOne($sql); $object->addFilter('product_images', '%1$s.ResourceId = ' . $resource_id); } $search_helper = $this->Application->recallObject('SearchHelper'); /* @var $search_helper kSearchHelper */ $types = $event->getEventParam('types'); $except_types = $event->getEventParam('except'); $type_clauses = $this->getTypeClauses($event); $search_helper->SetComplexFilter($event, $type_clauses, $types, $except_types); } /** * Return type clauses for list bulding on front * * @param kEvent $event * @return Array */ function getTypeClauses($event) { $type_clauses = Array (); $type_clauses['additional']['include'] = '%1$s.DefaultImg != 1'; $type_clauses['additional']['except'] = '%1$s.DefaultImg = 1'; $type_clauses['additional']['having_filter'] = false; return $type_clauses; } /** * [SCHEDULED TASK] Remove unused images from "/system/images" and "/system/images/pending" folders * * @param kEvent $event */ function OnCleanImages($event) { // 1. get images, that are currently in use $active_images = $this->_getActiveImages( $this->Application->getUnitConfig('img')->getTableName() ); $active_images[] = 'noimage.gif'; // 2. get images on disk $this->_deleteUnusedImages(FULL_PATH . IMAGES_PATH, $active_images); // 3. get images in use from "images/pending" folder $active_images = $this->_getPendingImages(); // 4. get image on disk $this->_deleteUnusedImages(FULL_PATH . IMAGES_PENDING_PATH, $active_images); } /** * Gets image filenames (no path) from given table * * @param string $image_table * @return Array */ function _getActiveImages($image_table) { $sql = 'SELECT LocalPath, ThumbPath FROM ' . $image_table . ' WHERE COALESCE(LocalPath, "") <> "" OR COALESCE(ThumbPath) <> ""'; $images = $this->Conn->Query($sql); $active_images = Array (); foreach ($images as $image) { if ($image['LocalPath']) { $active_images[] = basename($image['LocalPath']); } if ($image['ThumbPath']) { $active_images[] = basename($image['ThumbPath']); } } return $active_images; } /** * Gets active images, that are currently beeing edited inside temporary tables * * @return Array */ function _getPendingImages() { $tables = $this->Conn->GetCol('SHOW TABLES'); $mask_edit_table = '/'.TABLE_PREFIX.'ses_(.*)_edit_' . TABLE_PREFIX . 'CatalogImages/'; $active_images = Array (); foreach ($tables as $table) { if (!preg_match($mask_edit_table, $table)) { continue; } $active_images = array_unique( array_merge($active_images, $this->_getActiveImages($table)) ); } return $active_images; } /** * Deletes all files in given path, except of given $active_images * * @param string $path * @param Array $active_images */ function _deleteUnusedImages($path, &$active_images) { $images = glob($path . '*.*'); if ($images) { $images = array_map('basename', $images); // delete images, that are on disk, but are not mentioned in CatalogImages table $delete_images = array_diff($images, $active_images); foreach ($delete_images as $delete_image) { unlink($path . $delete_image); } } } - /** - * [SCHEDULED TASK] Remove all images from "/system/images/resized" and "/system/images/pending/resized" folders - * - * @param kEvent $event - */ - function OnCleanResizedImages($event) - { - $images = glob(FULL_PATH . IMAGES_PATH . 'resized/*.*'); - if ($images) { - foreach ($images as $image) { - unlink($image); - } - } - - $images = glob(FULL_PATH . IMAGES_PENDING_PATH . 'resized/*.*'); - if ($images) { - foreach ($images as $image) { - unlink($image); - } - } - } -} \ No newline at end of file +} Index: branches/5.3.x/core/units/configuration/configuration_event_handler.php =================================================================== --- branches/5.3.x/core/units/configuration/configuration_event_handler.php (revision 16394) +++ branches/5.3.x/core/units/configuration/configuration_event_handler.php (revision 16395) @@ -1,600 +1,600 @@ <?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); $sorting_prefix = $this->getSortingPrefix($variable_name); if ( $sorting_prefix ) { $sql = 'DELETE FROM ' . TABLE_PREFIX . 'UserPersistentSessionData WHERE VariableName LIKE "' . $sorting_prefix . '%Sortings.%"'; $this->Conn->Query($sql); } } 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; } } } /** * Returns prefix, related to given sorting system setting * * @param string $system_setting System setting. * * @return boolean|string */ protected function getSortingPrefix($system_setting) { foreach ( $this->Application->ModuleInfo as $module_info ) { if ( $module_info['Name'] == 'In-Portal' ) { continue; } $prefix = ($module_info['Var'] == 'adm') ? 'c' : $module_info['Var']; $config_mapping = $this->Application->getUnitConfig($prefix)->getConfigMapping(); if ( (isset($config_mapping['DefaultSorting1Field']) && $system_setting == $config_mapping['DefaultSorting1Field']) || (isset($config_mapping['DefaultSorting2Field']) && $system_setting == $config_mapping['DefaultSorting2Field']) ) { return $prefix; } } return false; } /** * 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); $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'); + $require_refresh = Array ('AdvancedUserManagement', 'Site_Name', 'AdminConsoleInterface', 'UsePopups', 'Catalog_PreselectModuleTab'); $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); } } Index: branches/5.3.x/core/units/system_event_subscriptions/system_event_subscriptions_config.php =================================================================== --- branches/5.3.x/core/units/system_event_subscriptions/system_event_subscriptions_config.php (revision 16394) +++ branches/5.3.x/core/units/system_event_subscriptions/system_event_subscriptions_config.php (revision 16395) @@ -1,142 +1,146 @@ <?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!'); $config = Array ( 'Prefix' => 'system-event-subscription', 'ItemClass' => Array ('class' => 'kDBItem', 'file' => '', 'build_event' => 'OnItemBuild'), 'ListClass' => Array ('class' => 'kDBList', 'file' => '', 'build_event' => 'OnListBuild'), 'EventHandlerClass' => Array ('class' => 'SystemEventSubscriptionEventHandler', 'file' => 'system_event_subscription_eh.php', 'build_event' => 'OnBuild'), 'TagProcessorClass' => Array ('class' => 'SystemEventSubscriptionTagProcessor', 'file' => 'system_event_subscription_tp.php', 'build_event' => 'OnBuild'), + 'RegisterClasses' => Array ( + Array ('pseudo' => 'kSubscriptionAnalyzer', 'class' => 'kSubscriptionAnalyzer', 'file' => 'system_event_subscription_tp.php', 'build_event' => ''), + ), + 'AutoLoad' => true, 'QueryString' => Array ( 1 => 'id', 2 => 'Page', 3 => 'PerPage', 4 => 'event', 5 => 'mode', ), 'IDField' => 'SubscriptionId', 'TableName' => TABLE_PREFIX . 'SystemEventSubscriptions', 'TitleField' => 'SubscriptionId', 'TitlePresets' => Array ( 'default' => Array ( 'new_status_labels' => Array ('system-event-subscription' => '!la_title_AddingSystemEventSubscription!'), 'edit_status_labels' => Array ('system-event-subscription' => '!la_title_EditingSystemEventSubscription!'), ), 'system_event_subscription_list' => Array ( 'prefixes' => Array ('system-event-subscription_List'), 'format' => "!la_title_SystemEventSubscriptions!", 'toolbar_buttons' => Array ('new_item', 'edit', 'delete', 'view', 'dbl-click'), ), 'system_event_subscription_edit' => Array ( 'prefixes' => Array ('system-event-subscription'), 'format' => "#system-event-subscription_status# '#system-event-subscription_titlefield#'", 'toolbar_buttons' => Array ('select', 'cancel', 'reset_edit', 'prev', 'next'), ), ), 'PermSection' => Array ('main' => 'in-portal:system_event_subscriptions'), 'Sections' => Array ( 'in-portal:system_event_subscriptions' => Array ( 'parent' => 'in-portal:users', 'icon' => 'system_event_subscriptions', 'label' => 'la_title_SystemEventSubscriptions', 'url' => Array ('t' => 'system_event_subscriptions/system_event_subscription_list', 'pass' => 'm'), 'permissions' => Array ('view', 'add', 'edit', 'delete'), 'priority' => 6, 'type' => stTREE, ), ), 'ListSQLs' => Array ( '' => ' SELECT %1$s.* %2$s FROM %1$s LEFT JOIN ' . TABLE_PREFIX . 'EmailTemplates et ON %1$s.EmailTemplateId = et.TemplateId LEFT JOIN ' . TABLE_PREFIX . 'Users u ON %1$s.UserId = u.PortalUserId LEFT JOIN ' . TABLE_PREFIX . 'Categories c ON %1$s.CategoryId = c.CategoryId', ), 'CalculatedFields' => Array ( '' => Array ( 'BindToSystemEvent' => 'et.BindToSystemEvent', 'EmailTemplateName' => 'et.TemplateName', 'Username' => 'CASE %1$s.UserId WHEN ' . USER_ROOT . ' THEN "root" WHEN ' . USER_GUEST . ' THEN "Guest" ELSE IF(CONCAT(u.FirstName, u.LastName) <> "", CONCAT(u.FirstName, " ", u.LastName), IF(%1$s.UserId IS NULL, "Guest", IF(u.Username = "", u.Email, u.Username))) END', 'CategoryName' => 'c.l%2$s_Name', 'ItemName' => '%1$s.ItemId', 'ParentItemName' => '%1$s.ParentItemId', ), ), 'ListSortings' => Array ( '' => Array ( 'Sorting' => Array ('SubscriptionId' => 'desc'), ) ), 'Fields' => Array ( 'SubscriptionId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0), 'EmailTemplateId' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options_sql' => 'SELECT %s FROM ' . TABLE_PREFIX . 'EmailTemplates WHERE BindToSystemEvent <> "" ORDER BY TemplateName ASC', 'option_key_field' => 'TemplateId', 'option_title_field' => 'BindToSystemEvent', 'required' => 1, 'default' => NULL ), 'SubscriberEmail' => Array ('type' => 'string', 'max_len' => 255, 'not_null' => 1, 'default' => ''), 'UserId' => Array ('type' => 'int', 'formatter' => 'kLEFTFormatter', 'error_msgs' => Array ('invalid_option' => '!la_error_UserNotFound!'), 'options' => Array (USER_ROOT => 'root', USER_GUEST => 'Guest'), 'left_sql' => 'SELECT %s FROM ' . TABLE_PREFIX . 'Users WHERE %s', 'left_key_field' => 'PortalUserId', 'left_title_field' => USER_TITLE_FIELD, 'default' => NULL), 'CategoryId' => Array ('type' => 'int', 'default' => NULL), 'IncludeSublevels' => Array ( 'type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 1 ), 'ItemId' => Array ('type' => 'int', 'default' => NULL), 'ParentItemId' => Array ('type' => 'int', 'default' => NULL), 'SubscribedOn' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'required' => 1, 'default' => '#NOW#') ), 'VirtualFields' => Array ( 'Username' => Array ('type' => 'string', 'default' => ''), 'EmailTemplateName' => Array ('type' => 'string', 'default' => ''), 'CategoryName' => Array ('type' => 'string', 'formatter' => 'kMultiLanguage', 'default' => ''), ), 'Grids' => Array ( 'Default' => Array ( 'Fields' => Array ( 'SubscriptionId' => Array ('title' => 'column:la_fld_Id', 'filter_block' => 'grid_range_filter', 'width' => 80), 'EmailTemplateId' => Array ('title' => 'column:la_fld_SystemEvent', 'filter_block' => 'grid_options_filter', 'width' => 170), 'EmailTemplateName' => Array ('filter_block' => 'grid_like_filter', 'width' => 170), 'SubscriberEmail' => Array ('title' => 'column:la_fld_Email', 'filter_block' => 'grid_like_filter'), 'UserId' => Array ('title' => 'la_col_PortalUserId', 'filter_block' => 'grid_range_filter', 'width' => 80), 'Username' => Array ('filter_block' => 'grid_like_filter'), 'CategoryId' => Array ('title' => 'column:la_fld_CategoryId', /*'data_block' => 'grid_category_td',*/ 'filter_block' => 'grid_range_filter', 'width' => 100), 'IncludeSublevels' => Array ('filter_block' => 'grid_options_filter', 'width' => 100), 'CategoryName' => Array ('title' => 'column:la_fld_Category', 'filter_block' => 'grid_like_filter', 'width' => 200), 'ItemId' => Array ('filter_block' => 'grid_range_filter', 'width' => 80), 'ItemName' => Array ('data_block' => 'grid_item_name_td', 'associated_field' => 'ItemId', 'filter_block' => 'grid_like_filter', 'width' => 120), 'ParentItemId' => Array ('filter_block' => 'grid_range_filter', 'width' => 80), 'ParentItemName' => Array ('data_block' => 'grid_item_name_td', 'associated_field' => 'ParentItemId', 'filter_block' => 'grid_like_filter', 'width' => 120), 'SubscribedOn' => Array ('filter_block' => 'grid_date_range_filter', 'width' => 170) ), ), ), -); \ 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 16394) +++ branches/5.3.x/core/units/admin/admin_events_handler.php (revision 16395) @@ -1,1420 +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 (), ) ); $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' ) { 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); $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')); + $this->Application->StorePersistentVar('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('; $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 .= ',' . ($expand ? '' : ' '); array_pop($this->parentPath); } $prepend = $expand ? "\n" . str_repeat("\t", $level) : ''; if ( !$expand ) { $ret = rtrim($ret, ', '); } $ret .= $prepend . ')'; } else { if ( is_null($var) ) { $ret = 'null'; } elseif ( is_string($var) ) { $ret = "'" . $var . "'"; } else { $ret = $var; } } return $ret; } } Index: branches/5.3.x/core/units/admin/admin_config.php =================================================================== --- branches/5.3.x/core/units/admin/admin_config.php (revision 16394) +++ branches/5.3.x/core/units/admin/admin_config.php (revision 16395) @@ -1,101 +1,105 @@ <?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' => 'adm', 'ItemClass' => Array ('class' => 'kDBItem', 'file' => '', 'build_event' => 'OnItemBuild'), 'EventHandlerClass' => Array ('class' => 'AdminEventsHandler', 'file' => 'admin_events_handler.php', 'build_event' => 'OnBuild'), 'TagProcessorClass' => Array ('class' => 'AdminTagProcessor', 'file' => 'admin_tag_processor.php', 'build_event' => 'OnBuild'), + 'RegisterClasses' => Array ( + Array ('pseudo' => 'UnitConfigDecorator', 'class' => 'UnitConfigDecorator', 'file' => 'admin_events_handler.php', 'build_event' => ''), + ), + 'AutoLoad' => false, // because defaults to `true` 'QueryString' => Array ( 1 => 'event', ), 'ScheduledTasks' => Array ( 'optimize_performance' => Array ('EventName' => 'OnOptimizePerformance', 'RunSchedule' => '0 0 * * *'), 'pre_resize_images' => Array ('EventName' => 'OnPreResizeImages', 'RunSchedule' => '0 * * * *'), ), 'TitlePresets' => Array ( 'tree_root' => Array ('format' => '!la_section_overview!'), 'tree_reports' => Array ('format' => '!la_section_overview!'), 'tree_system' => Array ('format' => '!la_section_overview!'), 'tree_tools' => Array ('format' => '!la_section_overview!'), 'system_tools' => Array ('format' => '!la_title_SystemTools!'), 'backup' => Array ('format' => '!la_performing_backup! - !la_Step! <span id="step_number"></span>'), 'import' => Array ('format' => '!la_performing_import! - !la_Step! <span id="step_number"></span>'), 'restore' => Array ('format' => '!la_performing_restore! - !la_Step! <span id="step_number"></span>'), 'server_info' => Array ('format' => '!la_tab_ServerInfo!'), 'sql_query' => Array ('format' => '!la_tab_QueryDB!'), 'no_permissions' => Array ('format' => '!la_title_NoPermissions!'), 'column_picker' => Array ('format' => '!la_title_ColumnPicker!'), 'csv_export' => Array ('format' => '!la_title_CSVExport!'), 'csv_import' => Array ('format' => '!la_title_CSVImport!'), ), 'PermSection' => Array ('main' => 'in-portal:service'), 'Sections' => Array ( 'in-portal:root' => Array ( 'parent' => null, 'icon' => 'site', 'label' => 'SITE_NAME', 'url' => Array ('t' => 'index', 'pass' => 'm'), 'permissions' => Array ('view'), 'priority' => 0, 'container' => true, 'type' => stTREE, 'icon_module' => 'core', ), 'in-portal:service' => Array ( 'parent' => 'in-portal:tools', 'icon' => 'service', 'label' => 'la_tab_Service', 'url' => Array ('t' => 'tools/system_tools', 'pass' => 'm'), 'permissions' => Array ('view', 'edit'), 'priority' => 6, 'type' => stTREE, ), ), 'ListSQLs' => Array ( '' => '', // to prevent warning ), 'Fields' => Array (), // we need empty array because kernel doesn't use virtual fields else 'VirtualFields' => Array ( 'ImportFile' => Array ( 'type' => 'string', 'formatter' => 'kUploadFormatter', 'max_size' => MAX_UPLOAD_SIZE, // in Bytes ! 'error_msgs' => Array ( 'cant_open_file' => '!la_error_CantOpenFile!', 'no_matching_columns' => '!la_error_NoMatchingColumns!', ), 'file_types' => '*.csv', 'files_description' => '!la_hint_CSVFiles!', 'upload_dir' => '/system/import/', // relative to project's home 'multiple' => false, 'direct_links' => false, 'default' => null, ), 'Content' => Array ('type' => 'string', 'default' => ''), ), -); \ No newline at end of file +); Index: branches/5.3.x/core/units/admin/admin_tag_processor.php =================================================================== --- branches/5.3.x/core/units/admin/admin_tag_processor.php (revision 16394) +++ branches/5.3.x/core/units/admin/admin_tag_processor.php (revision 16395) @@ -1,1222 +1,1226 @@ <?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 AdminTagProcessor extends kDBTagProcessor { /** * Allows to execute js script after the page is fully loaded * * @param Array $params * @return string */ function AfterScript($params) { $after_script = $this->Application->GetVar('after_script'); if ($after_script) { return '<script type="text/javascript">'.$after_script.'</script>'; } return ''; } /** * Returns section title with #section# keyword replaced with current section * * @param Array $params * @return string */ function GetSectionTitle($params) { if (array_key_exists('default', $params)) { return $params['default']; } return $this->Application->Phrase( kUtil::replaceModuleSection($params['phrase']) ); } /** * Returns section icon with #section# keyword replaced with current section * * @param Array $params * @return string */ function GetSectionIcon($params) { return kUtil::replaceModuleSection($params['icon']); } /** * Returns version of module by name * * @param Array $params * @return string */ function ModuleVersion($params) { return $this->Application->findModule('Name', $params['module'], 'Version'); } /** * Used in table form section drawing * * @param Array $params * @return string */ function DrawTree($params) { static $deep_level = 0; // when processings, then sort children by priority (key of children array) $ret = ''; $section_name = $params['section_name']; $params['name'] = $this->SelectParam($params, 'name,render_as,block'); $sections_helper = $this->Application->recallObject('SectionsHelper'); /* @var $sections_helper kSectionsHelper */ $section_data =& $sections_helper->getSectionData($section_name); $params['children_count'] = isset($section_data['children']) ? count($section_data['children']) : 0; $params['deep_level'] = $deep_level++; $template = $section_data['url']['t']; unset($section_data['url']['t']); $section_data['section_url'] = $this->Application->HREF($template, '', $section_data['url']); $ret .= $this->Application->ParseBlock( array_merge($params, $section_data) ); if (!isset($section_data['children'])) { return $ret; } ksort($section_data['children'], SORT_NUMERIC); foreach ($section_data['children'] as $section_name) { if (!$sections_helper->sectionVisible($section_name)) { continue; } $params['section_name'] = $section_name; $ret .= $this->DrawTree($params); $deep_level--; } return $ret; } function SectionInfo($params) { $section = $params['section']; if ($section == '#session#') { $section = $this->Application->RecallVar('section'); } $sections_helper = $this->Application->recallObject('SectionsHelper'); /* @var $sections_helper kSectionsHelper */ $section_data =& $sections_helper->getSectionData($section); if (!$section_data) { throw new Exception('Use of undefined section "<strong>' . $section . '</strong>" in "<strong>' . __METHOD__ . '</strong>"'); } if (array_key_exists('parent', $params) && $params['parent']) { do { $section = $section_data['parent']; $section_data =& $sections_helper->getSectionData($section); } while (array_key_exists('use_parent_header', $section_data) && $section_data['use_parent_header']); } $info = $params['info']; switch ($info) { case 'module_path': if (isset($params['module']) && $params['module']) { $module = $params['module']; } elseif (isset($section_data['icon_module'])) { $module = $section_data['icon_module']; } else { $module = '#session#'; } $res = $this->ModulePath(array('module' => $module)); break; case 'perm_section': $res = $sections_helper->getPermSection($section); break; case 'label': $res = ''; if ( $section ) { if ( $section == 'in-portal:root' ) { // don't translate label for top section, because it's already translated $res = $section_data['label']; } else { $no_editing = array_key_exists('no_editing', $params) ? $params['no_editing'] : false; $res = $this->Application->Phrase($section_data['label'], !$no_editing); } } break; default: $res = $section_data[$info]; break; } if (array_key_exists('as_label', $params) && $params['as_label']) { $res = $this->Application->Phrase($res); } return $res; } function PrintSection($params) { $section_name = $params['section_name']; if ($section_name == '#session#') { $section_name = $this->Application->RecallVar('section'); } $sections_helper = $this->Application->recallObject('SectionsHelper'); /* @var $sections_helper kSectionsHelper */ if (isset($params['use_first_child']) && $params['use_first_child']) { $section_name = $sections_helper->getFirstChild($section_name, true); } $section_data =& $sections_helper->getSectionData($section_name); $params['name'] = $this->SelectParam($params, 'name,render_as,block'); $params['section_name'] = $section_name; $url_params = $section_data['url']; unset($url_params['t']); $section_data['section_url'] = $this->Application->HREF($section_data['url']['t'], '', $url_params); $ret = $this->Application->ParseBlock( array_merge($params, $section_data) ); return $ret; } /** * Used in XML drawing for tree * * @param Array $params * @return string */ function PrintSections($params) { // when processings, then sort children by priority (key of children array) $ret = ''; $section_name = $params['section_name']; if ($section_name == '#session#') { $section_name = $this->Application->RecallVar('section'); } $sections_helper = $this->Application->recallObject('SectionsHelper'); /* @var $sections_helper kSectionsHelper */ $section_data =& $sections_helper->getSectionData($section_name); $params['name'] = $this->SelectParam($params, 'name,render_as,block'); if (!isset($section_data['children'])) { return ''; } ksort($section_data['children'], SORT_NUMERIC); foreach ($section_data['children'] as $section_name) { $params['section_name'] = $section_name; $section_data =& $sections_helper->getSectionData($section_name); if (!$sections_helper->sectionVisible($section_name)) { continue; } else { $show_mode = isset($section_data['show_mode']) ? $section_data['show_mode'] : smNORMAL; $section_data['debug_only'] = ($show_mode == smDEBUG) || ($show_mode == smSUPER_ADMIN) ? 1 : 0; } if (isset($section_data['tabs_only']) && $section_data['tabs_only']) { $perm_status = false; $folder_label = $section_data['label']; ksort($section_data['children'], SORT_NUMERIC); foreach ($section_data['children'] as $priority => $section_name) { // if only tabs in this section & none of them have permission, then skip section too $section_name = $sections_helper->getPermSection($section_name); $perm_status = $this->Application->CheckPermission($section_name.'.view', 1); if ($perm_status) { break; } } if (!$perm_status) { // no permission for all tabs -> don't display tree node either continue; } $params['section_name'] = $section_name; $section_data =& $sections_helper->getSectionData($section_name); $section_data['label'] = $folder_label; // use folder label in tree $section_data['is_tab'] = 1; } else { $section_name = $sections_helper->getPermSection($section_name); if (!$this->Application->CheckPermission($section_name.'.view', 1)) continue; } $params['children_count'] = isset($section_data['children']) ? count($section_data['children']) : 0; // remove template, so it doesn't appear as additional parameter in url $template = $section_data['url']['t']; unset($section_data['url']['t']); $section_data['section_url'] = $this->Application->HREF($template, '', $section_data['url']); $late_load = getArrayValue($section_data, 'late_load'); if ($late_load) { $t = $late_load['t']; unset($late_load['t']); $section_data['late_load'] = $this->Application->HREF($t, '', $late_load); $params['children_count'] = 99; } else { $section_data['late_load'] = ''; } // restore template $section_data['url']['t'] = $template; $ret .= $this->Application->ParseBlock( array_merge($params, $section_data) ); $params['section_name'] = $section_name; } return preg_replace("/\r\n|\n/", '', $ret); } function ListSectionPermissions($params) { $section_name = isset($params['section_name']) ? $params['section_name'] : $this->Application->GetVar('section_name'); $sections_helper = $this->Application->recallObject('SectionsHelper'); /* @var $sections_helper kSectionsHelper */ $section_data =& $sections_helper->getSectionData($section_name); $block_params = array_merge($section_data, Array('name' => $params['render_as'], 'section_name' => $section_name)); $ret = ''; foreach ($section_data['permissions'] as $perm_name) { if (preg_match('/^advanced:(.*)/', $perm_name) != $params['type']) continue; $block_params['perm_name'] = $perm_name; $ret .= $this->Application->ParseBlock($block_params); } return $ret; } function ModuleInclude($params) { foreach ($params as $param_name => $param_value) { $params[$param_name] = kUtil::replaceModuleSection($param_value); } return $this->Application->ProcessParsedTag('m', 'ModuleInclude', $params); } function TodayDate($params) { return date($params['format']); } /** * Draws section tabs using block name passed * * @param Array $params */ function ListTabs($params) { $sections_helper = $this->Application->recallObject('SectionsHelper'); /* @var $sections_helper kSectionsHelper */ $section_data =& $sections_helper->getSectionData($params['section_name']); $ret = ''; $block_params = Array('name' => $params['render_as']); ksort($section_data['children'], SORT_NUMERIC); foreach ($section_data['children'] as $priority => $section_name) { $perm_section = $sections_helper->getPermSection($section_name); if ( !$this->Application->CheckPermission($perm_section.'.view') ) { continue; } $tab_data =& $sections_helper->getSectionData($section_name); $block_params['t'] = $tab_data['url']['t']; $block_params['pass'] = $tab_data['url']['pass']; $block_params['title'] = $tab_data['label']; $block_params['main_prefix'] = $section_data['SectionPrefix']; $ret .= $this->Application->ParseBlock($block_params); } return $ret; } /** * Returns list of module item tabs that have view permission in current category * * @param Array $params */ function ListCatalogTabs($params) { $ret = ''; $special = isset($params['special']) ? $params['special'] : ''; $replace_main = isset($params['replace_m']) && $params['replace_m']; $skip_prefixes = isset($params['skip_prefixes']) ? explode(',', $params['skip_prefixes']) : Array (); $block_params = $this->prepareTagParams($params); $block_params['name'] = $params['render_as']; foreach ($this->Application->ModuleInfo as $module_name => $module_info) { $prefix = $module_info['Var']; if ( $prefix == 'm' && $replace_main ) { $prefix = 'c'; } if ( $this->Application->prefixRegistred($prefix) ) { $config = $this->Application->getUnitConfig($prefix); } else { $config = null; } if ( in_array($prefix, $skip_prefixes) || !is_object($config) || !$config->getCatalogItem() ) { continue; } $icon = $config->getCatalogTabIcon(); if ( strpos($icon, ':') !== false ) { list ($icon_module, $icon) = explode(':', $icon, 2); } else { $icon_module = 'core'; } $label = $config->getSetting($params['title_property']); $block_params['title'] = $label; $block_params['prefix'] = $prefix; $block_params['icon_module'] = $icon_module; $block_params['icon'] = $icon; $ret .= $this->Application->ParseBlock($block_params); } return $ret; } /** * Renders inividual catalog tab based on prefix and title_property given * * @param Array $params * @return string */ function CatalogTab($params) { $config = $this->Application->getUnitConfig($params['prefix']); $icon = $config->getCatalogTabIcon(); if ( strpos($icon, ':') !== false ) { list ($icon_module, $icon) = explode(':', $icon, 2); } else { $icon_module = 'core'; } $block_params = $this->prepareTagParams($params); $block_params['name'] = $params['render_as']; $block_params['icon_module'] = $icon_module; $block_params['icon'] = $icon; $block_params['title'] = $config->getSetting($params['title_property']); return $this->Application->ParseBlock($block_params); } /** * Allows to construct link for opening any type of catalog item selector. * * @param array $params Tag params. * * @return string */ function SelectorLink($params) { $template = $this->SelectParam($params, 't,template'); if ( !$template ) { $mode = 'catalog'; // {catalog, advanced_view} if ( isset($params['mode']) ) { $mode = $params['mode']; unset($params['mode']); } $params['t'] = 'catalog/item_selector/item_selector_' . $mode; } if ( !isset($params['m_cat_id']) ) { $params['m_cat_id'] = $this->Application->getBaseCategory(); } $default_params = array('pass' => 'all'); if ( isset($params['prefix']) && $params['prefix'] ) { $default_params['pass'] .= ',' . $params['prefix']; unset($params['prefix']); } $pass_through = $this->Application->GetVar('pass_through', ''); if ( isset($params['pass_through']) && $params['pass_through'] ) { $pass_through .= ',' . $params['pass_through']; } $pass_through = array_filter(explode(',', $pass_through)); // {yes, no} if ( isset($params['tabs_dependant']) ) { $params['td'] = $params['tabs_dependant']; $pass_through[] = 'td'; unset($params['tabs_dependant']); } // {single, multi} if ( isset($params['selection_mode']) ) { $params['tm'] = $params['selection_mode']; $pass_through[] = 'tm'; unset($params['selection_mode']); } // {all, none, <comma separated prefix list>} if ( isset($params['tab_prefixes']) ) { $params['tp'] = $params['tab_prefixes']; $pass_through[] = 'tp'; unset($params['tab_prefixes']); } if ( $pass_through ) { // Add pass_through to selector url if any. $params['pass_through'] = implode(',', array_unique($pass_through)); } // user can override default parameters (except pass_through of course) $params = array_merge($default_params, $params); return $this->Application->ProcessParsedTag('m', 'T', $params); } function TimeFrame($params) { $w = date('w'); $m = date('m'); $y = date('Y'); //FirstDayOfWeek is 0 for Sunday and 1 for Monday $fdow = $this->Application->ConfigValue('FirstDayOfWeek'); if ( $fdow && $w == 0 ) { $w = 7; } $today_start = mktime(0, 0, 0, date('m'), date('d'), $y); $first_day_of_this_week = $today_start - ($w - $fdow) * 86400; $first_day_of_this_month = mktime(0, 0, 0, $m, 1, $y); $this_quater = ceil($m / 3); $this_quater_start = mktime(0, 0, 0, $this_quater * 3 - 2, 1, $y); switch ( $params['type'] ) { case 'last_week_start': $timestamp = $first_day_of_this_week - 86400 * 7; break; case 'last_week_end': $timestamp = $first_day_of_this_week - 1; break; case 'last_month_start': $timestamp = $m == 1 ? mktime(0, 0, 0, 12, 1, $y - 1) : mktime(0, 0, 0, $m - 1, 1, $y); break; case 'last_month_end': $timestamp = $first_day_of_this_month = mktime(0, 0, 0, $m, 1, $y) - 1; break; case 'last_quater_start': $timestamp = $this_quater == 1 ? mktime(0, 0, 0, 10, 1, $y - 1) : mktime(0, 0, 0, ($this_quater - 1) * 3 - 2, 1, $y); break; case 'last_quater_end': $timestamp = $this_quater_start - 1; break; case 'last_6_months_start': $timestamp = $m <= 6 ? mktime(0, 0, 0, $m + 6, 1, $y - 1) : mktime(0, 0, 0, $m - 6, 1, $y); break; case 'last_year_start': $timestamp = mktime(0, 0, 0, 1, 1, $y - 1); break; case 'last_year_end': $timestamp = mktime(23, 59, 59, 12, 31, $y - 1); break; default: $timestamp = 0; break; } if ( isset($params['format']) ) { $format = $params['format']; if ( preg_match("/_regional_(.*)/", $format, $regs) ) { $lang = $this->Application->recallObject('lang.current'); /* @var $lang LanguagesItem */ $format = $lang->GetDBField($regs[1]); } return date($format, $timestamp); } return $timestamp; } /** * Redirect to cache rebuild template, when required by installator * * @param Array $params */ function CheckPermCache($params) { // we have separate session between install wizard and admin console, so store in cache $global_mark = $this->Application->getDBCache('ForcePermCacheUpdate'); $local_mark = $this->Application->RecallVar('PermCache_UpdateRequired'); if ( $global_mark || $local_mark ) { $this->Application->RemoveVar('PermCache_UpdateRequired'); $rebuild_mode = $this->Application->ConfigValue('CategoryPermissionRebuildMode'); if ( $rebuild_mode == CategoryPermissionRebuild::SILENT ) { $updater = $this->Application->makeClass('kPermCacheUpdater'); /* @var $updater kPermCacheUpdater */ $updater->OneStepRun(); $this->Application->HandleEvent(new kEvent('c:OnResetCMSMenuCache')); } elseif ( $rebuild_mode == CategoryPermissionRebuild::AUTOMATIC ) { // update with progress bar return true; } } return false; } /** * Checks if current protocol is SSL * * @param Array $params * @return int */ function IsSSL($params) { return (PROTOCOL == 'https://')? 1 : 0; } function PrintColumns($params) { $picker_helper = new kColumnPickerHelper( $this->Application->RecallVar('main_prefix'), $this->Application->GetLinkedVar('grid_name') ); $cols = $picker_helper->getData(); $this->Application->Phrases->AddCachedPhrase('__FREEZER__', '-------------'); $o = ''; if ( isset($params['hidden']) && $params['hidden'] ) { foreach ( $cols->hiddenFields as $column_name ) { $title = $this->Application->Phrase($cols->getTitle($column_name)); $o .= "<option value='$column_name'>" . $title; } } else { foreach ( $cols->allFields as $column_name ) { if ( $cols->isHidden($column_name) ) { continue; } $title = $this->Application->Phrase($cols->getTitle($column_name)); $o .= "<option value='$column_name'>" . $title; } } return $o; } /** * Allows to set popup size (key - current template name) * * @param Array $params * @return string * @access protected */ protected function SetPopupSize($params) { $width = $params['width']; $height = $params['height']; if ( $this->Application->GetVar('ajax') == 'yes' ) { // during AJAX request just output size die($width . 'x' . $height); } if ( !$this->UsePopups($params) ) { return; } $t = $this->Application->GetVar('t'); $sql = 'SELECT * FROM ' . TABLE_PREFIX . 'PopupSizes WHERE TemplateName = ' . $this->Conn->qstr($t); $popup_info = $this->Conn->GetRow($sql); if ( !$popup_info ) { // create new popup size record $fields_hash = Array ( 'TemplateName' => $t, 'PopupWidth' => $width, 'PopupHeight' => $height, ); $this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'PopupSizes'); } elseif ( $popup_info['PopupWidth'] != $width || $popup_info['PopupHeight'] != $height ) { // popup found and size in tag differs from one in db -> update in db $fields_hash = Array ( 'PopupWidth' => $width, 'PopupHeight' => $height, ); $this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'PopupSizes', 'PopupId = ' . $popup_info['PopupId']); } } /** * Allows to check if popups are generally enabled OR to check for "popup" or "modal" mode is enabled * * @param Array $params * @return bool */ function UsePopups($params) { if ($this->Application->GetVar('_force_popup')) { return true; } $use_popups = (int)$this->Application->ConfigValue('UsePopups'); if (array_key_exists('mode', $params)) { $mode_mapping = Array ('popup' => 1, 'modal' => 2); return $use_popups == $mode_mapping[ $params['mode'] ]; } return $use_popups; } function UseToolbarLabels($params) { return (int)$this->Application->ConfigValue('UseToolbarLabels'); } /** * Checks if debug mode enabled (optionally) and specified constant is on * * @param Array $params * @return bool * @todo Could be a duplicate of kMainTagProcessor::ConstOn */ function ConstOn($params) { $constant_name = $this->SelectParam($params, 'name,const'); $debug_mode = isset($params['debug_mode']) && $params['debug_mode'] ? $this->Application->isDebugMode() : true; return $debug_mode && kUtil::constOn($constant_name); } /** * Builds link to last template in main frame of admin * * @param Array $params * @return string */ function MainFrameLink($params) { $persistent = isset($params['persistent']) && $params['persistent']; if ($persistent && $this->Application->ConfigValue('RememberLastAdminTemplate')) { // check last_template in persistent session $last_template = $this->Application->RecallPersistentVar('last_template_popup'); } else { // check last_template in session $last_template = $this->Application->RecallVar('last_template_popup'); // because of m_opener=s there } if (!$last_template) { $params['persistent'] = 1; return $persistent ? false : $this->MainFrameLink($params); } list($index_file, $env) = explode('|', $last_template); $vars = $this->Application->processQueryString($env, 'pass'); $recursion_templates = Array ('login', 'index', 'no_permission'); if (isset($vars['admin']) && $vars['admin'] == 1) { // index template doesn't begin recursion on front-end (in admin frame) $vars['m_theme'] = ''; if (isset($params['m_opener']) && $params['m_opener'] == 'r') { // front-end link for highlighting purposes $vars['t'] = 'index'; $vars['m_cat_id'] = $this->Application->getBaseCategory(); } unset($recursion_templates[ array_search('index', $recursion_templates)]); } if (in_array($vars['t'], $recursion_templates)) { // prevents redirect recursion OR old in-portal pages $params['persistent'] = 1; return $persistent ? false : $this->MainFrameLink($params); } $vars = array_merge($vars, $params); $t = $vars['t']; unset($vars['t'], $vars['persistent']); // substitute language in link to current (link will work, even when language will be changed) $vars['m_lang'] = $this->Application->GetVar('m_lang'); return $this->Application->HREF($t, '', $vars, $index_file); } /** * Returns menu frame width or 200 in case, when invalid width specified in config * * @param Array $params * @return string */ function MenuFrameWidth($params) { - $width = (int)$this->Application->ConfigValue('MenuFrameWidth'); + $width = (int)$this->Application->RecallPersistentVar('MenuFrameWidth'); + + if ( $width <= 0 ) { + $width = (int)$this->Application->ConfigValue('MenuFrameWidth'); + } return $width > 0 ? $width : 200; } function AdminSkin($params) { $skin_helper = $this->Application->recallObject('SkinHelper'); /* @var $skin_helper SkinHelper */ return $skin_helper->AdminSkinTag($params); } /** * Prints errors, discovered during mass template compilation * * @param $params * @return string * @access protected */ protected function PrintCompileErrors($params) { $block_params = $this->prepareTagParams($params); $block_params['name'] = $params['render_as']; $errors = $this->Application->RecallVar('compile_errors'); if ( !$errors ) { return ''; } $ret = ''; $errors = unserialize($errors); $path_regexp = '/^' . preg_quote(FULL_PATH, '/') . '/'; foreach ($errors as $an_error) { $block_params = array_merge($block_params, $an_error); $block_params['file'] = preg_replace($path_regexp, '', $an_error['file'], 1); $ret .= $this->Application->ParseBlock($block_params); } $this->Application->RemoveVar('compile_errors'); return $ret; } function CompileErrorCount($params) { $errors = $this->Application->RecallVar('compile_errors'); if (!$errors) { return 0; } return count( unserialize($errors) ); } /** * Detects if given exception isn't one caused by tag error * * @param Array $params * @return string * @access protected */ protected function IsParserException($params) { return mb_strtolower($params['class']) == 'parserexception'; } function ExportData($params) { $export_helper = $this->Application->recallObject('CSVHelper'); /* @var $export_helper kCSVHelper */ $result = $export_helper->ExportData( $this->SelectParam($params, 'var,name,field') ); return ($result === false) ? '' : $result; } function ImportData($params) { $import_helper = $this->Application->recallObject('CSVHelper'); /* @var $import_helper kCSVHelper */ $result = $import_helper->ImportData( $this->SelectParam($params, 'var,name,field') ); return ($result === false) ? '' : $result; } function PrintCSVNotImportedLines($params) { $import_helper = $this->Application->recallObject('CSVHelper'); /* @var $import_helper kCSVHelper */ return $import_helper->GetNotImportedLines(); } /** * 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().'[0]['.$field.']'; // 0 always, as has no idfield if( getArrayValue($params, 'as_preg') ) $ret = preg_quote($ret, '/'); return $ret; } /** * Returns list of all backup file dates formatted * in passed block * * @param Array $params * @return string * @access public */ function PrintBackupDates($params) { $backup_helper = $this->Application->recallObject('BackupHelper'); /* @var $backup_helper BackupHelper */ $ret = ''; $dates = $backup_helper->getBackupFiles(); foreach ($dates as $date) { $params['backuptimestamp'] = $date['filedate']; $params['backuptime'] = date('F j, Y, g:i a', $date['filedate']); $params['backupsize'] = round($date['filesize'] / 1024 / 1024, 2); // MBytes $ret .= $this->Application->ParseBlock($params); } return $ret; } /** * Returns phpinfo() output * * @param Array $params * @return string */ function PrintPHPinfo($params) { ob_start(); phpinfo(); return ob_get_clean(); } function PrintSqlCols($params) { $ret = ''; $block = $params['render_as']; $a_data = unserialize($this->Application->GetVar('sql_rows')); $a_row = current($a_data); foreach ($a_row AS $col => $value) { $ret .= $this->Application->ParseBlock(Array ('name' => $block, 'value' => $col)); } return $ret; } function PrintSqlRows($params) { $ret = ''; $block = $params['render_as']; $a_data = unserialize($this->Application->GetVar('sql_rows')); foreach ($a_data as $a_row) { $cells = ''; foreach ($a_row as $value) { $cells .= '<td>' . kUtil::escape($value, kUtil::ESCAPE_HTML) . '</td>'; } $ret .= $this->Application->ParseBlock(Array ('name' => $block, 'cells' => $cells)); } return $ret; } /** * Prints available and enabled import sources using given block * * @param Array $params * @return string */ function PrintImportSources($params) { $sql = 'SELECT * FROM ' . TABLE_PREFIX . 'ImportScripts WHERE (Status = ' . STATUS_ACTIVE . ') AND (Type = "CSV")'; $import_sources = $this->Conn->Query($sql); $block_params = $this->prepareTagParams($params); $block_params['name'] = $params['render_as']; $ret = ''; foreach ($import_sources as $import_source) { $block_params['script_id'] = $import_source['ImportId']; $block_params['script_module'] = mb_strtolower($import_source['Module']); $block_params['script_name'] = $import_source['Name']; $block_params['script_prefix'] = $import_source['Prefix']; $block_params['module_path'] = $this->Application->findModule('Name', $import_source['Module'], 'Path'); $ret .= $this->Application->ParseBlock($block_params); } return $ret; } /** * Checks, that new window should be opened in "incs/close_popup" template instead of refreshing parent window * * @param Array $params * @return bool */ function OpenNewWindow($params) { if (!$this->UsePopups($params)) { return false; } $diff = array_key_exists('diff', $params) ? $params['diff'] : 0; $wid = $this->Application->GetVar('m_wid'); $stack_name = rtrim('opener_stack_' . $wid, '_'); $opener_stack = $this->Application->RecallVar($stack_name); $opener_stack = $opener_stack ? unserialize($opener_stack) : Array (); return count($opener_stack) >= 2 - $diff; } /** * Allows to dynamically change current language in template * * @param Array $params */ function SetLanguage($params) { $this->Application->SetVar('m_lang', $params['language_id']); $this->Application->Phrases->Init('phrases', '', $params['language_id']); } /** * Performs HTTP Authentification for administrative console * * @param Array $params * @return bool */ function HTTPAuth($params) { if ( !$this->Application->ConfigValue('UseHTTPAuth') ) { // http authentification not required return true; } $super_admin_ips = defined('SA_IP') ? SA_IP : false; $auth_bypass_ips = $this->Application->ConfigValue('HTTPAuthBypassIPs'); if ( ($auth_bypass_ips && kUtil::ipMatch($auth_bypass_ips)) || ($super_admin_ips && kUtil::ipMatch($super_admin_ips)) ) { // user ip is in ip bypass list return true; } if ( !array_key_exists('PHP_AUTH_USER', $_SERVER) ) { // ask user to authentificate, when not authentificated before return $this->_httpAuthentificate(); } else { // validate user credentials (browsers remembers user/password // and sends them each time page is visited, so no need to save // authentification result in session) if ( $this->Application->ConfigValue('HTTPAuthUsername') != $_SERVER['PHP_AUTH_USER'] ) { // incorrect username return $this->_httpAuthentificate(); } $password_formatter = $this->Application->recallObject('kPasswordFormatter'); /* @var $password_formatter kPasswordFormatter */ if ( !$password_formatter->checkPasswordFromSetting('HTTPAuthPassword', $_SERVER['PHP_AUTH_PW']) ) { // incorrect password return $this->_httpAuthentificate(); } } return true; } /** * Ask user to authentificate * * @return bool */ function _httpAuthentificate() { $realm = strip_tags( $this->Application->ConfigValue('Site_Name') ); header('WWW-Authenticate: Basic realm="' . $realm . '"'); header('HTTP/1.0 401 Unauthorized'); return false; } /** * Checks, that we are using memory cache * * @param Array $params * @return bool */ function MemoryCacheEnabled($params) { return $this->Application->isCachingType(CACHING_TYPE_MEMORY); } /** * Generates HTML for additional js and css files inclusion in accordance to selected editor language * * @param Array $params * @return string * @access protected */ protected function IncludeCodeMirrorFilesByLanguage($params) { $ret = ''; $language = $params['language']; $language_map = Array ( 'application/x-httpd-php' => array('htmlmixed.js', 'xml.js', 'javascript.js', 'css.js', 'clike.js', 'php.js'), 'text/css' => array('css.js'), 'text/html' => array('xml.js', 'javascript.js', 'css.js', 'htmlmixed.js'), 'text/x-sql' => array('sql.js'), 'text/x-mysql' => array('sql.js'), ); if ( !isset($language_map[$language]) ) { $language_map[$language] = Array ($language . '.js'); } foreach ($language_map[$language] as $filename) { $ret .= $this->_includeCodeMirrorFile($filename, $params); } return $ret; } /** * Generates code for one CodeMirror additional file inclusion * * @param string $filename * @param Array $params * @return string * @access protected * @throws InvalidArgumentException */ protected function _includeCodeMirrorFile($filename, $params) { static $included = Array (); $name = pathinfo($filename, PATHINFO_FILENAME); $extension = pathinfo($filename, PATHINFO_EXTENSION); if ( $extension != 'js' && $extension != 'css' ) { throw new InvalidArgumentException('Extension "' . $extension . '" not supported'); } if ( isset($included[$filename]) ) { return ''; } $included[$filename] = 1; $block_params = $this->prepareTagParams($params); $block_params['name'] = $params['render_as']; $block_params['resource_extension'] = $extension; $block_params['resource_file'] = $name . '/' . $filename; return $this->Application->ParseBlock($block_params); } } 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 16394) +++ branches/5.3.x/core/units/user_profile/user_profile_eh.php (revision 16395) @@ -1,93 +1,122 @@ <?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, $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(); } } } + + /** + * Adds virtual fields for "Display To Public" fields. + * + * @param kEvent $event Event. + * + * @return void + */ + protected function OnAfterConfigRead(kEvent $event) + { + parent::OnAfterConfigRead($event); + + $config = $event->getUnitConfig(); + $profile_mapping = $this->Application->getUnitConfig('u')->getUserProfileMapping(); + + $virtual_fields = $config->getVirtualFields(); + + foreach ( array_keys($profile_mapping) as $field_name ) { + $virtual_fields[$field_name] = array( + 'type' => 'int', + 'formatter' => 'kOptionsFormatter', 'options' => array(1 => 'la_Yes', 2 => 'la_No'), + 'use_phrases' => 1, + 'default' => 0, + ); + } + + $config->setVirtualFields($virtual_fields); + } + } Index: branches/5.3.x/core/units/user_profile/user_profile_tp.php =================================================================== --- branches/5.3.x/core/units/user_profile/user_profile_tp.php (revision 16394) +++ branches/5.3.x/core/units/user_profile/user_profile_tp.php (revision 16395) @@ -1,145 +1,161 @@ <?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 UserProfileTagProcessor extends kDBTagProcessor { - function Field($params) + /** + * Get's requested field value. + * + * @param array $params Tag params. + * + * @return string + */ + public function Field($params) { $field = $this->SelectParam($params, 'name,field'); $profile_mapping = $this->Application->getUnitConfig('u')->getUserProfileMapping(); $user_field = array_key_exists($field, $profile_mapping) ? $profile_mapping[$field] : false; - if (array_key_exists('profile_field', $params) && $params['profile_field']) { - // get variable from mapping - $params['name'] = $user_field; - $value = $this->Application->ProcessParsedTag('u.profile', 'Field', $params); - } - elseif ($user_field) { - // old style variable for displaying fields in public profile (named "pp_*") - $block_params = Array ('name' => 'DisplayToPublic', 'value' => $user_field); - $value = $this->Application->ProcessParsedTag($this->getUserPrefixSpecial(), 'Selected', $block_params); + // Get field value to show on "Public Profile" page. + if ( array_key_exists('profile_field', $params) && $params['profile_field'] ) { + if ( $user_field ) { + $params['name'] = $user_field; + } + + return $this->Application->ProcessParsedTag('u.profile', 'Field', $params); + } + + if ( $user_field ) { + // Determine if field should be shown on "Public Profile" page (always checkbox). + $block_params = array('name' => 'DisplayToPublic', 'value' => $user_field); + $selected = $this->Application->ProcessParsedTag( + $this->getUserPrefixSpecial(), + 'Selected', + $block_params + ); + $value = $selected ? 1 : 0; } else { - // get variable by name + // Custom user preference (can by field of any type) - fallback to persistent session storage. $value = $this->recallUserProfileVar($field); + $selected = $value == 1; } - if (isset($params['checked']) && $params['checked']) { - $checked_value = isset($params['value']) ? $params['value'] : 1; - $value = ($value == $checked_value) ? 'checked' : ''; + if ( isset($params['checked']) && $params['checked'] ) { + return $selected ? 'checked' : ''; } return $value; } /** * Returns prefix and special of user to operate with * * @return string */ function getUserPrefixSpecial() { return $this->Application->GetVar('user_id') ? 'u.profile' : 'u.current'; } /** * Allows to get persistent var from other user * * @param string $var_name * @return mixed */ function recallUserProfileVar($var_name) { static $cache = null; if (!isset($cache)) { $user = $this->Application->recallObject( $this->getUserPrefixSpecial() ); /* @var $user kDBItem */ $sql = 'SELECT VariableValue, VariableName FROM ' . TABLE_PREFIX . 'UserPersistentSessionData WHERE (PortalUserId = ' . $user->GetID() . ')'; $cache = $this->Conn->GetCol($sql, 'VariableName'); } if (array_key_exists($var_name, $cache)) { // get variable value from persistent session return $cache[$var_name]; } else { // not found in persistent session -> get default value from config variable with same name $config_value = $this->Application->ConfigValue($var_name); if ($config_value !== false) { return $config_value; } } return false; } /** * Returns visible field count in user profile * * @param Array $params * @return int */ function ProfileFieldCount($params) { static $field_count = null; if (!isset($field_count)) { $user = $this->Application->recallObject( $this->getUserPrefixSpecial() ); /* @var $user kDBItem */ $display_to_public = $user->GetDBField('DisplayToPublic'); $field_count = $display_to_public ? substr_count($display_to_public, '|') - 1 : 0; } return $field_count; } /** * Allows to detect that not all fields were shown * * @param Array $params * @return bool * @access protected */ protected function NotLastField($params) { $counter = (int)$this->Application->GetVar( $params['counter'] ); return $counter < $this->ProfileFieldCount($params); } /** * Because of persistent session table doesn't have ids, we use user id as id for each record * * @param Array $params * @return Array (id,field) * @access private */ function prepareInputName($params) { $params['force_id'] = $this->Application->RecallVar('user_id'); return parent::prepareInputName($params); } - } \ 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 16394) +++ branches/5.3.x/core/units/languages/languages_event_handler.php (revision 16395) @@ -1,823 +1,828 @@ <?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); } /** * Ensure, that current object is always taken from live table. * * @param kDBBase|kDBItem|kDBList $object Object. * @param kEvent $event Event. * * @return void */ protected function dbBuild(&$object, kEvent $event) { if ( $event->Special == 'current' ) { $event->setEventParam('live_table', true); } parent::dbBuild($object, $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(); } elseif ( $event->Special == 'current' ) { $language_id = $this->Application->GetVar('m_lang'); if ( !$language_id ) { $language_id = 'default'; } + else { + $event->setEventParam(kEvent::FLAG_ID_FROM_REQUEST, true); + } $this->Application->SetVar('m_lang', $language_id); $this->Application->SetVar($event->getPrefixSpecial() . '_id', $language_id); + + return $language_id; } 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); $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); $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); } } } } Index: branches/5.3.x/core/units/permissions/permissions_event_handler.php =================================================================== --- branches/5.3.x/core/units/permissions/permissions_event_handler.php (revision 16394) +++ branches/5.3.x/core/units/permissions/permissions_event_handler.php (revision 16395) @@ -1,266 +1,261 @@ <?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 PermissionsEventHandler extends kDBEventHandler { /** * Allows to override standard permission mapping * * @return void * @access protected * @see kEventHandler::$permMapping */ protected function mapPermissions() { parent::mapPermissions(); $permissions = Array ( 'OnGroupSavePermissions' => Array ('subitem' => 'advanced:manage_permissions'), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Save category permissions * * @param kEvent $event */ function OnCategorySavePermissions($event) { $group_id = $this->Application->GetVar('current_group_id'); $category_id = $this->Application->GetVar('c_id'); $permissions = $this->Application->GetVar($event->getPrefixSpecial(true)); if (isset($permissions[$group_id])) { $permissions = $permissions[$group_id]; $object = $event->getObject( Array('skip_autoload' => true) ); $permissions_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $permissions_helper kPermissionsHelper */ $permissions_helper->LoadPermissions($group_id, $category_id, 0, 'c'); // format: <perm_name>['inherited'] || <perm_name>['value'] $delete_ids = Array(); $create_sql = Array(); $update_sql = Array(); $create_mask = '(%s,%s,'.$group_id.',%s,0,'.$category_id.')'; $new_id = (int)$this->Conn->GetOne('SELECT MIN('.$object->IDField.') FROM '.$object->TableName); if($new_id > 0) $new_id = 0; --$new_id; foreach ($permissions as $perm_name => $perm_data) { $inherited = $perm_data['inherited']; $perm_value = isset($perm_data['value']) ? $perm_data['value'] : false; $perm_id = $permissions_helper->getPermissionID($perm_name); if ($inherited && ($perm_id != 0)) { // permission become inherited (+ direct value was set before) => DELETE $delete_ids[] = $permissions_helper->getPermissionID($perm_name); } if (!$inherited) { // not inherited if (($perm_id != 0) && ($perm_value != $permissions_helper->getPermissionValue($perm_name))) { // record was found in db & new value differs from old one => UPDATE $update_sql[$perm_id] = ' UPDATE '.$object->TableName.' SET PermissionValue = '.$perm_value.' WHERE (PermissionId = '.$perm_id.')'; } if ($perm_id == 0) { // not found in db, but set directly => INSERT $create_sql[] = sprintf($create_mask, $new_id--, $this->Conn->qstr($perm_name), $this->Conn->qstr($perm_value)); } } // permission state was not changed in all other cases } $this->UpdatePermissions($event, $create_sql, $update_sql, $delete_ids); } $event->MasterEvent->SetRedirectParam('item_prefix', $this->Application->GetVar('item_prefix')); $event->MasterEvent->SetRedirectParam('group_id', $this->Application->GetVar('group_id')); } /** * Saves permissions while editing group * * @param kEvent $event * * @return void * @access protected */ protected function OnGroupSavePermissions($event) { if ( !$this->Application->CheckPermission('in-portal:user_groups.advanced:manage_permissions', 1) ) { // no permission to save permissions return ; } $permissions = $this->Application->GetVar($event->getPrefixSpecial(true)); if ( !$permissions ) { return ; } $object = $event->getObject( Array ('skip_autoload' => true) ); /* @var $object kDBItem */ $group_id = $this->Application->GetVar('g_id'); $permissions_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $permissions_helper kPermissionsHelper */ $permissions_helper->LoadPermissions($group_id, 0, 1, 'g'); $delete_ids = $create_sql = Array (); $create_mask = '(%s,%s,' . $group_id . ',%s,1,0)'; $new_id = (int)$this->Conn->GetOne('SELECT MIN(' . $object->IDField . ') FROM ' . $object->TableName); if ( $new_id > 0 ) { $new_id = 0; } --$new_id; $sections_helper = $this->Application->recallObject('SectionsHelper'); /* @var $sections_helper kSectionsHelper */ foreach ($permissions as $section_name => $section_permissions) { - $section_data =& $sections_helper->getSectionData($section_name); - - if ( $section_data && isset($section_data['perm_prefix']) ) { - // using permission from other prefix - $section_name = $this->Application->getUnitConfig($section_data['perm_prefix'])->getPermSectionByName('main'); - } + $section_name = $sections_helper->getPermSection($section_name); foreach ($section_permissions as $perm_name => $perm_value) { if ( !$permissions_helper->isOldPermission($section_name, $perm_name) ) { $perm_name = $section_name . '.' . $perm_name; } $db_perm_value = $permissions_helper->getPermissionValue($perm_name); if ( $db_perm_value == 1 && $perm_value == 0 ) { // permission was disabled => delete it's record $delete_ids[] = $permissions_helper->getPermissionID($perm_name); } elseif ( $db_perm_value == 0 && $perm_value == 1 ) { // permission was enabled => created it's record $create_sql[$perm_name] = sprintf($create_mask, $new_id--, $this->Conn->qstr($perm_name), $this->Conn->qstr($perm_value)); } // permission state was not changed in all other cases } } $this->UpdatePermissions($event, $create_sql, Array (), $delete_ids); if ( $this->Application->GetVar('advanced_save') == 1 ) { // advanced permission popup [save button] $this->finalizePopup($event); // $event->redirect = 'incs/just_close'; } elseif ( $this->Application->GetVar('section_name') != '' ) { // save simple permissions before opening advanced permission popup $event->redirect = false; } } /** * Apply modification sqls to permissions table * * @param kEvent $event * @param Array $create_sql * @param Array $update_sql * @param Array $delete_ids */ function UpdatePermissions($event, $create_sql, $update_sql, $delete_ids) { $object = $event->getObject(); /* @var $object kDBItem */ if ($delete_ids) { $action = ChangeLog::DELETE; $object->Load($delete_ids[count($delete_ids) - 1]); $delete_sql = ' DELETE FROM '.$object->TableName.' WHERE '.$object->IDField.' IN ('.implode(',', $delete_ids).')'; $this->Conn->Query($delete_sql); } if ($create_sql) { $create_sql = ' INSERT INTO '.$object->TableName.' VALUES '.implode(',', $create_sql); $this->Conn->Query($create_sql); $sql = 'SELECT MIN(' . $object->IDField . ') FROM ' . $object->TableName; $id = $this->Conn->GetOne($sql); $action = ChangeLog::CREATE; $object->Load($id); } if ($update_sql) { foreach ($update_sql as $id => $sql) { $this->Conn->Query($sql); } $action = ChangeLog::UPDATE; $object->Load($id); $object->SetDBField('PermissionValue', $object->GetDBField('PermissionValue') ? 0 : 1); } if ($delete_ids || $create_sql || $update_sql) { $object->setModifiedFlag($action); if ($event->Name == 'OnCategorySavePermissions') { $this->Application->StoreVar('PermCache_UpdateRequired', 1); } } } /** * Don't delete permissions from live table in case of new category creation. * Called as much times as permission count for categories set, so don't * perform any sql queries here! * * @param kEvent $event * @return void * @access protected */ protected function OnBeforeDeleteFromLive(kEvent $event) { parent::OnBeforeDeleteFromLive($event); if ( $event->Prefix == 'c-perm' ) { // only when saving category permissions, not group permissions $foreign_keys = $event->getEventParam('foreign_key'); if ( (count($foreign_keys) == 1) && ($foreign_keys[0] == 0) ) { // parent item has zero id $temp_object = $this->Application->recallObject('c'); /* @var $temp_object CategoriesItem */ if ( $temp_object->isLoaded() ) { // category with id = 0 found in temp table $event->status = kEvent::erFAIL; } } } } -} \ No newline at end of file +} Index: branches/5.3.x/core/units/permissions/permissions_tag_processor.php =================================================================== --- branches/5.3.x/core/units/permissions/permissions_tag_processor.php (revision 16394) +++ branches/5.3.x/core/units/permissions/permissions_tag_processor.php (revision 16395) @@ -1,230 +1,225 @@ <?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 PermissionsTagProcessor extends kDBTagProcessor { function HasPermission($params) { $section_name = $params['section_name']; $sections_helper = $this->Application->recallObject('SectionsHelper'); /* @var $sections_helper kSectionsHelper */ $section_data =& $sections_helper->getSectionData($section_name); return array_search($params['perm_name'], $section_data['permissions']) !== false; } function HasAdvancedPermissions($params) { $section_name = $params['section_name']; $sections_helper = $this->Application->recallObject('SectionsHelper'); /* @var $sections_helper kSectionsHelper */ $section_data =& $sections_helper->getSectionData($section_name); $ret = false; foreach ($section_data['permissions'] as $perm_name) { if (preg_match('/^advanced:(.*)/', $perm_name)) { $ret = true; break; } } return $ret; } function PermissionValue($params) { $section_name = $params['section_name']; $perm_name = $params['perm_name']; $sections_helper = $this->Application->recallObject('SectionsHelper'); /* @var $sections_helper kSectionsHelper */ - $section_data =& $sections_helper->getSectionData($section_name); - - if ($section_data && isset($section_data['perm_prefix'])) { - // using permission from other prefix - $section_name = $this->Application->getUnitConfig($section_data['perm_prefix'])->getPermSectionByName('main'); - } + $section_name = $sections_helper->getPermSection($section_name); $permissions_helper = $this->Application->recallObject('PermissionsHelper'); /* @var $permissions_helper kPermissionsHelper */ if (!$permissions_helper->isOldPermission($section_name, $perm_name)) { $perm_name = $section_name.'.'.$perm_name; } return $permissions_helper->getPermissionValue($perm_name); } function LoadPermissions($params) { $permissions_helper = $this->Application->recallObject('PermissionsHelper'); $prefix_parts = explode('-', $this->Prefix, 2); /* @var $permissions_helper kPermissionsHelper */ $permissions_helper->LoadPermissions($this->Application->GetVar('g_id'), 0, 1, 'g'); } function LevelIndicator($params) { return $params['level'] * $params['multiply']; } function PrintPermissions($params) { $category = $this->Application->recallObject('c'); /* @var $category kDBItem */ $group_id = $this->Application->GetVar('group_id'); $prefix = $this->Application->GetVar('item_prefix'); $module = $this->Application->findModule('Var', $prefix, 'Name'); $perm_live_table = $this->Application->getUnitConfig('c-perm')->getTableName(); $perm_temp_table = $this->Application->GetTempName($perm_live_table, 'prefix:'.$this->Prefix); if ($category->GetID() == 0) { $categories = Array(0); } else { $categories = explode('|', substr($category->GetDBField('ParentPath'), 1, -1)); } if (count($categories) == 1 || $category->GetID() == 0) { // category located in root category ("Home") => then add it to path virtually array_unshift($categories, 0); } $this_cat = array_pop($categories); // get permission name + category position in parent path that has value set for that permission $case = 'MAX(CASE p.CatId'; foreach ($categories as $pos => $cat_id) { $case .= ' WHEN '.$cat_id.' THEN '.$pos; } $case .= ' END) AS InheritedPosition'; $sql = 'SELECT '.$case.', p.Permission AS Perm FROM '.$perm_live_table.' p LEFT JOIN '.TABLE_PREFIX.'CategoryPermissionsConfig pc ON pc.PermissionName = p.Permission WHERE p.CatId IN ('.implode(',', $categories).') AND pc.ModuleId = ' . $this->Conn->qstr($module) . ' AND ( (p.GroupId = ' . (int)$group_id . ' AND p.Type = 0) ) GROUP BY Perm'; $perm_positions = $this->Conn->GetCol($sql, 'Perm'); $pos_sql = ''; foreach ($perm_positions as $perm_name => $category_pos) { $pos_sql .= '(#TABLE_PREFIX#.Permission = "'.$perm_name.'" AND #TABLE_PREFIX#.CatId = '.$categories[$category_pos].') OR '; } $pos_sql = $pos_sql ? substr($pos_sql, 0, -4) : '0'; // get all permissions list with iheritence status, inherited category id and permission value $sql = 'SELECT pc.PermissionName, pc.Description, IF (tmp_p.PermissionValue IS NULL AND p.PermissionValue IS NULL, 0, IF (tmp_p.PermissionValue IS NOT NULL, tmp_p.PermissionValue, p.PermissionValue) ) AS Value, IF (tmp_p.CatId IS NOT NULL, tmp_p.CatId, IF(p.CatId IS NOT NULL, p.CatId, 0) ) AS InheritedFrom, IF(tmp_p.CatId = '.$category->GetID().', 0, 1) AS Inherited, IF(p.PermissionValue IS NOT NULL, p.PermissionValue, 0) AS InheritedValue FROM '.TABLE_PREFIX.'CategoryPermissionsConfig pc LEFT JOIN '.$perm_live_table.' p ON (p.Permission = pc.PermissionName) AND ('.str_replace('#TABLE_PREFIX#', 'p', $pos_sql).') AND (p.GroupId = '.(int)$group_id.') LEFT JOIN '.$perm_temp_table.' tmp_p ON (tmp_p.Permission = pc.PermissionName) AND (tmp_p.CatId = '.$this_cat.') AND (tmp_p.GroupId = '.$group_id.') WHERE ModuleId = "'.$module.'"'; $permissions = $this->Conn->Query($sql); $ret = ''; $block_params = $this->prepareTagParams($params); $block_params['name'] = $params['render_as']; foreach ($permissions as $perm_record) { $block_params = array_merge($block_params, $perm_record); $ret .= $this->Application->ParseBlock($block_params); } return $ret; } /** * Print module tab for each module * * @param Array $params * @return string */ function PrintTabs($params) { $ret = ''; $block_params = $params; foreach ($this->Application->ModuleInfo as $module_name => $module_data) { if (!$this->Application->prefixRegistred($module_data['Var']) || !$this->Application->getUnitConfig($module_data['Var'])->getCatalogItem()) continue; $params['item_prefix'] = $module_data['Var']; $ret .= $this->Application->IncludeTemplate($params); } return $ret; } /** * Returns category name by ID * * @param Array $params * @return string * @access protected */ protected function CategoryPath($params) { $category_id = $params['cat_id']; $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 cached if ( $category_id > 0 ) { $ml_formatter = $this->Application->recallObject('kMultiLanguage'); /* @var $ml_formatter kMultiLanguage */ $categories_config = $this->Application->getUnitConfig('c'); $sql = 'SELECT ' . $ml_formatter->LangFieldName('CachedNavbar') . ' FROM ' . $categories_config->getTableName() . ' WHERE ' . $categories_config->getIDField() . ' = ' . $category_id; $cached_navbar = preg_replace('/^Content(&\|&){0,1}/i', '', $this->Conn->GetOne($sql)); $category_path = trim($this->CategoryPath(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; } function PermInputName($params) { return $this->Prefix.'['.$this->Application->GetVar('group_id').']['.$this->Application->Parser->GetParam('PermissionName').']['.$params['sub_key'].']'; } - } \ No newline at end of file + } Index: branches/5.3.x/core/units/general/general_config.php =================================================================== --- branches/5.3.x/core/units/general/general_config.php (revision 16394) +++ branches/5.3.x/core/units/general/general_config.php (revision 16395) @@ -1,40 +1,41 @@ <?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 = new kUnitConfig('m', null, false); $config->setEventHandlerClass(Array ('class' => 'kEventHandler', 'file' => '', 'build_event' => 'OnBuild')); //$config->setTagProcessorClass(Array ('class' => 'kMainTagProcessor', 'file' => '', 'build_event' => 'OnBuild')); $config->setQueryString(Array ( 1 => 'cat_id', 2 => 'cat_page', 3 => 'lang', 4 => 'theme', 5 => 'opener', 6 => 'wid', )); $config->setTitleField('CachedNavbar'); $config->setTitlePhrase('la_Text_Category'); $config->setCatalogTabIcon('icon16_section.png'); $config->setItemType(1); $config->setTableName(TABLE_PREFIX . 'Categories'); $config->setCatalogItem(true); $config->setPortalStyleEnv(true); $config->setPermTabText('In-Portal'); +$config->setViewMenuPhrase('la_text_Categories'); $config->setPermSection(Array ('search' => 'in-portal:configuration_search', 'custom' => 'in-portal:configuration_custom')); Index: branches/5.3.x/core/ckeditor/plugins/my_link/plugin.js =================================================================== --- branches/5.3.x/core/ckeditor/plugins/my_link/plugin.js (revision 16394) +++ branches/5.3.x/core/ckeditor/plugins/my_link/plugin.js (revision 16395) @@ -1,706 +1,706 @@ ( function() { CKEDITOR.plugins.add('my_link'); CKEDITOR.on( 'dialogDefinition', function(ev) { var dialogName = ev.data.name; var dialogDefinition = ev.data.definition; var editor = this; if (dialogName == 'link') { var plugin = CKEDITOR.plugins.link; var $internal_page_mapping = {}; // mapping between @@PAGE_ID@@ and real url (for anchor loading) var load_page_anchors = function ($page_id, dialog) { var $url; var $tmp_iframe = $('#tmp_iframe'); var $internal_page_anchors = dialog.getContentElement( 'info', 'internalPageAnchors' ); var $current_anchor = $('#' + $internal_page_anchors.domId).data('anchor'); $internal_page_anchors.clear(); $internal_page_anchors.add( 'Loading ...', '' ); if ( $tmp_iframe.length == 0 ) { $tmp_iframe = $('<iframe width="0" height="0" id="tmp_iframe"></iframe>').appendTo('body'); $tmp_iframe.get(0).onload = function () { var $anchors = $('a[name]', $tmp_iframe.get(0).contentWindow.document); $internal_page_anchors.allowOnChange = false; $internal_page_anchors.clear(); $internal_page_anchors.add( $anchors.length > 0 ? '' : '(No anchors available in the document)', '' ); $anchors.each( function () { $internal_page_anchors.add( $(this).attr('name') ); } ); $internal_page_anchors.setValue($current_anchor); $internal_page_anchors.allowOnChange = true; } } $url = $internal_page_mapping[$page_id]; $url += ($url.indexOf('?') ? '&' : '?') + 'skip_last_template=1'; $tmp_iframe.attr('src', $url); } // Handles the event when the "Type" selection box is changed. var linkTypeChanged = function() { var dialog = this.getDialog(), partIds = [ 'internalOptions', 'urlOptions', 'anchorOptions', 'emailOptions' ], typeValue = this.getValue(), uploadTab = dialog.definition.getContents( 'upload' ), uploadInitiallyHidden = uploadTab && uploadTab.hidden; switch ( typeValue ) { case 'url': if ( editor.config.linkShowTargetTab ) dialog.showPage( 'target' ); if ( !uploadInitiallyHidden ) dialog.showPage( 'upload' ); break; case 'internal': if ( editor.config.linkShowTargetTab ) dialog.showPage( 'target' ); if ( !uploadInitiallyHidden ) dialog.hidePage( 'upload' ); // select internal page according to link url (on link type change) var $url = dialog.getContentElement('info', 'url').getValue(); if ( $internal_page_mapping[$url] !== undefined ) { dialog.getContentElement('info', 'internal_page').setValue($url); } break; default: dialog.hidePage( 'target' ); if ( !uploadInitiallyHidden ) dialog.hidePage( 'upload' ); break; } for ( var i = 0 ; i < partIds.length ; i++ ) { var element = dialog.getContentElement( 'info', partIds[i] ); if ( !element ) continue; element = element.getElement().getParent().getParent(); if ( partIds[i] == typeValue + 'Options' ) element.show(); else element.hide(); } dialog.layout(); }; var javascriptProtocolRegex = /^javascript:/, emailRegex = /^mailto:([^?]+)(?:\?(.+))?$/, emailSubjectRegex = /subject=([^;?:@&=$,\/]*)/, emailBodyRegex = /body=([^;?:@&=$,\/]*)/, anchorRegex = /^#(.*)$/, urlRegex = /^((?:http|https|ftp|news):\/\/)?(.*)$/, selectableTargets = /^(_(?:self|top|parent|blank))$/, encodedEmailLinkRegex = /^javascript:void\(location\.href='mailto:'\+String\.fromCharCode\(([^)]+)\)(?:\+'(.*)')?\)$/, functionCallProtectedEmailLinkRegex = /^javascript:([^(]+)\(([^)]+)\)$/, internalPageRegex = /^@@[\d]+@@(#.*$|$)/; // CUSTOM var popupRegex = /\s*window.open\(\s*this\.href\s*,\s*(?:'([^']*)'|null)\s*,\s*'([^']*)'\s*\)\s*;\s*return\s*false;*\s*/; var popupFeaturesRegex = /(?:^|,)([^=]+)=(\d+|yes|no)/gi; var parseLink = function( editor, element ) { var href = ( element && ( element.data( 'cke-saved-href' ) || element.getAttribute( 'href' ) ) ) || '', javascriptMatch, emailMatch, anchorMatch, urlMatch, retval = {}; if ( ( javascriptMatch = href.match( javascriptProtocolRegex ) ) ) { var emailProtection = editor.config.emailProtection || ''; if ( emailProtection == 'encode' ) { href = href.replace( encodedEmailLinkRegex, function ( match, protectedAddress, rest ) { return 'mailto:' + String.fromCharCode.apply( String, protectedAddress.split( ',' ) ) + ( rest && unescapeSingleQuote( rest ) ); }); } // Protected email link as function call. else if ( emailProtection ) { href.replace( functionCallProtectedEmailLinkRegex, function( match, funcName, funcArgs ) { if ( funcName == compiledProtectionFunction.name ) { retval.type = 'email'; var email = retval.email = {}; var paramRegex = /[^,\s]+/g, paramQuoteRegex = /(^')|('$)/g, paramsMatch = funcArgs.match( paramRegex ), paramsMatchLength = paramsMatch.length, paramName, paramVal; for ( var i = 0; i < paramsMatchLength; i++ ) { paramVal = decodeURIComponent( unescapeSingleQuote( paramsMatch[ i ].replace( paramQuoteRegex, '' ) ) ); paramName = compiledProtectionFunction.params[ i ].toLowerCase(); email[ paramName ] = paramVal; } email.address = [ email.name, email.domain ].join( '@' ); } } ); } } if ( !retval.type ) { if ( ( anchorMatch = href.match( anchorRegex ) ) ) { retval.type = 'anchor'; retval.anchor = {}; retval.anchor.name = retval.anchor.id = anchorMatch[1]; } // Protected email link as encoded string. else if ( ( emailMatch = href.match( emailRegex ) ) ) { var subjectMatch = href.match( emailSubjectRegex ), bodyMatch = href.match( emailBodyRegex ); retval.type = 'email'; var email = ( retval.email = {} ); email.address = emailMatch[ 1 ]; subjectMatch && ( email.subject = decodeURIComponent( subjectMatch[ 1 ] ) ); bodyMatch && ( email.body = decodeURIComponent( bodyMatch[ 1 ] ) ); } // CUSTOM: begin else if ( href && href.match( internalPageRegex ) ) { retval.type = 'internal'; retval.url = {}; retval.url.protocol = ''; retval.anchor = {}; if ( href.match(/^(.*)#(.*)$/) ) { retval.url.url = RegExp.$1; retval.internal_anchor = RegExp.$2; } else { retval.url.url = href; retval.internal_anchor = ''; } } // CUSTOM: end // urlRegex matches empty strings, so need to check for href as well. else if ( href && ( urlMatch = href.match( urlRegex ) ) ) { retval.type = 'url'; retval.url = {}; retval.url.protocol = urlMatch[1]; retval.url.url = urlMatch[2]; } // CUSTOM: begin else { retval.type = 'internal'; retval.url = {}; retval.url.protocol = ''; retval.url.url = ''; retval.internal_anchor = ''; } // CUSTOM: end } // Load target and popup settings. if ( element ) { var target = element.getAttribute( 'target' ); retval.target = {}; retval.adv = {}; // IE BUG: target attribute is an empty string instead of null in IE if it's not set. if ( !target ) { var onclick = element.data( 'cke-pa-onclick' ) || element.getAttribute( 'onclick' ), onclickMatch = onclick && onclick.match( popupRegex ); if ( onclickMatch ) { retval.target.type = 'popup'; retval.target.name = onclickMatch[1]; var featureMatch; while ( ( featureMatch = popupFeaturesRegex.exec( onclickMatch[2] ) ) ) { // Some values should remain numbers (#7300) if ( ( featureMatch[2] == 'yes' || featureMatch[2] == '1' ) && !( featureMatch[1] in { height:1, width:1, top:1, left:1 } ) ) retval.target[ featureMatch[1] ] = true; else if ( isFinite( featureMatch[2] ) ) retval.target[ featureMatch[1] ] = featureMatch[2]; } } } else { var targetMatch = target.match( selectableTargets ); if ( targetMatch ) retval.target.type = retval.target.name = target; else { retval.target.type = 'frame'; retval.target.name = target; } } var me = this; var advAttr = function( inputName, attrName ) { var value = element.getAttribute( attrName ); if ( value !== null ) retval.adv[ inputName ] = value || ''; }; advAttr( 'advId', 'id' ); advAttr( 'advLangDir', 'dir' ); advAttr( 'advAccessKey', 'accessKey' ); retval.adv.advName = element.data( 'cke-saved-name' ) || element.getAttribute( 'name' ) || ''; advAttr( 'advLangCode', 'lang' ); advAttr( 'advTabIndex', 'tabindex' ); advAttr( 'advTitle', 'title' ); advAttr( 'advContentType', 'type' ); advAttr( 'advCSSClasses', 'class' ); advAttr( 'advCharset', 'charset' ); advAttr( 'advStyles', 'style' ); advAttr( 'advRel', 'rel' ); } // Find out whether we have any anchors in the editor. // Get all IMG elements in CK document. var elements = editor.document.getElementsByTag( 'img' ), realAnchors = new CKEDITOR.dom.nodeList( editor.document.$.anchors ), anchors = retval.anchors = []; for ( var i = 0; i < elements.count() ; i++ ) { var item = elements.getItem( i ); if ( item.data( 'cke-realelement' ) && item.data( 'cke-real-element-type' ) == 'anchor' ) anchors.push( editor.restoreRealElement( item ) ); } for ( i = 0 ; i < realAnchors.count() ; i++ ) anchors.push( realAnchors.getItem( i ) ); for ( i = 0 ; i < anchors.length ; i++ ) { item = anchors[ i ]; anchors[ i ] = { name : item.getAttribute( 'name' ), id : item.getAttribute( 'id' ) }; } // Record down the selected element in the dialog. this._.selectedElement = element; return retval; }; var infoTab = dialogDefinition.getContents( 'info' ); var linkType = infoTab.get('linkType'); linkType['items'].unshift( ['Internal Web Page', 'internal'] ); linkType['default'] = 'internal'; linkType['onChange'] = linkTypeChanged; infoTab.add( { type : 'vbox', id : 'internalOptions', children : [ { type : 'select', id : 'internal_page', label : 'Internal Page Name', items : [ [ '' ] ], onChange: function ( ) { // place internal page ID from this dropdown into URL field of the dialog var dialog = this.getDialog(); this.allowOnChange = false; dialog.getContentElement('info', 'url').setValue( this.getValue() ); this.allowOnChange = true; load_page_anchors( this.getValue(), dialog ); }, setup : function( data ) { var $me = this; - var $link_url = data.url.url; + var $link_url = data.url ? data.url.url : ''; var $ajax_url = CKEDITOR.basePath.replace( /core.*$/, 'admin/index.php?events[fck]=OnLoadCmsTree' ); var $internal_page_anchors = this.getDialog().getContentElement( 'info', 'internalPageAnchors' ); $('#' + $internal_page_anchors.domId).data('anchor', data.internal_anchor); $.ajax( { type: 'GET', url: $ajax_url, async: false, success: function ($xml) { $me.allowOnChange = false; $me.clear(); $me.add( '' ); $('CmsPage', $xml).each( function ($i) { var $node = $(this); var cms_id = $node.attr('st_id'); var cms_page = $node.attr('path'); var real_url = $node.attr('real_url'); $internal_page_mapping[cms_page] = real_url; $me.add( $node.attr('title').replace(/&/g, '&') , cms_page ); if ( $link_url && $link_url.match(cms_page + '$') ) { $me.setValue(cms_page); load_page_anchors( cms_page, $me.getDialog() ); } } ); // undo CKEditor default "htmlspecialchars" on all HTML $('option', '#' + $me.domId).each( function ($index) { var $option = $(this), $level_indicator = '—', $html = $option.html().replace(/&/g, '&'), $level = ($html.length - $html.replace(new RegExp($level_indicator, 'g'), '').length ) / $level_indicator.length; if ( $html.match(/^(.*?) (.*?) (\(.*?\))$/) ) { // "—— rss (news/rss.html)" $level_indicator = RegExp.$1; var $page_name = RegExp.$2, $page_url = RegExp.$3; $html = $level_indicator + ' ' + $page_name + ' ' + $page_url; } $option.html($html); } ); $me.allowOnChange = true; $me.getDialog().layout(); }, dataType: 'xml' } ); var linkType = this.getDialog().getContentElement( 'info', 'linkType' ); if ( linkType && linkType.getValue() == 'internal' ) { this.focus(); } }, }, { type : 'select', id : 'internalPageAnchors', label : 'Select an Anchor', items : [ [ '' ] ], commit : function( data ) { data.internal_anchor = this.getValue(); this.allowOnChange = false; } } ] } ); dialogDefinition['onShow'] = function() { this.fakeObj = false; var editor = this.getParentEditor(), selection = editor.getSelection(), element = null; // Fill in all the relevant fields if there's already one link selected. if ( ( element = plugin.getSelectedLink( editor ) ) && element.hasAttribute( 'href' ) ) selection.selectElement( element ); else if ( ( element = selection.getSelectedElement() ) && element.is( 'img' ) && element.data( 'cke-real-element-type' ) && element.data( 'cke-real-element-type' ) == 'anchor' ) { this.fakeObj = element; element = editor.restoreRealElement( this.fakeObj ); selection.selectElement( this.fakeObj ); } else element = null; this.setupContent( parseLink.apply( this, [ editor, element ] ) ); }; dialogDefinition['onOk'] = function() { var attributes = {}, removeAttributes = [], data = {}, me = this, editor = this.getParentEditor(); this.commitContent( data ); // Compose the URL. switch ( data.type || 'internal' ) { // CUSTOM: begin case 'internal': var url = ( data.url && data.url.url ) || ''; var anchor = data.internal_anchor || ''; attributes[ 'data-cke-saved-href' ] = url; if ( anchor ) { attributes[ 'data-cke-saved-href' ] += '#' + anchor; } break; // CUSTOM: end case 'url': var protocol = ( data.url && data.url.protocol != undefined ) ? data.url.protocol : 'http://', url = ( data.url && data.url.url ) || ''; attributes[ 'data-cke-saved-href' ] = ( url.indexOf( '/' ) === 0 ) ? url : protocol + url; break; case 'anchor': var name = ( data.anchor && data.anchor.name ), id = ( data.anchor && data.anchor.id ); attributes[ 'data-cke-saved-href' ] = '#' + ( name || id || '' ); break; case 'email': var linkHref, email = data.email, address = email.address, emailProtection = editor.config.emailProtection || ''; switch( emailProtection ) { case '' : case 'encode' : { var subject = encodeURIComponent( email.subject || '' ), body = encodeURIComponent( email.body || '' ); // Build the e-mail parameters first. var argList = []; subject && argList.push( 'subject=' + subject ); body && argList.push( 'body=' + body ); argList = argList.length ? '?' + argList.join( '&' ) : ''; if ( emailProtection == 'encode' ) { linkHref = [ 'javascript:void(location.href=\'mailto:\'+', protectEmailAddressAsEncodedString( address ) ]; // parameters are optional. argList && linkHref.push( '+\'', escapeSingleQuote( argList ), '\'' ); linkHref.push( ')' ); } else linkHref = [ 'mailto:', address, argList ]; break; } default : { // Separating name and domain. var nameAndDomain = address.split( '@', 2 ); email.name = nameAndDomain[ 0 ]; email.domain = nameAndDomain[ 1 ]; linkHref = [ 'javascript:', protectEmailLinkAsFunction( email ) ]; } } attributes[ 'data-cke-saved-href' ] = linkHref.join( '' ); break; } // Popups and target. if ( data.target ) { if ( data.target.type == 'popup' ) { var onclickList = [ 'window.open(this.href, \'', data.target.name || '', '\', \'' ]; var featureList = [ 'resizable', 'status', 'location', 'toolbar', 'menubar', 'fullscreen', 'scrollbars', 'dependent' ]; var featureLength = featureList.length; var addFeature = function( featureName ) { if ( data.target[ featureName ] ) featureList.push( featureName + '=' + data.target[ featureName ] ); }; for ( var i = 0 ; i < featureLength ; i++ ) featureList[i] = featureList[i] + ( data.target[ featureList[i] ] ? '=yes' : '=no' ) ; addFeature( 'width' ); addFeature( 'left' ); addFeature( 'height' ); addFeature( 'top' ); onclickList.push( featureList.join( ',' ), '\'); return false;' ); attributes[ 'data-cke-pa-onclick' ] = onclickList.join( '' ); // Add the "target" attribute. (#5074) removeAttributes.push( 'target' ); } else { if ( data.target.type != 'notSet' && data.target.name ) attributes.target = data.target.name; else removeAttributes.push( 'target' ); removeAttributes.push( 'data-cke-pa-onclick', 'onclick' ); } } // Advanced attributes. if ( data.adv ) { var advAttr = function( inputName, attrName ) { var value = data.adv[ inputName ]; if ( value ) attributes[attrName] = value; else removeAttributes.push( attrName ); }; advAttr( 'advId', 'id' ); advAttr( 'advLangDir', 'dir' ); advAttr( 'advAccessKey', 'accessKey' ); if ( data.adv[ 'advName' ] ) { attributes[ 'name' ] = attributes[ 'data-cke-saved-name' ] = data.adv[ 'advName' ]; attributes[ 'class' ] = ( attributes[ 'class' ] ? attributes[ 'class' ] + ' ' : '' ) + 'cke_anchor'; } else removeAttributes = removeAttributes.concat( [ 'data-cke-saved-name', 'name' ] ); advAttr( 'advLangCode', 'lang' ); advAttr( 'advTabIndex', 'tabindex' ); advAttr( 'advTitle', 'title' ); advAttr( 'advContentType', 'type' ); advAttr( 'advCSSClasses', 'class' ); advAttr( 'advCharset', 'charset' ); advAttr( 'advStyles', 'style' ); advAttr( 'advRel', 'rel' ); } // Browser need the "href" fro copy/paste link to work. (#6641) attributes.href = attributes[ 'data-cke-saved-href' ]; if ( !this._.selectedElement ) { // Create element if current selection is collapsed. var selection = editor.getSelection(), ranges = selection.getRanges( true ); if ( ranges.length == 1 && ranges[0].collapsed ) { // Short mailto link text view (#5736). var text = new CKEDITOR.dom.text( data.type == 'email' ? data.email.address : attributes[ 'data-cke-saved-href' ], editor.document ); ranges[0].insertNode( text ); ranges[0].selectNodeContents( text ); selection.selectRanges( ranges ); } // Apply style. var style = new CKEDITOR.style( { element : 'a', attributes : attributes } ); style.type = CKEDITOR.STYLE_INLINE; // need to override... dunno why. style.apply( editor.document ); } else { // We're only editing an existing link, so just overwrite the attributes. var element = this._.selectedElement, href = element.data( 'cke-saved-href' ), textView = element.getHtml(); // IE BUG: Setting the name attribute to an existing link doesn't work. // Must re-create the link from weired syntax to workaround. if ( CKEDITOR.env.ie && !( CKEDITOR.document.$.documentMode >= 8 ) && attributes.name != element.getAttribute( 'name' ) ) { var newElement = new CKEDITOR.dom.element( '<a name="' + CKEDITOR.tools.htmlEncode( attributes.name ) + '">', editor.document ); selection = editor.getSelection(); element.copyAttributes( newElement, { name : 1 } ); element.moveChildren( newElement ); newElement.replace( element ); element = newElement; selection.selectElement( element ); } element.setAttributes( attributes ); element.removeAttributes( removeAttributes ); // Update text view when user changes protocol (#4612). if ( href == textView || data.type == 'email' && textView.indexOf( '@' ) != -1 ) { // Short mailto link text view (#5736). element.setHtml( data.type == 'email' ? data.email.address : attributes[ 'data-cke-saved-href' ] ); } // Make the element display as an anchor if a name has been set. if ( element.getAttribute( 'name' ) ) element.addClass( 'cke_anchor' ); else element.removeClass( 'cke_anchor' ); if ( this.fakeObj ) editor.createFakeElement( element, 'cke_anchor', 'anchor' ).replace( this.fakeObj ); delete this._.selectedElement; } }; } } ); } )(); Index: branches/5.3.x/core/ckeditor/ckfinder/config.php =================================================================== --- branches/5.3.x/core/ckeditor/ckfinder/config.php (revision 16394) +++ branches/5.3.x/core/ckeditor/ckfinder/config.php (revision 16395) @@ -1,375 +1,375 @@ <?php /* * ### CKFinder : Configuration File - Basic Instructions * * In a generic usage case, the following tasks must be done to configure * CKFinder: * 1. Check the $baseUrl and $baseDir variables; * 2. If available, paste your license key in the "LicenseKey" setting; * 3. Create the CheckAuthentication() function that enables CKFinder for authenticated users; * * Other settings may be left with their default values, or used to control * advanced features of CKFinder. */ define('ADMIN', 1); define('DBG_SKIP_REPORTING', 1); define('FULL_PATH', realpath( dirname(__FILE__) . '/../../..' ) ); define('REL_PATH', 'core/ckeditor/ckfinder/core/connector/php'); include_once(FULL_PATH . '/core/kernel/startup.php'); /** * This function must check the user session to be sure that he/she is * authorized to upload and access files in the File Browser. * * @return boolean */ function CheckAuthentication() { // WARNING : DO NOT simply return "true". By doing so, you are allowing // "anyone" to upload and list the files in your server. You must implement // some kind of session validation here. Even something very simple as... // return isset($_SESSION['IsAuthorized']) && $_SESSION['IsAuthorized']; // ... where $_SESSION['IsAuthorized'] is set to "true" as soon as the // user logs in your system. To be able to use session variables don't // forget to add session_start() at the top of this file. global $config; // TODO: show all folders all the time foreach ($config['ResourceType'] as $resource_type) { if ( !file_exists($resource_type['directory']) ) { mkdir($resource_type['directory'], $config['ChmodFolders']); } } $application =& kApplication::Instance(); $application->Init(); $config['LicenseName'] = $application->ConfigValue('CKFinderLicenseName'); $config['LicenseKey'] = $application->ConfigValue('CKFinderLicenseKey'); return $application->isAdminUser; } // LicenseKey : Paste your license key here. If left blank, CKFinder will be // fully functional, in demo mode. $config['LicenseName'] = ''; $config['LicenseKey'] = ''; /* Uncomment lines below to enable PHP error reporting and displaying PHP errors. Do not do this on a production server. Might be helpful when debugging why CKFinder does not work as expected. */ // error_reporting(E_ALL); // ini_set('display_errors', 1); /* To make it easy to configure CKFinder, the $baseUrl and $baseDir can be used. Those are helper variables used later in this config file. */ /* $baseUrl : the base path used to build the final URL for the resources handled in CKFinder. If empty, the default value (/userfiles/) is used. Examples: $baseUrl = 'http://example.com/ckfinder/files/'; $baseUrl = '/userfiles/'; ATTENTION: The trailing slash is required. */ $baseUrl = BASE_PATH . WRITEBALE_BASE . '/user_files/'; /* $baseDir : the path to the local directory (in the server) which points to the above $baseUrl URL. This is the path used by CKFinder to handle the files in the server. Full write permissions must be granted to this directory. Examples: // You may point it to a directory directly: $baseDir = '/home/login/public_html/ckfinder/files/'; $baseDir = 'C:/SiteDir/CKFinder/userfiles/'; // Or you may let CKFinder discover the path, based on $baseUrl. // WARNING: resolveUrl() *will not work* if $baseUrl does not start with a slash ("/"), // for example if $baseDir is set to http://example.com/ckfinder/files/ $baseDir = resolveUrl($baseUrl); ATTENTION: The trailing slash is required. */ $baseDir = resolveUrl($baseUrl); /* * ### Advanced Settings */ /* Thumbnails : thumbnails settings. All thumbnails will end up in the same directory, no matter the resource type. */ $config['Thumbnails'] = Array( 'url' => $baseUrl . '_thumbs', 'directory' => $baseDir . '_thumbs', 'enabled' => true, 'directAccess' => false, 'maxWidth' => 100, 'maxHeight' => 100, 'bmpSupported' => false, 'quality' => 80); /* Set the maximum size of uploaded images. If an uploaded image is larger, it gets scaled down proportionally. Set to 0 to disable this feature. */ $config['Images'] = Array( - 'maxWidth' => 1600, - 'maxHeight' => 1200, - 'quality' => 80); + 'maxWidth' => 0, + 'maxHeight' => 0, + 'quality' => 0); /* RoleSessionVar : the session variable name that CKFinder must use to retrieve the "role" of the current user. The "role", can be used in the "AccessControl" settings (bellow in this page). To be able to use this feature, you must initialize the session data by uncommenting the following "session_start()" call. */ $config['RoleSessionVar'] = 'CKFinder_UserRole'; //session_start(); /* AccessControl : used to restrict access or features to specific folders. Many "AccessControl" entries can be added. All attributes are optional. Subfolders inherit their default settings from their parents' definitions. - The "role" attribute accepts the special '*' value, which means "everybody". - The "resourceType" attribute accepts the special value '*', which means "all resource types". */ $config['AccessControl'][] = Array( 'role' => '*', 'resourceType' => '*', 'folder' => '/', 'folderView' => true, 'folderCreate' => true, 'folderRename' => true, 'folderDelete' => true, 'fileView' => true, 'fileUpload' => true, 'fileRename' => true, 'fileDelete' => true); /* For example, if you want to restrict the upload, rename or delete of files in the "Logos" folder of the resource type "Images", you may uncomment the following definition, leaving the above one: $config['AccessControl'][] = Array( 'role' => '*', 'resourceType' => 'Images', 'folder' => '/Logos', 'folderView' => true, 'folderCreate' => true, 'folderRename' => true, 'folderDelete' => true, 'fileView' => true, 'fileUpload' => false, 'fileRename' => false, 'fileDelete' => false); */ /* ResourceType : defines the "resource types" handled in CKFinder. A resource type is nothing more than a way to group files under different paths, each one having different configuration settings. Each resource type name must be unique. When loading CKFinder, the "type" querystring parameter can be used to display a specific type only. If "type" is omitted in the URL, the "DefaultResourceTypes" settings is used (may contain the resource type names separated by a comma). If left empty, all types are loaded. maxSize is defined in bytes, but shorthand notation may be also used. Available options are: G, M, K (case insensitive). 1M equals 1048576 bytes (one Megabyte), 1K equals 1024 bytes (one Kilobyte), 1G equals one Gigabyte. Example: 'maxSize' => "8M", ============================================================================== ATTENTION: Flash files with `swf' extension, just like HTML files, can be used to execute JavaScript code and to e.g. perform an XSS attack. Grant permission to upload `.swf` files only if you understand and can accept this risk. ============================================================================== */ $config['DefaultResourceTypes'] = ''; // 'Files,Images,Flash,Media,Documents'; $config['ResourceType'][] = Array( 'name' => 'Files', // Single quotes not allowed 'url' => $baseUrl . 'Files', 'directory' => $baseDir . 'Files', 'maxSize' => 0, 'allowedExtensions' => 'jpg,gif,jpeg,png,swf,fla,jpg,gif,jpeg,png,avi,mpg,mpeg,zip,rar,arj,gz,tar,doc,pdf,ppt,rdp,swf,swt,txt,vsd,xls,csv,odt', 'deniedExtensions' => 'php,asp,aspx,ascx,jsp,cfm,cfc,pl,bat,exe,dll,reg'); $config['ResourceType'][] = Array( 'name' => 'Images', 'url' => $baseUrl . 'Images', 'directory' => $baseDir . 'Images', 'maxSize' => "16M", 'allowedExtensions' => 'jpg,gif,jpeg,png,bmp', 'deniedExtensions' => 'php,asp,aspx,ascx,jsp,cfm,cfc,pl,bat,exe,dll,reg'); $config['ResourceType'][] = Array( 'name' => 'Flash', 'url' => $baseUrl . 'Flash', 'directory' => $baseDir . 'Flash', 'maxSize' => 0, 'allowedExtensions' => 'swf,flv', 'deniedExtensions' => 'php,asp,aspx,ascx,jsp,cfm,cfc,pl,bat,exe,dll,reg'); $config['ResourceType'][] = Array( 'name' => 'Media', 'url' => $baseUrl . 'Media', 'directory' => $baseDir . 'Media', 'maxSize' => 0, 'allowedExtensions' => 'asf,asx,avi,wav,wax,wma,wm,wmv,m3u,mp2v,mpg,mpeg,m1v,mp2,mp3,mpa,mpe,mpv2,mp4,mid,midi,rmi,qt,aif,aifc,aiff,mov,flv,rm,svcd,swf,vcd', 'deniedExtensions' => 'php,asp,aspx,ascx,jsp,cfm,cfc,pl,bat,exe,dll,reg'); $config['ResourceType'][] = Array( 'name' => 'Documents', 'url' => $baseUrl . 'Documents', 'directory' => $baseDir . 'Documents', 'maxSize' => 0, 'allowedExtensions' => 'doc,pdf,ppt,rdp,swf,swt,txt,vsd,xls,csv,zip,odt', 'deniedExtensions' => 'php,asp,aspx,ascx,jsp,cfm,cfc,pl,bat,exe,dll,reg'); /* Due to security issues with Apache modules, it is recommended to leave the following setting enabled. How does it work? Suppose the following: - If "php" is on the denied extensions list, a file named foo.php cannot be uploaded. - If "rar" (or any other) extension is allowed, one can upload a file named foo.rar. - The file foo.php.rar has "rar" extension so, in theory, it can be also uploaded. In some conditions Apache can treat the foo.php.rar file just like any PHP script and execute it. If CheckDoubleExtension is enabled, each part of the file name after a dot is checked, not only the last part. In this way, uploading foo.php.rar would be denied, because "php" is on the denied extensions list. */ $config['CheckDoubleExtension'] = true; /* Increases the security on an IIS web server. If enabled, CKFinder will disallow creating folders and uploading files whose names contain characters that are not safe under an IIS web server. */ $config['DisallowUnsafeCharacters'] = false; /* If you have iconv enabled (visit http://php.net/iconv for more information), you can use this directive to specify the encoding of file names in your system. Acceptable values can be found at: http://www.gnu.org/software/libiconv/ Examples: $config['FilesystemEncoding'] = 'CP1250'; $config['FilesystemEncoding'] = 'ISO-8859-2'; */ $config['FilesystemEncoding'] = 'UTF-8'; /* Perform additional checks for image files if set to true, validate image size */ $config['SecureImageUploads'] = true; /* Indicates that the file size (maxSize) for images must be checked only after scaling them. Otherwise, it is checked right after uploading. */ $config['CheckSizeAfterScaling'] = true; /* For security, HTML is allowed in the first Kb of data for files having the following extensions only. */ $config['HtmlExtensions'] = array('html', 'htm', 'xml', 'xsd', 'txt', 'js'); /* Folders to not display in CKFinder, no matter their location. No paths are accepted, only the folder name. The * and ? wildcards are accepted. ".*" disallows the creation of folders starting with a dot character. */ $config['HideFolders'] = Array('.*', 'icons', '.svn', 'CVS'); /* Files to not display in CKFinder, no matter their location. No paths are accepted, only the file name, including extension. The * and ? wildcards are accepted. */ $config['HideFiles'] = Array(".*"); /* After file is uploaded, sometimes it is required to change its permissions so that it was possible to access it at the later time. If possible, it is recommended to set more restrictive permissions, like 0755. Set to 0 to disable this feature. Note: not needed on Windows-based servers. */ $config['ChmodFiles'] = 0777 ; /* See comments above. Used when creating folders that does not exist. */ $config['ChmodFolders'] = 0777 ; /* Force ASCII names for files and folders. If enabled, characters with diactric marks, like å, ä, ö, ć, č, đ, š will be automatically converted to ASCII letters. */ $config['ForceAscii'] = false; /* Send files using X-Sendfile module Mod X-Sendfile (or similar) is avalible on Apache2, Nginx, Cherokee, Lighttpd Enabling X-Sendfile option can potentially cause security issue. - server path to the file may be send to the browser with X-Sendfile header - if server is not configured properly files will be send with 0 length For more complex configuration options visit our Developer's Guide http://docs.cksource.com/CKFinder_2.x/Developers_Guide/PHP */ $config['XSendfile'] = false; include_once "plugins/imageresize/plugin.php"; include_once "plugins/fileeditor/plugin.php"; include_once "plugins/zip/plugin.php"; $config['plugin_imageresize']['smallThumb'] = '90x90'; $config['plugin_imageresize']['mediumThumb'] = '120x120'; $config['plugin_imageresize']['largeThumb'] = '180x180'; Index: branches/5.3.x/core/admin_templates/categories/categories_edit_permissions.tpl =================================================================== --- branches/5.3.x/core/admin_templates/categories/categories_edit_permissions.tpl (revision 16394) +++ branches/5.3.x/core/admin_templates/categories/categories_edit_permissions.tpl (revision 16395) @@ -1,219 +1,219 @@ <inp2:adm_SetPopupSize width="880" height="680"/> <inp2:m_include t="incs/header"/> <inp2:m_RenderElement name="combined_header" prefix="c" section="in-portal:browse" title_preset="categories_permissions" tab_preset="Default"/> <inp2:m_include template="catalog/catalog_elements"/> <inp2:m_include t="categories/categories_tabs"/> <!-- ToolBar --> <table class="toolbar" height="30" cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <script type="text/javascript" src="js/ajax.js"></script> <script type="text/javascript" src="js/catalog.js"></script> <script type="text/javascript"> Request.progressText = '<inp2:m_phrase name="la_title_Loading" no_editing="1" escape="1"/>'; Catalog.prototype.AfterInit = function () { $('#group_id').change(); } Catalog.prototype.go_to_group = function($group_id) { this.switchTab(); // refresh current item tab } Catalog.prototype.refreshTab = function($prefix, $div_id, $force) { var $group_id = $('#group_id').val(); // alert('refreshTab. GroupID: '+$group_id); var $tab_group_id = document.getElementById($div_id).getAttribute('group_id'); if ($group_id != $tab_group_id || $force) { // query tab content only in case if not queried or category don't match var $url = this.URLMask.replace('#ITEM_PREFIX#', $prefix).replace('#GROUP_ID#', $group_id); this.BusyRequest[$prefix] = false; Request.makeRequest($url, this.BusyRequest[$prefix], $div_id, this.successCallback, this.errorCallback, $div_id, this); } /*else { alert('refresh disabled = {tab: '+this.ActivePrefix+'; group_id: '+$group_id+'}'); }*/ } // adds information about tab to tab_registry Catalog.prototype.registerTab = function($tab_id) { var $tab = document.getElementById($tab_id + '_div'); var $index = this.TabRegistry.length; this.TabRegistry[$index] = new Array(); this.TabRegistry[$index]['tab_id'] = $tab_id; this.TabRegistry[$index]['prefix'] = $tab.getAttribute('prefix'); this.TabRegistry[$index]['dep_buttons'] = new Array(); this.TabRegistry[$index]['index'] = $index; } Catalog.prototype.displaySearch = function ($prefix) { } Catalog.prototype.submit_event = function($prefix_special, $event, $t) { var $prev_template = get_hidden_field('t'); if (isset($event)) set_hidden_field('events[' + $prefix_special + ']', $event); if (isset($t)) set_hidden_field('t', $t); var $tab_id = this.queryTabRegistry('prefix', this.ActivePrefix, 'tab_id'); this.submit_kernel_form(); set_hidden_field('t', $prev_template); } var $PermManager = new Catalog('<inp2:m_Link template="categories/permissions_tab" item_prefix="#ITEM_PREFIX#" group_id="#GROUP_ID#" pass="m,c" no_amp="1" js_escape="1"/>', 'permmanager_', 'CategoryPermissionEditor'); a_toolbar = new ToolBar(); a_toolbar.AddButton( new ToolBarButton('select', '<inp2:m_phrase label="la_ToolTip_Save" escape="1"/>', function() { submit_event('c','<inp2:c_SaveEvent/>'); } ) ); a_toolbar.AddButton( new ToolBarButton('cancel', '<inp2:m_phrase label="la_ToolTip_Cancel" escape="1"/>', function() { submit_event('c','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('c', '<inp2:c_PrevId/>'); } ) ); a_toolbar.AddButton( new ToolBarButton('next', '<inp2:m_phrase label="la_ToolTip_Next" escape="1"/>', function() { go_to_id('c', '<inp2:c_NextId/>'); } ) ); function edit(){ } a_toolbar.Render(); <inp2:m_if check="c_IsSingle"> a_toolbar.HideButton('prev'); a_toolbar.HideButton('next'); a_toolbar.HideButton('sep1'); <inp2:m_else/> <inp2:m_if check="c_IsLast"> a_toolbar.DisableButton('next'); </inp2:m_if> <inp2:m_if check="c_IsFirst"> a_toolbar.DisableButton('prev'); </inp2:m_if> </inp2:m_if> </script> </td> <inp2:m_RenderElement name="search_main_toolbar" prefix="g" grid="Radio"/> </tr> </tbody> </table> <inp2:c_SaveWarning name="grid_save_warning"/> <div id="scroll_container" mode="minimal"> <table class="edit-form" style="border-bottom: 1px solid black;"> <inp2:m_RenderElement name="subsection" title="la_section_General"/> <tr class="<inp2:m_odd_even odd='edit-form-odd' even='edit-form-even'/>"> <td class="label-cell"> <inp2:m_Phrase name="la_fld_Group"/>: </td> <td class="control-mid"> </td> <td class="control-cell"> <select name="group_id" id="group_id"> <inp2:m_DefineElement name="group_element"> <option value="<inp2:Field name='GroupId'/>"><inp2:Field name="Name"/></option> </inp2:m_DefineElement> <inp2:g_PrintList render_as="group_element" per_page="-1"/> </select> </td> </tr> <inp2:m_RenderElement name="inp_edit_filler"/> </table> </div> <br/> <!-- item tabs: begin --> <inp2:m_DefineElement name="item_tab" title="" special=""> <td class="tab-spacer"><img src="img/spacer.gif" width="3" height="1"/></td> <td id="<inp2:m_param name="prefix"/><inp2:m_param name="special"/>_tab" class="tab"> <img src="<inp2:m_TemplatesBase module='$icon_module'/>/img/itemicons/<inp2:m_Param name='icon'/>" width="16" height="16" align="absmiddle" alt=""/> <a href="#" onclick="$PermManager.switchTab('<inp2:m_param name="prefix"/>'); return false;" class="tab-link"> - <inp2:m_param name="title"/> + <inp2:m_Phrase name="$title"/> </a> </td> </inp2:m_DefineElement> <inp2:m_DefineElement name="permission_tabs"> - <inp2:adm_ListCatalogTabs render_as="item_tab" title_property="PermTabText"/> + <inp2:adm_ListCatalogTabs render_as="item_tab" title_property="ViewMenuPhrase"/> </inp2:m_DefineElement> <inp2:m_RenderElement name="tabs_container" tabs_render_as="permission_tabs"/> <!-- item tabs: end --> <inp2:c-perm_PrintTabs template="categories/permissions_tab" tab_init="1"/> <inp2:m_include t="incs/footer"/> <script type="text/javascript"> var $last_group_id = false; function update_light(perm_name, value) { document.getElementById('light_'+perm_name).src = 'img/perm_' + (value ? 'green' : 'red') + '.gif'; } function inherited_click(perm_name, inherited_value, state, access_cb_id) { if (state) { update_light(perm_name, inherited_value); document.getElementById(access_cb_id).disabled = true; } else { update_light(perm_name, document.getElementById(access_cb_id).checked) document.getElementById(access_cb_id).disabled = false; } } $(document).ready( function() { // last selected group set_hidden_field('current_group_id', $('#group_id').val()); $('#group_id').change( function ($e) { var $group_id = $(this).val(); if ($last_group_id !== false) { // save permissions from previous selected group set_hidden_field('current_group_id', $last_group_id); set_hidden_field('item_prefix', $PermManager.ActivePrefix); $PermManager.submit_event('c', 'OnPreSave', 'categories/permissions_tab'); } // get permissions from new group $PermManager.go_to_group($group_id); // remember previous selected group $last_group_id = $group_id; } ); $PermManager.Init(false); } ); Application.setHook( new Array ('c:OnPreSaveAndGoToTab', 'c:OnPreSaveAndGo', 'c:OnSave'), function($event) { set_hidden_field('current_group_id', $last_group_id); } ); -</script> \ No newline at end of file +</script> Index: branches/5.3.x/core/admin_templates/categories/xml/tree_categories.tpl =================================================================== --- branches/5.3.x/core/admin_templates/categories/xml/tree_categories.tpl (revision 16394) +++ branches/5.3.x/core/admin_templates/categories/xml/tree_categories.tpl (revision 16395) @@ -1,20 +1,20 @@ <inp2:m_XMLTemplate/> <inp2:m_DefineElement name="category"> <inp2:m_if check="Field" field="CachedDescendantCatsQty"> <folder name="<inp2:Field name='Name'/>" - href="<inp2:CategoryLink template='catalog/catalog' direct_link='1' pass='m' m_opener='r'/>" + href="<inp2:AdminTreeLink template='catalog/catalog'/>" icon="<inp2:c_ModulePath module='core'/>img/icons/<inp2:ItemIcon grid='Default' icon_prefix='icon24_'/>" onclick="checkCatalog(<inp2:Field name='CategoryId' db="db"/>, '<inp2:GetModulePrefix/>')" load_url="<inp2:CategoryLink direct_link="1" pass='m'/>"> </folder> <inp2:m_else/> <item - href="<inp2:CategoryLink template='catalog/catalog' direct_link='1' pass='m' m_opener='r'/>" + href="<inp2:AdminTreeLink template='catalog/catalog'/>" icon="<inp2:c_ModulePath module='core'/>img/icons/<inp2:ItemIcon grid='Default' icon_prefix='icon24_'/>" onclick="checkCatalog(<inp2:Field name='CategoryId' db="db"/>, '<inp2:GetModulePrefix/>')"><inp2:Field name="Name"/></item> </inp2:m_if> </inp2:m_DefineElement> <tree> <inp2:c.tree_PrintList render_as="category" per_page="-1"/> -</tree> \ No newline at end of file +</tree> Index: branches/5.3.x/core/admin_templates/tree.tpl =================================================================== --- branches/5.3.x/core/admin_templates/tree.tpl (revision 16394) +++ branches/5.3.x/core/admin_templates/tree.tpl (revision 16395) @@ -1,196 +1,199 @@ <inp2:m_Set skip_last_template="1"/> <inp2:m_include t="incs/header" nobody="yes" noform="yes"/> <inp2:m_NoDebug/> <body class="tree-body" onresize="onTreeFrameResize();"> <script type="text/javascript"> var $save_timer = null, $last_width = parseInt('<inp2:m_GetConfig name="MenuFrameWidth"/>'); function credits(url) { openwin(url, 'credits', 280, 520); } function onTreeFrameResize() { - var $frameset = $('#sub_frameset', window.parent.document); - if (!$frameset.length) { - return ; + var $menu_frame = $('#menu_frame', window.parent.document), + $width; + + if (!$menu_frame.length) { + return; } - var $width = $frameset.attr('cols').split(',')[0]; - if (($width <= 0) || ($width == $last_width)) { + $width = $menu_frame.width(); + + if (($width <= 0) || ($width == $last_width)) { // don't save zero width - return ; + return; } clearTimeout($save_timer); $save_timer = setTimeout( function() {saveFrameWidth($width);}, 2000); } function saveFrameWidth($width) { getFrame('head').$FrameResizer.OpenWidth = $width; $.get( '<inp2:m_Link template="index" adm_event="OnSaveMenuFrameWidth" pass="m,adm" no_amp="1" js_escape="1"/>', {width: $width} ); $last_width = $width; } </script> <script type="text/javascript" src="<inp2:m_Compress files='js/tree.js'/>"></script> <table style="height: 100%; width: 100%; border-right: 1px solid #777; border-bottom: 1px solid #777;"> <tr> <td colspan="2" style="vertical-align: top; padding: 5px;"> <inp2:m_DefineElement name="xml_node" icon_module=""> <inp2:m_if check="m_ParamEquals" param="children_count" value="0"> <item href="<inp2:m_param name='section_url' html_escape='1'/>" priority="<inp2:m_param name='priority' html_escape='1'/>" onclick="<inp2:m_param name='onclick' html_escape='1'/>" icon="<inp2:$SectionPrefix_ModulePath module='$icon_module'/>img/icons/icon24_<inp2:m_param name='icon'/>.png"<inp2:m_if check="m_Param" name="debug_only"> debug_only="1"</inp2:m_if>><inp2:m_phrase name="$label" html_escape="1"/></item> <inp2:m_else/> <folder href="<inp2:m_param name='section_url' html_escape='1'/>" priority="<inp2:m_param name='priority' html_escape='1'/>" container="<inp2:m_param name='container' html_escape='1'/>" onclick="<inp2:m_param name='onclick' html_escape='1'/>" name="<inp2:m_phrase name='$label' html_escape='1'/>" icon="<inp2:$SectionPrefix_ModulePath module="$icon_module"/>img/icons/icon24_<inp2:m_param name='icon'/>.png" load_url="<inp2:m_param name='late_load' html_escape='1'/>"<inp2:m_if check="m_Param" name="debug_only"> debug_only="1"</inp2:m_if>><inp2:adm_PrintSections render_as="xml_node" section_name="$section_name"/></folder> </inp2:m_if> </inp2:m_DefineElement> <table class="tree"> <tbody id="tree"> </tbody> </table> <script type="text/javascript"> var TREE_ICONS_PATH = 'img/tree'; var TREE_SHOW_PRIORITY = <inp2:m_if check="adm_ConstOn" name="DBG_SHOW_TREE_PRIORITY" debug_mode="1">1<inp2:m_else/>0</inp2:m_if>; var TREE_LOADING_NODE = { text: '<inp2:m_Phrase name="la_title_Loading" no_editing="1"/>', icon: '<inp2:adm_ModulePath module="core"/>img/icons/icon24_loading.gif' }; <inp2:m_DefineElement name="root_node"> var the_tree = new TreeFolder('tree', '<inp2:m_param name="label" js_escape="1"/>', '<inp2:m_param name="section_url" js_escape="1"/>', '<inp2:$SectionPrefix_ModulePath module="$icon_module"/>img/icons/icon24_<inp2:m_param name="icon"/>.png', null, null, '<inp2:m_param name="priority" js_escape='1'/>', '<inp2:m_param name="container" js_escape="1"/>'); </inp2:m_DefineElement> <inp2:adm_PrintSection render_as="root_node" section_name="in-portal:root"/> the_tree.AddFromXML('<tree><inp2:adm_PrintSections render_as="xml_node" section_name="in-portal:root" js_escape="1"/></tree>'); <inp2:m_if check="adm_MainFrameLink"> var fld = the_tree.locateItemByURL('<inp2:adm_MainFrameLink m_opener="r" js_escape="1"/>'); if (fld) { fld.highLight(); } else { the_tree.highLight(); } <inp2:m_else/> the_tree.highLight(); </inp2:m_if> </script> </td> </tr> </table> <script type="text/javascript"> - function checkCatalog($cat_id) { + function checkCatalog($cat_id, $module_prefix) { var $ret = checkEditMode(false); var $right_frame = getFrame('main'); if ($ret && $right_frame.$is_catalog) { - $right_frame.$Catalog.go_to_cat($cat_id); + $right_frame.$Catalog.go_to_cat($cat_id, $module_prefix); return 1; // this opens folder, but disables click } return $ret; } function setCatalogTab($prefix) { var $ret = checkEditMode(false); if ($ret) { var $right_frame = getFrame('main'); var $catalog_type = (typeof $right_frame.$Catalog != 'undefined') ? $right_frame.$Catalog.type : ''; // highlight "Structure & Data" node, when one of it's shortcut nodes are clicked <inp2:m_DefineElement name="section_url_element"><inp2:m_param name="section_url"/></inp2:m_DefineElement> var $structure_node = the_tree.locateItemByURL('<inp2:adm_PrintSection render_as="section_url_element" section_name="in-portal:browse" js_escape="1"/>'); if ($catalog_type == 'AdvancedView') { $right_frame.$Catalog.switchTab($prefix); return $structure_node; // this opens folder, but disables click } // this disabled click and causes other node to be highlighted return $structure_node; } return $ret; } function checkEditMode($reset_toolbar) { if (!isset($reset_toolbar)) { $reset_toolbar = true; } if ($reset_toolbar) { getFrame('head').$('#extra_toolbar').html(''); } var $phrase = '<inp2:m_Phrase label="la_EditingInProgress" js_escape="1"/>'; if (getFrame('main').$edit_mode) { return confirm($phrase) ? true : false; } return true; } function ReloadFolder(url, with_late_load) { if (!with_late_load) with_late_load = false; var fld = the_tree.locateItemByURL(url.replace(/&/g, '&'), with_late_load); if (fld) { fld.reload(); } } function ShowStructure($url, $visible) { var fld = the_tree.locateItemByURL($url, true); if (fld) { if ($visible) { fld.expand(); } else { fld.collapse(); } } } function SyncActive(url) { var fld = the_tree.locateItemByURL(url); if (fld) { fld.highLight(); } } <inp2:m_DefineElement name="container_node"> var $container_node = the_tree.locateItemByURL('<inp2:m_param name="section_url" js_escape="1"/>'); $container_node.Container = true; </inp2:m_DefineElement> <inp2:m_DefineElement name="container_node_fix"> <inp2:m_if check="m_GetConfig" name="$config_var"> <inp2:m_ifnot check="m_IsDebugMode"> <inp2:adm_PrintSection render_as="container_node" section_name="$section"/> </inp2:m_ifnot> </inp2:m_if> </inp2:m_DefineElement> <inp2:m_RenderElement name="container_node_fix" config_var="DebugOnlyFormConfigurator" section="in-portal:forms"/> <inp2:m_RenderElement name="container_node_fix" config_var="DebugOnlyPromoBlockGroupConfigurator" section="in-portal:promo_block_groups"/> </script> <!--## when form is on top, then 100% height is broken ##--> <inp2:m_RenderElement name="kernel_form"/> -<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/mailing_lists/send_queue.tpl =================================================================== --- branches/5.3.x/core/admin_templates/mailing_lists/send_queue.tpl (revision 16394) +++ branches/5.3.x/core/admin_templates/mailing_lists/send_queue.tpl (revision 16395) @@ -1,15 +1,15 @@ <inp2:adm_SetPopupSize width="900" height="600"/> <inp2:m_include t="incs/header"/> <inp2:m_RenderElement name="combined_header" section="in-portal:user_list" permission_type="advanced:send_email" prefix="email-queue" title_preset="email_send"/> <inp2:m_RenderElement name="ajax_progress_bar" cancel_action="cancel_action();"/> <script type="text/javascript"> function cancel_action() { redirect('<inp2:m_Link template="mailing_lists/send_complete" no_amp="1" js_escape="1"/>'); } $QueueProcessor = new AjaxProgressBar('<inp2:m_t template="index" email-queue_event="OnProcessAjax" finish_template="mailing_lists/send_complete" pass="m,email-queue" no_amp="1" js_escape="1"/>'); </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_blocks.tpl =================================================================== --- branches/5.3.x/core/admin_templates/users/user_blocks.tpl (revision 16394) +++ branches/5.3.x/core/admin_templates/users/user_blocks.tpl (revision 16395) @@ -1,70 +1,70 @@ <inp2:m_DefineElement name="password_fields"> <inp2:m_RenderElement design="form_row" prefix="u" field="Password" title="la_fld_Password"> <td class="control-cell"> - <input type="password" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field name='{$field}_plain'/>" tabindex="<inp2:m_get param='tab_index'/>"/> + <input type="password" autocomplete="off" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field name='{$field}_plain'/>" tabindex="<inp2:m_get param='tab_index'/>"/> <input type="button" class="button" id="generate_password" value="<inp2:m_Phrase name='la_btn_Generate' no_editing='1'/>"/> <script type="text/javascript"> $(document).ready( function() { <inp2:m_ifnot check="{$prefix}_Field" name="{$field}_plain"> $('#' + jq('<inp2:{$prefix}_InputName field="$field"/>')).val(''); </inp2:m_ifnot> } ); </script> </td> </inp2:m_RenderElement> <inp2:m_RenderElement design="form_row" prefix="u" field="VerifyPassword" title="la_fld_VerifyPassword"> <td class="control-cell"> - <input type="password" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field name='{$field}_plain'/>" tabindex="<inp2:m_get param='tab_index'/>"/> + <input type="password" autocomplete="off" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field name='{$field}_plain'/>" tabindex="<inp2:m_get param='tab_index'/>"/> <span<inp2:m_ifnot check="{$prefix}_Field" name="Password_plain"> style="display: none"</inp2:m_ifnot> id="email_password"> <input type="hidden" id="<inp2:{$prefix}_InputName field='EmailPassword'/>" name="<inp2:{$prefix}_InputName field='EmailPassword'/>" value="<inp2:{$prefix}_Field field='EmailPassword' db='db'/>"> <input tabindex="<inp2:m_get param='tab_index'/>" type="checkbox" id="_cb_<inp2:{$prefix}_InputName field='EmailPassword'/>" name="_cb_<inp2:{$prefix}_InputName field='EmailPassword'/>" <inp2:{$prefix}_Field field="EmailPassword" checked="checked" db="db"/> onchange="update_checkbox(this, document.getElementById('<inp2:{$prefix}_InputName field='EmailPassword'/>'));"/> <label for="_cb_<inp2:{$prefix}_InputName field='EmailPassword'/>"><inp2:m_Phrase name="la_fld_EmailPassword"/></label> </span> <script type="text/javascript"> $(document).ready( function() { <inp2:m_ifnot check="{$prefix}_Field" name="{$field}_plain"> $('#' + jq('<inp2:{$prefix}_InputName field="$field"/>')).val(''); </inp2:m_ifnot> } ); </script> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="password_fields_js"> var $field_mask = '<inp2:u_InputName name="#FIELD_NAME#" js_escape="1"/>'; var $email_phrase = '<inp2:m_Phrase name="la_fld_EmailPassword" js_escape="1"/>'; $( get_control($field_mask, 'Password') ).change( function ($e) { var $password = $(this).val(); if ($password) { $('label', '#email_password').html( $email_phrase.replace('{password}', $password) ); $('#email_password').show(); } } ); $('#generate_password').click( function ($e) { $.get( '<inp2:m_Link template="dummy" events[u]="OnGeneratePassword" no_amp="1" js_escape="1"/>', function ($password) { $( get_control($field_mask, 'Password') ).val($password).change(); $( get_control($field_mask, 'VerifyPassword') ).val($password); } ) } ); $('label', '#email_password').html( $email_phrase.replace('{password}', '<inp2:u_Field name="Password_plain"/>') ); -</inp2:m_DefineElement> \ No newline at end of file +</inp2:m_DefineElement> Index: branches/5.3.x/core/admin_templates/js/script.js =================================================================== --- branches/5.3.x/core/admin_templates/js/script.js (revision 16394) +++ branches/5.3.x/core/admin_templates/js/script.js (revision 16395) @@ -1,1960 +1,1974 @@ if ( !( isset($init_made) && $init_made ) ) { var Application = new kApplication(); var Grids = [], GridScrollers = [], Toolbars = [], $Menus = [], $ViewMenus = [], $nls_menus = [], $MenuNames = []; var $CKEditors = {}; // input name VS ck options mapping var $CodeMirrorEditors = {}; // input name VS code mirror options mapping var $form_name = 'kernel_form'; if ( !$fw_menus ) { var $fw_menus = new Array(); } var $env = '', submitted = false, unload_legal = false, $edit_mode = false, $init_made = true; // in case of double inclusion of script.js :) // hook processing var hBEFORE = 1; // this is const, but including this twice causes errors var hAFTER = 2; // this is const, but including this twice causes errors replaceFireBug(); } function use_popups($prefix_special, $event, $mode) { if ( $mode === undefined || $mode == 'popup' ) { return $use_popups; } return $modal_windows; } function getArrayValue() { var $value = arguments[0]; var $current_key = 0; $i = 1; while ($i < arguments.length) { $current_key = arguments[$i]; if ( isset($value[$current_key]) ) { $value = $value[$current_key]; } else { return false; } $i++; } return $value; } function setArrayValue() { // first argument - array, other arguments - keys (arrays too), last argument - value var $array = arguments[0]; var $current_key = 0; $i = 1; while ($i < arguments.length - 1) { $current_key = arguments[$i]; if ( !isset($array[$current_key]) ) { $array[$current_key] = new Array(); } $array = $array[$current_key]; $i++; } $array[$array.length] = arguments[arguments.length - 1]; } function resort_grid($prefix_special, $field, $ajax) { set_form($prefix_special, $ajax); set_hidden_field($prefix_special + '_Sort1', $field); submit_event($prefix_special, 'OnSetSorting', null, null, $ajax); } function direct_sort_grid($prefix_special, $field, $direction, $field_pos, $ajax) { if ( !isset($field_pos) ) { $field_pos = 1; } set_form($prefix_special, $ajax); set_hidden_field($prefix_special + '_Sort' + $field_pos, $field); set_hidden_field($prefix_special + '_Sort' + $field_pos + '_Dir', $direction); set_hidden_field($prefix_special + '_SortPos', $field_pos); submit_event($prefix_special, 'OnSetSortingDirect', null, null, $ajax); } function reset_sorting($prefix_special, $ajax) { submit_event($prefix_special, 'OnResetSorting', null, null, $ajax); } function set_per_page($prefix_special, $per_page, $ajax) { set_form($prefix_special, $ajax); set_hidden_field($prefix_special + '_PerPage', $per_page); submit_event($prefix_special, 'OnSetPerPage', null, null, $ajax); } function set_refresh_interval($prefix_special, $refresh_interval, $ajax) { set_form($prefix_special, $ajax); set_hidden_field('refresh_interval', $refresh_interval); submit_event($prefix_special, 'OnSetAutoRefreshInterval', null, null, $ajax); } function submit_event(prefix_special, event, t, form_action, $ajax) { if ( !Application.processHooks(prefix_special + ':' + event) ) { return false; } if ( $ajax ) { return $Catalog.submit_event(prefix_special, event, t); } if ( event ) { set_hidden_field('events[' + prefix_special + ']', event); } if ( t ) { set_hidden_field('t', t); } if ( form_action ) { var old_env = ''; if ( !form_action.match(/\?/) ) { document.getElementById($form_name).action.match(/.*(\?.*)/); old_env = RegExp.$1; } document.getElementById($form_name).action = form_action + old_env; } submit_kernel_form(); // reset remove special mark (otherwise all future events will have special removed too) set_hidden_field('remove_specials[' + prefix_special + ']', null); } function submit_event_ajax(prefix_special, event, t, $callback) { if ( !Application.processHooks(prefix_special + ':' + event) ) { return false; } if ( event ) { set_hidden_field('events[' + prefix_special + ']', event); } if ( t ) { set_hidden_field('t', t); } var $form = $('#kernel_form'), $from_params = $form.serialize(); $.post($form.attr('action'), $from_params, $callback); // reset remove special mark (otherwise all future events will have special removed too) set_hidden_field('events[' + prefix_special + ']', ''); set_hidden_field('remove_specials[' + prefix_special + ']', null); } function submit_action($url, $action) { $form = document.getElementById($form_name); $form.action = $url; set_hidden_field('Action', $action); submit_kernel_form(); } function show_form_data() { var $kf = document.getElementById($form_name); $ret = ''; for (var i in $kf.elements) { $elem = $kf.elements[i]; $ret += $elem.id + ' = ' + $elem.value + "\n"; } alert($ret); } function submit_kernel_form() { if ( submitted ) { return; } submitted = true; unload_legal = true; // bug: when opening new popup from this window, then this window is not refreshed and this mark stays forever var $form = document.getElementById($form_name); if ( $.isFunction($form.onsubmit) ) { $form.onsubmit(); } $($form).submit(); $form.target = ''; set_hidden_field('t', t); // window.setTimeout(function() {submitted = false}, 500); } function set_event(prefix_special, event) { var event_field = document.getElementById('events[' + prefix_special + ']'); if ( isset(event_field) ) { event_field.value = event; } } function isset(variable) { if ( variable == null ) { return false; } return (typeof(variable) == 'undefined') ? false : true; } function in_array(needle, haystack) { return array_search(needle, haystack) != -1; } function array_search(needle, haystack) { for (var i = 0; i < haystack.length; i++) { if ( haystack[i] == needle ) return i; } return -1; } function print_pre(variable, msg) { if ( !isset(msg) ) { msg = ''; } var s = msg; for (prop in variable) { s += prop + " => " + variable[prop] + "\n"; } alert(s); } function go_to_page($prefix_special, $page, $ajax) { set_form($prefix_special, $ajax); set_hidden_field($prefix_special + '_Page', $page); submit_event($prefix_special, 'OnSetPage', null, null, $ajax); } function go_to_list(prefix_special, tab) { set_hidden_field(prefix_special + '_GoTab', tab); submit_event(prefix_special, 'OnUpdateAndGoToTab', null); } function go_to_tab(prefix_special, tab) { set_hidden_field(prefix_special + '_GoTab', tab); submit_event(prefix_special, 'OnPreSaveAndGoToTab', null); } function go_to_id(prefix_special, id) { set_hidden_field(prefix_special + '_GoId', id); submit_event(prefix_special, 'OnPreSaveAndGo') } // in-portal compatibility functions: begin function getScriptURL($script_name, tpl) { tpl = tpl ? '-' + tpl : ''; var $asid = get_hidden_field('sid'); return base_url + $script_name + '?env=' + ( isset($env) && $env ? $env : $asid ) + tpl + '&en=0'; } function OpenEditor(extra_env, TargetForm, TargetField) { var $url = getScriptURL('admin/index.php', 'popups/editor'); $url = $url + '&TargetForm=' + TargetForm + '&TargetField=' + TargetField + '&destform=popup'; if ( extra_env.length > 0 ) { $url += extra_env; } openwin($url, 'html_edit', 800, 575); } // in-portal compatibility functions: end function InitTranslator(prefix, field, t, multi_line, $before_callback) { var $window_name = 'select_' + t.replace(/(\/|-)/g, '_'); var $options = { onAfterShow: function ($popup_window) { if ( $modal_windows ) { getFrame('main').initTranslatorOnAfterShow(prefix, field, t, multi_line, $before_callback); } else { initTranslatorOnAfterShow(prefix, field, t, multi_line, $before_callback, $popup_window); } } }; openwin('', $window_name, 750, 400, $options); } function initTranslatorOnAfterShow(prefix, field, t, multi_line, $before_callback, $popup_window) { var $window_name = 'select_' + t.replace(/(\/|-)/g, '_'); $popup_window = onAfterWindowOpen($window_name, undefined, $popup_window); if ( $popup_window === false ) { // iframe onload happens on frame content change too -> don't react on it return; } var $opener = getWindowOpener($popup_window); var $prev_opener = get_hidden_field('m_opener'); $opener.set_hidden_field('m_opener', 'p'); $opener.set_hidden_field('translator_wnd_name', $window_name); $opener.set_hidden_field('translator_field', field); $opener.set_hidden_field('translator_t', t); $opener.set_hidden_field('translator_prefixes', prefix); $opener.set_hidden_field('translator_multi_line', isset(multi_line) ? multi_line : 0); if ( $.isFunction($before_callback) ) { $before_callback($opener); } $opener.document.getElementById($opener.$form_name).target = $window_name; var split_prefix = prefix.split(','); $opener.submit_event(split_prefix[0], 'OnPreSaveAndOpenTranslator'); $opener.set_hidden_field('m_opener', $prev_opener); $opener.submitted = false; } function PreSaveAndOpenTranslator(prefix, field, t, multi_line) { InitTranslator(prefix, field, t, multi_line); } function PreSaveAndOpenTranslatorCV(prefix, field, t, resource_id, multi_line) { InitTranslator( prefix, field, t, multi_line, function ($opener) { $opener.set_hidden_field('translator_resource_id', resource_id); } ); } function openTranslator(prefix, field, url, wnd) { var $kf = document.getElementById($form_name); set_hidden_field('trans_prefix', prefix); set_hidden_field('trans_field', field); set_hidden_field('events[trans]', 'OnLoad'); var $regex = new RegExp('(.*)\?env=(' + document.getElementById('sid').value + ')?-(.*?):(.*)'); var $t = $regex.exec(rawurldecode(url))[3]; $kf.target = wnd; submit_event(prefix, '', $t, url); submitted = false; } function openwin($url, $name, $width, $height, $options) { var $settings = { url: base_url + 'core/admin_templates/blank.html?width=' + $width + '&height=' + $height + '&TB_iframe=true&modal=true', caption: 'Loading ...', onAfterShow: function ($popup_window) { if ( $modal_windows ) { getFrame('main').onAfterWindowOpen($name, $url); } else { onAfterWindowOpen($name, $url, $popup_window); } } }; if ( $options !== undefined ) { $.extend($settings, $options); } if ( $modal_windows ) { if ( window.name != 'main' ) { // all popups are opened based on main frame return getFrame('main').TB.show($settings); } TB.show($settings); return; } // prevent window from opening larger, then screen resolution on user's computer (to Kostja) var left = Math.round((screen.width - $width) / 2); var top = Math.round((screen.height - $height) / 2); var cur_x = document.all ? window.screenLeft : window.screenX; var cur_y = document.all ? window.screenTop : window.screenY; var $window_params = 'left=' + left + ',top=' + top + ',width=' + $width + ',height=' + $height + ',status=yes,resizable=yes,menubar=no,scrollbars=yes,toolbar=no'; var $popup_window = window.open($url, $name, $window_params); if ( $.isFunction($settings.onAfterShow) ) { $settings.onAfterShow($popup_window); } return $popup_window; } function onAfterWindowOpen($window_name, $url, $popup_window) { // this is always invoked from "main" frame if ( $popup_window === undefined ) { var $popup_window = $('#' + TB.getId('TB_iframeContent')).get(0).contentWindow; if ( !$.isFunction($popup_window.onLoad) ) { // iframe onload happens on frame content change too -> don't react on it return false; } $popup_window.onLoad(); } $popup_window.name = $window_name; if ( $url !== undefined ) { $popup_window.location.href = $url; } if ( $modal_windows ) { TB.setWindowMetaData('window_name', $window_name); // used to simulate window.opener functionality } return $popup_window; } function OnResizePopup(e) { if ( !document.all ) { var $winW = window.innerWidth; var $winH = window.innerHeight; } else { var $winW = window.document.body.offsetWidth; var $winH = window.document.body.offsetHeight; } window.status = '[width: ' + $winW + '; height: ' + $winH + ']'; } function opener_action(new_action) { var $prev_opener = get_hidden_field('m_opener'); set_hidden_field('m_opener', new_action); return $prev_opener; } function open_popup($prefix_special, $event, $t, $window_size, $onAfterOpenPopup) { if ( !$window_size ) { // if no size given, then query it from ajax var $default_size = '750x400'; var $pm = getFrame('head').$popup_manager; if ( $pm ) { // popup manager was found in head frame $pm.GetSize($t, function ($response) { if ( !$response.match(/([\d]+)x([\d]+)/) ) { // invalid response was received, may be php fatal error during AJAX request $response = $default_size; } open_popup($prefix_special, $event, $t, $response, $onAfterOpenPopup); }); return; } $window_size = $default_size; } $window_size = $window_size.split('x'); var $window_name = $t.replace(/(\/|-)/g, '_'); // replace "/" and "-" with "_" var $options = { onAfterShow: function ($popup_window) { if ( $modal_windows ) { getFrame('main').onAfterOpenPopup($prefix_special, $event, $t); } else { onAfterOpenPopup($prefix_special, $event, $t, $popup_window); } if ( $onAfterOpenPopup !== undefined && $.isFunction($onAfterOpenPopup) ) { $onAfterOpenPopup($popup_window); } } }; openwin('', $window_name, $window_size[0], $window_size[1], $options); } function onAfterOpenPopup($prefix_special, $event, $t, $popup_window) { // this is always invoked from "main" frame var $window_name = $t.replace(/(\/|-)/g, '_'); // replace "/" and "-" with "_" $popup_window = onAfterWindowOpen($window_name, undefined, $popup_window); if ( $popup_window === false ) { // iframe onload happens on frame content change too -> don't react on it return; } var $opener = getWindowOpener($popup_window); if ( $opener === null ) { // we are already in main window $opener = window; } $opener.document.getElementById($opener.$form_name).target = $window_name; var $prev_opener = $opener.opener_action('p'); event_bak = $opener.get_hidden_field('events[' + $prefix_special + ']') if ( !event_bak ) { event_bak = ''; } $opener.submit_event($prefix_special, $event, $t); $opener.opener_action($prev_opener); // restore opener in parent window $opener.set_hidden_field('events[' + $prefix_special + ']', event_bak); // restore event // AJAX popup size respoce is received after std_edit_item/std_precreate_item function exit $opener.set_hidden_field($prefix_special + '_mode', null); $opener.submitted = false; $opener.Application.processHooks($prefix_special + ':OnAfterOpenPopup'); } function openSelector($prefix, $url, $dst_field, $window_size, $event) { // get template name from url var $regex = new RegExp('(.*)\?env=(' + document.getElementById('sid').value + ')?-(.*?):(m[^:]+)'); $regex = $regex.exec(rawurldecode($url)); var $t = $regex[3]; // substitute form action with selector's url var $kf = document.getElementById($form_name); var $prev_action = $kf.action; $kf.action = $url; // check parameter values if ( !isset($event) ) { $event = ''; } Application.processHooks($prefix + ':OnBeforeOpenSelector'); // set variables need for selector to work set_hidden_field('main_prefix', $prefix); set_hidden_field('dst_field', $dst_field); // alert('openSelector(' + $prefix + ', ' + $event + ', ' + $t + ', ' + $window_size + ')'); open_popup( $prefix, $event, $t, $window_size, function () { // restore form action back $kf.action = $prev_action; } ); } function translate_phrase($label, $edit_template, $options) { set_hidden_field('phrases_label', $label); var $event = $options.event === undefined ? 'OnNew' : $options.event; if ( $options.simple_mode !== undefined ) { Application.SetVar('simple_mode', $options.simple_mode ? 1 : 0); if ( $options.simple_mode ) { Application.SetVar('front', 1); } } else { Application.SetVar('front', null); Application.SetVar('simple_mode', null); Application.SetVar('phrases_label', null); } if ( use_popups('phrases', $event) ) { open_popup('phrases', $event, $edit_template, null, function () { Application.SetVar('front', null); Application.SetVar('simple_mode', null); }); } else { opener_action('d'); submit_event('phrases', $event, $edit_template); Application.SetVar('front', null); Application.SetVar('simple_mode', null); Application.SetVar('phrases_label', null); } } function direct_edit($prefix_special, $url) { if ( use_popups($prefix_special, '') ) { openSelector($prefix_special, $url); } else { redirect($url); } return false; } function std_precreate_item(prefix_special, edit_template, $onAfterOpenPopup) { set_hidden_field(prefix_special + '_mode', 't'); if ( use_popups(prefix_special, 'OnPreCreate') ) { open_popup(prefix_special, 'OnPreCreate', edit_template, null, $onAfterOpenPopup); } else { opener_action('d'); submit_event(prefix_special, 'OnPreCreate', edit_template); } // set_hidden_field(prefix_special+'_mode', ''); } function std_new_item(prefix_special, edit_template, $onAfterOpenPopup) { if ( use_popups(prefix_special, 'OnNew') ) { open_popup(prefix_special, 'OnNew', edit_template, null, $onAfterOpenPopup); } else { opener_action('d'); submit_event(prefix_special, 'OnNew', edit_template); } } function std_edit_item(prefix_special, edit_template, $onAfterOpenPopup) { set_hidden_field(prefix_special + '_mode', 't'); if ( use_popups(prefix_special, 'OnEdit') ) { open_popup(prefix_special, 'OnEdit', edit_template, null, $onAfterOpenPopup); } else { opener_action('d'); submit_event(prefix_special, 'OnEdit', edit_template); } // set_hidden_field(prefix_special+'_mode', ''); } function std_edit_temp_item(prefix_special, edit_template, $onAfterOpenPopup) { if ( use_popups(prefix_special, 'OnStoreSelected') ) { open_popup(prefix_special, 'OnStoreSelected', edit_template, null, $onAfterOpenPopup); } else { opener_action('d'); submit_event(prefix_special, 'OnStoreSelected', edit_template); } } function std_delete_items(prefix_special, t, $ajax) { var phrase = phrases['la_Delete_Confirm'] ? phrases['la_Delete_Confirm'] : 'Are you sure you want to delete selected items?'; if ( inpConfirm(phrase) ) { submit_event(prefix_special, 'OnMassDelete', t, null, $ajax); } } function std_csv_export(prefix_special, grid, template) { set_hidden_field('PrefixSpecial', prefix_special); set_hidden_field('grid', grid); open_popup(prefix_special, '', template); } function std_csv_import(prefix_special, grid, template) { set_hidden_field('PrefixSpecial', prefix_special); set_hidden_field('grid', grid); if ( use_popups(prefix_special, '') ) { open_popup(prefix_special, '', template); } else { submit_event(prefix_special, '', template); } } // set current form base on ajax function set_form($prefix_special, $ajax) { if ( $ajax ) { $form_name = $Catalog.queryTabRegistry('prefix', $prefix_special, 'tab_id') + '_form'; } } // sets hidden field value // if the field does not exist - creates it function set_hidden_field($field_id, $value, $has_id) { var $kf = document.getElementById($form_name); var $field = $kf.elements[$field_id]; if ( $value === null ) { if ( $field ) { // alert('tag name on remove: ' + $field.parentNode.tagName); $field.parentNode.removeChild($field); // bug: sometimes hidden fields are inside BODY tag in DOM model, why? } return true; } if ( $field ) { $field.value = $value; return true; } $field = document.createElement('INPUT'); $field.type = 'hidden'; $field.name = $field_id; if ( !isset($has_id) || $has_id ) { $field.id = $field_id; } $field.value = $value; $kf.appendChild($field); return false; } // sets hidden field value // if the field does not exist - creates it function setInnerHTML($field_id, $value) { $(jq('#' + $field_id)).html($value); } function get_hidden_field($field) { var $kf = document.getElementById($form_name); return $kf.elements[$field] ? $kf.elements[$field].value : false; } function search($prefix_special, $grid_name, $ajax) { set_form($prefix_special, $ajax); set_hidden_field('grid_name', $grid_name); submit_event($prefix_special, 'OnSearch', null, null, $ajax); } function search_reset($prefix_special, $grid_name, $ajax) { set_form($prefix_special, $ajax); set_hidden_field('grid_name', $grid_name); submit_event($prefix_special, 'OnSearchReset', null, null, $ajax); } function search_keydown($event, $prefix_special, $grid, $ajax) { if ( $prefix_special !== undefined ) { // if $prefix_special is passed, then keydown event was not assigned by jQuery $event = $event ? $event : event; if ( window.event ) {// IE var $key_code = $event.keyCode; } else if ( $event.which ) { // Netscape/Firefox/Opera var $key_code = $event.which; } } else { // event bind with jQuery, so always use which var $key_code = $event.which; $prefix_special = $(this).attr('PrefixSpecial'); $grid = $(this).attr('Grid'); $ajax = $(this).attr('ajax'); } switch ($key_code) { case 13: search($prefix_special, $grid, parseInt($ajax)); break; case 27: search_reset($prefix_special, $grid, parseInt($ajax)); break; } } function getRealLeft(el) { if ( typeof(el) == 'string' ) { el = document.getElementById(el); } xPos = el.offsetLeft; tempEl = el.offsetParent; while (tempEl != null) { xPos += tempEl.offsetLeft; tempEl = tempEl.offsetParent; } // if (obj.x) return obj.x; return xPos; } function getRealTop(el) { if ( typeof(el) == 'string' ) { el = document.getElementById(el); } yPos = el.offsetTop; tempEl = el.offsetParent; while (tempEl != null) { yPos += tempEl.offsetTop; tempEl = tempEl.offsetParent; } // if (obj.y) return obj.y; return yPos; } function show_viewmenu_old($toolbar, $button_id) { var $img = $toolbar.GetButtonImage($button_id); var $pos_x = getRealLeft($img) - ((document.all) ? 6 : -2); var $pos_y = getRealTop($img) + 32; var $prefix_special = ''; window.triedToWriteMenus = false; if ( $ViewMenus.length == 1 ) { $prefix_special = $ViewMenus[$ViewMenus.length - 1]; $fw_menus[$prefix_special + '_view_menu'](); $Menus[$prefix_special + '_view_menu'].writeMenus('MenuContainers[' + $prefix_special + ']'); window.FW_showMenu($Menus[$prefix_special + '_view_menu'], $pos_x, $pos_y); } else { // prepare menus for (var $i in $ViewMenus) { $prefix_special = $ViewMenus[$i]; $fw_menus[$prefix_special + '_view_menu'](); } $Menus['mixed'] = new Menu('ViewMenu_mixed'); // merge menus into new one for (var $i in $ViewMenus) { $prefix_special = $ViewMenus[$i]; $Menus['mixed'].addMenuItem($Menus[$prefix_special + '_view_menu']); } $Menus['mixed'].writeMenus('MenuContainers[mixed]'); window.FW_showMenu($Menus['mixed'], $pos_x, $pos_y); } } var nlsMenuRendered = false; function show_viewmenu($toolbar, $button_id) { if ( $ViewMenus.length == 1 ) { $prefix_special = $ViewMenus[$ViewMenus.length - 1]; menu_to_show = $prefix_special + '_view_menu'; } else { mixed_menu = menuMgr.createMenu(rs('mixed_menu')); mixed_menu.applyBorder(false, false, false, false); mixed_menu.dropShadow("none"); mixed_menu.showIcon = true; // merge menus into new one for (var $i in $ViewMenus) { $prefix_special = $ViewMenus[$i]; mixed_menu.addItem(rs($prefix_special + '.view.menu.mixed'), $MenuNames[$prefix_special + '_view_menu'], 'javascript:void()', null, true, null, rs($prefix_special + '.view.menu'), $MenuNames[$prefix_special + '_view_menu']); } menu_to_show = 'mixed_menu'; } renderMenus(); nls_showMenu(rs(menu_to_show), $toolbar.GetButtonImage($button_id)) } function renderMenus() { // menuMgr.renderMenus closes all opened menus, but doesn't mark them as closed menuMgr.hideMenus(); menuMgr.renderMenus('nlsMenuPlace'); nlsMenuRendered = true; } function set_window_title($title) { var $window = window; - if ( $window.name != 'main' ) { - // traverse through real popups - $window = getFrame('main'); + if ( main_title.length ) { + $title += ' - ' + main_title; } - $window.top.document.title = (main_title.length ? main_title + ' - ' : '') + $title; + if ( !$modal_windows && getWindowOpener($window) ) { + $window.document.title = $title; + } + else { + if ( $window.name != 'main' ) { + // Traverse through real popups. + $window = getFrame('main'); + } + + $window.top.document.title = $title; + } if ( $modal_windows ) { $window.TB.setWindowTitle(''); } } function set_filter($prefix_special, $filter_id, $filter_value, $ajax) { set_form($prefix_special, $ajax); set_hidden_field('filter_id', $filter_id); set_hidden_field('filter_value', $filter_value); submit_event($prefix_special, 'OnSetFilter', null, null, $ajax); } function filters_remove_all($prefix_special, $ajax) { set_form($prefix_special, $ajax); submit_event($prefix_special, 'OnRemoveFilters', null, null, $ajax); } function filters_apply_all($prefix_special, $ajax) { set_form($prefix_special, $ajax); submit_event($prefix_special, 'OnApplyFilters', null, null, $ajax); } function filter_toggle($row_id, $prefix) { // var $row = $('#' + jq($row_id)); var $row = $('tr.to-range-filter'); var $hidden = $row.hasClass('hidden-filter'); if ( $hidden ) { $('td', $row).show(); $row.removeClass('hidden-filter'); } else { $('td', $row).hide(); $row.addClass('hidden-filter'); } // recalculate filter row heights/widths var $grid = GridScrollers[$prefix]; $grid.UpdateColWidths(); if ( $hidden && $grid.FiltersExpanded !== true ) { $grid.AdjustInputWidths(); $grid.FiltersExpanded = true; } // $grid.SetLeftHeights(); // $grid.UpdateTotalDimensions(); // $grid.SyncScroll(); // $grid.Resize( $grid.GetAutoSize() ); } function htmlspecialchars_decode(string) { string = string.toString().replace(/</g, '<').replace(/>/g, '>'); string = string.replace(/�*39;/g, "'"); string = string.replace(/"/g, '"'); string = string.replace(/&/g, '&'); return string; } function RemoveTranslationLink($string, $escaped) { if ( !isset($escaped) ) $escaped = true; if ( $escaped ) { return $string.replace(/<a href="(.*?)".*>(.*?)<\/a>/g, '$2'); } return $string.replace(/<a href="(.*?)".*>(.*?)<\/a>/g, '$2'); } function redirect($url) { window.location.href = $url; } function update_checkbox_options($cb_mask, $hidden_id) { var $kf = document.getElementById($form_name); var $tmp = ''; for (var i = 0; i < $kf.elements.length; i++) { if ( $kf.elements[i].id.match($cb_mask) ) { if ( $kf.elements[i].checked ) { $tmp += '|' + $kf.elements[i].value; } } } if ( $tmp.length > 0 ) { $tmp += '|'; } document.getElementById($hidden_id).value = $tmp.replace(/,$/, ''); } function update_multiple_options($hidden_id) { var $select = document.getElementById($hidden_id + '_select'); var $result = ''; for (var $i = 0; $i < $select.options.length; $i++) { if ( $select.options[$i].selected ) { $result += $select.options[$i].value + '|'; } } document.getElementById($hidden_id).value = $result ? '|' + $result : ''; } // related to lists operations (moving) function move_selected($from_list, $to_list, $error_msg) { if ( typeof($from_list) != 'object' ) $from_list = document.getElementById($from_list); if ( typeof($to_list) != 'object' ) $to_list = document.getElementById($to_list); if ( has_selected_options($from_list) ) { var $from_array = select_to_array($from_list); var $to_array = select_to_array($to_list); var $new_from = Array(); var $cur = null; for (var $i = 0; $i < $from_array.length; $i++) { $cur = $from_array[$i]; if ( $cur[2] ) // If selected - add to To array { $to_array[$to_array.length] = $cur; } else //Else - keep in new From { $new_from[$new_from.length] = $cur; } } $from_list = array_to_select($new_from, $from_list); $to_list = array_to_select($to_array, $to_list); } else { alert(isset($error_msg) ? $error_msg : 'Please select items to perform moving!'); } } function select_to_array($aSelect) { var $an_array = new Array(); var $cur = null; for (var $i = 0; $i < $aSelect.length; $i++) { $cur = $aSelect.options[$i]; $an_array[$an_array.length] = new Array($cur.text, $cur.value, $cur.selected); } return $an_array; } function array_to_select($anArray, $aSelect) { var $initial_length = $aSelect.length; for (var $i = $initial_length - 1; $i >= 0; $i--) { $aSelect.options[$i] = null; } for (var $i = 0; $i < $anArray.length; $i++) { $cur = $anArray[$i]; $aSelect.options[$aSelect.length] = new Option($cur[0], $cur[1]); } } function select_compare($a, $b) { if ( $a[0] < $b[0] ) { return -1; } if ( $a[0] > $b[0] ) { return 1; } return 0; } function select_to_string($aSelect) { var $result = ''; var $cur = null; if ( typeof($aSelect) != 'object' ) $aSelect = document.getElementById($aSelect); for (var $i = 0; $i < $aSelect.length; $i++) { $result += $aSelect.options[$i].value + '|'; } return $result.length ? '|' + $result : ''; } function selected_to_string($aSelect) { var $result = ''; var $cur = null; if ( typeof($aSelect) != 'object' ) $aSelect = document.getElementById($aSelect); for (var $i = 0; $i < $aSelect.length; $i++) { $cur = $aSelect.options[$i]; if ( $cur.selected && $cur.value != '' ) { $result += $cur.value + '|'; } } return $result.length ? '|' + $result : ''; } function string_to_selected($str, $aSelect) { var $cur = null; for (var $i = 0; $i < $aSelect.length; $i++) { $cur = $aSelect.options[$i]; $aSelect.options[$i].selected = $str.match('\\|' + $cur.value + '\\|') ? true : false; } } function set_selected($selected_options, $aSelect) { if ( !$selected_options.length ) return false; for (var $i = 0; $i < $aSelect.length; $i++) { for (var $k = 0; $k < $selected_options.length; $k++) { if ( $aSelect.options[$i].value == $selected_options[$k] ) { $aSelect.options[$i].selected = true; } } } } function get_selected_count($theList) { var $count = 0; var $cur = null; for (var $i = 0; $i < $theList.length; $i++) { $cur = $theList.options[$i]; if ( $cur.selected ) { $count++; } } return $count; } function get_selected_index($aSelect, $typeIndex) { var $index = 0; for (var $i = 0; $i < $aSelect.length; $i++) { if ( $aSelect.options[$i].selected ) { $index = $i; if ( $typeIndex == 'firstSelected' ) { break; } } } return $index; } function has_selected_options($theList) { var $ret = false; var $cur = null; for (var $i = 0; $i < $theList.length; $i++) { $cur = $theList.options[$i]; if ( $cur.selected ) { $ret = true; break; } } return $ret; } function select_sort($aSelect) { if ( typeof($aSelect) != 'object' ) { $aSelect = document.getElementById($aSelect); } var $to_array = select_to_array($aSelect); $to_array.sort(select_compare); array_to_select($to_array, $aSelect); } function move_options_up($aSelect, $interval) { if ( typeof($aSelect) != 'object' ) { $aSelect = document.getElementById($aSelect); } if ( has_selected_options($aSelect) ) { var $selected_options = Array(); var $first_selected = get_selected_index($aSelect, 'firstSelected'); for (var $i = 0; $i < $aSelect.length; $i++) { if ( $aSelect.options[$i].selected && ($first_selected > 0) ) { swap_options($aSelect, $i, $i - $interval); $selected_options[$selected_options.length] = $aSelect.options[$i - $interval].value; } else if ( $first_selected == 0 ) { //alert('Begin of list'); break; } } set_selected($selected_options, $aSelect); } else { //alert('Check items from moving'); } } function move_options_down($aSelect, $interval) { if ( typeof($aSelect) != 'object' ) { $aSelect = document.getElementById($aSelect); } if ( has_selected_options($aSelect) ) { var $last_selected = get_selected_index($aSelect, 'lastSelected'); var $selected_options = Array(); for (var $i = $aSelect.length - 1; $i >= 0; $i--) { if ( $aSelect.options[$i].selected && ($aSelect.length - ($last_selected + 1) > 0) ) { swap_options($aSelect, $i, $i + $interval); $selected_options[$selected_options.length] = $aSelect.options[$i + $interval].value; } else if ( $last_selected + 1 == $aSelect.length ) { //alert('End of list'); break; } } set_selected($selected_options, $aSelect); } else { //alert('Check items from moving'); } } function swap_options($aSelect, $src_num, $dst_num) { var $src_html = $aSelect.options[$src_num].innerHTML; var $dst_html = $aSelect.options[$dst_num].innerHTML; var $src_value = $aSelect.options[$src_num].value; var $dst_value = $aSelect.options[$dst_num].value; var $src_option = document.createElement('OPTION'); var $dst_option = document.createElement('OPTION'); $aSelect.remove($src_num); $aSelect.options.add($dst_option, $src_num); $dst_option.innerText = $dst_html; $dst_option.value = $dst_value; $dst_option.innerHTML = $dst_html; $aSelect.remove($dst_num); $aSelect.options.add($src_option, $dst_num); $src_option.innerText = $src_html; $src_option.value = $src_value; $src_option.innerHTML = $src_html; } function getXMLHTTPObject(content_type) { if ( !isset(content_type) ) { content_type = 'text/plain'; } var http_request = false; if ( window.XMLHttpRequest ) { // Mozilla, Safari,... http_request = new XMLHttpRequest(); if ( http_request.overrideMimeType ) { http_request.overrideMimeType(content_type); // See note below about this line } } else if ( window.ActiveXObject ) { // IE try { http_request = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { http_request = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { } } } return http_request; } function str_repeat($symbol, $count) { var $i = 0; var $ret = ''; while ($i < $count) { $ret += $symbol; $i++; } return $ret; } function getDocumentFromXML(xml) { if ( window.ActiveXObject ) { var doc = new ActiveXObject("Microsoft.XMLDOM"); doc.async = false; doc.loadXML(xml); } else { var parser = new DOMParser(); var doc = parser.parseFromString(xml, "text/xml"); } return doc; } function set_persistant_var($var_name, $var_value, $t, $form_action) { set_hidden_field('field', $var_name); set_hidden_field('value', $var_value); submit_event('u', 'OnSetPersistantVariable', $t, $form_action); } function setCookie($Name, $Value) { // set cookie if ( getCookie($Name) != $Value ) { document.cookie = $Name + '=' + escape($Value); } } function getCookie($Name) { // get cookie var $cookieString = document.cookie; var $index = $cookieString.indexOf($Name + '='); if ( $index == -1 ) { return null; } $index = $cookieString.indexOf('=', $index) + 1; var $endstr = $cookieString.indexOf(';', $index); if ( $endstr == -1 ) { $endstr = $cookieString.length; } return unescape($cookieString.substring($index, $endstr)); } function deleteCookie($Name) { // deletes cookie if ( getCookie($Name) ) { var d = new Date(); document.cookie = $Name + '=;expires=' + d.toGMTString() + ';' + ';'; } } function addElement($dst_element, $tag_name) { var $new_element = document.createElement($tag_name.toUpperCase()); $dst_element.appendChild($new_element); return $new_element; } Math.sum = function ($array) { var $i = 0; var $total = 0; while ($i < $array.length) { $total += $array[$i]; $i++; } return $total; } Math.average = function ($array) { return Math.sum($array) / $array.length; } // remove spaces and underscores from a string, used for nls_menu function rs(str, is_phrase) { if ( isset(is_phrase) && is_phrase ) { str = RemoveTranslationLink(str, false); } return str.replace(/[ _\']+/g, '.'); } function getFrame($name) { var $main_window = window; // 1. cycle through popups to get main window try { // will be error, when other site is opened in parent window var $i = 0, $opener, $is_frameset = false; do { if ( $i == 10 || $is_frameset ) { break; } // try to get popup window opener $opener = $main_window.opener; if ( !$opener ) { // not inside a popup, then we're inside a frameset already - get it once $is_frameset = true; $opener = $main_window.parent; } if ( $opener ) { $main_window = $opener; } $i++; } while ($opener); } catch (err) { // catch Access/Permission Denied error // alert('getFrame.Error: [' + err.description + ']'); return window; } var $frameset = $main_window.parent.frames; for ($i = 0; $i < $frameset.length; $i++) { - if ( $frameset[$i].name == $name ) { - return $frameset[$i]; + try { + if ($frameset[$i].name == $name) { + return $frameset[$i]; + } + } + catch ( e ) { + // Foreign domain frame detected. } } return $main_window.parent; } function ClearBrowserSelection() { if ( window.getSelection ) { // removeAllRanges will be supported by Opera from v 9+, do nothing by now var selection = window.getSelection(); if ( selection.removeAllRanges ) { // Mozilla & Opera 9+ // alert('clearing FF') window.getSelection().removeAllRanges(); } } else if ( document.selection && !is.opera ) { // IE // alert('clearing IE') document.selection.empty(); } } function reset_form(prefix, event, msg) { if ( confirm(RemoveTranslationLink(msg, true)) ) { submit_event(prefix, event) } } function cancel_edit(prefix, cancel_ev, save_ev, msg) { if ( (!Form || (Form && Form.HasChanged)) && confirm(RemoveTranslationLink(msg, true)) ) { submit_event(prefix, save_ev) } else { submit_event(prefix, cancel_ev) } } function execJS(node) { var bSaf = (navigator.userAgent.indexOf('Safari') != -1); var bOpera = (navigator.userAgent.indexOf('Opera') != -1); var bMoz = (navigator.appName == 'Netscape'); if ( !node ) { return; } /* IE wants it uppercase */ var st = node.getElementsByTagName('SCRIPT'); var strExec; for (var i = 0; i < st.length; i++) { if ( bSaf ) { strExec = st[i].innerHTML; st[i].innerHTML = ""; } else if ( bOpera ) { strExec = st[i].text; st[i].text = ""; } else if ( bMoz ) { strExec = st[i].textContent; st[i].textContent = ""; } else { strExec = st[i].text; st[i].text = ""; } try { var x = document.createElement("script"); x.type = "text/javascript"; /* In IE we must use .text! */ if ( (bSaf) || (bOpera) || (bMoz) ) { x.innerHTML = strExec; } else { x.text = strExec; } document.getElementsByTagName("head")[0].appendChild(x); } catch (e) { alert(e); } } } ; function NumberFormatter() { } NumberFormatter.ThousandsSep = '\''; NumberFormatter.DecimalSep = '.'; NumberFormatter.Parse = function (num) { if ( num == '' ) { return 0; } var $string = num.toString(); // we could have multiple thousand separators ! return parseFloat($string.replace(new RegExp(this.ThousandsSep, 'g'), '').replace(this.DecimalSep, '.')); } NumberFormatter.Format = function (num) { num += ''; x = num.split('.'); x1 = x[0]; x2 = x.length > 1 ? this.DecimalSep + x[1] : ''; var rgx = /(\d+)(\d{3})/; while (rgx.test(x1)) { x1 = x1.replace(rgx, '$1' + this.ThousandsSep + '$2'); } return x1 + x2; } function getDimensions(obj) { var style if ( obj.currentStyle ) { style = obj.currentStyle; } else { style = getComputedStyle(obj, ''); } padding = [parseInt(style.paddingTop), parseInt(style.paddingRight), parseInt(style.paddingBottom), parseInt(style.paddingLeft)] border = [parseInt(style.borderTopWidth), parseInt(style.borderRightWidth), parseInt(style.borderBottomWidth), parseInt(style.borderLeftWidth)] for (var i = 0; i < padding.length; i++) { if ( isNaN(padding[i]) ) { padding[i] = 0 } } for (var i = 0; i < border.length; i++) { if ( isNaN(border[i]) ) { border[i] = 0 } } var result = new Object(); result.innerHeight = obj.clientHeight - padding[0] - padding[2]; result.innerWidth = obj.clientWidth - padding[1] - padding[3]; result.padding = padding; result.borders = border; result.outerHeight = obj.clientHeight + border[0] + border[2]; result.outerWidth = obj.clientHeight + border[1] + border[3]; return result; } function findPos(obj, with_scroll) { /*var $offset = $(obj).offset(); return [$offset.left, $offset.top];*/ if ( !with_scroll ) { var with_scroll = false; } var curleft = curtop = 0; if ( obj.offsetParent ) { curleft = obj.offsetLeft - (with_scroll ? obj.scrollLeft : 0) curtop = obj.offsetTop - (with_scroll ? obj.scrollTop : 0) while (obj = obj.offsetParent) { curleft += obj.offsetLeft - (with_scroll ? obj.scrollLeft : 0) curtop += obj.offsetTop - (with_scroll ? obj.scrollTop : 0) } } return [curleft, curtop]; } function scrollbarWidth() { // Scrollbalken im Body ausschalten var $overflow_backup = document.body.style.overflow; document.body.style.overflow = 'hidden'; var width = document.body.clientWidth; // Scrollbalken document.body.style.overflow = 'scroll'; width -= document.body.clientWidth; // Der IE im Standardmode if ( !width ) { width = document.body.offsetWidth - document.body.clientWidth; } // urspr?ngliche Einstellungen document.body.style.overflow = $overflow_backup; return width; } function maximizeElement($selector, $max_height) { if ( $max_height === undefined ) { $max_height = false; } var $element = $($selector); if ( $element.length == 0 ) { return; } $element.width('100%'); var $container_id = $element.attr('id') + '_container'; var $container = $(jq('#' + $container_id)); if ( $container.length == 0 ) { // don't create same container twice // all <script> tags will be executed again after wrap method is called, so remove them to prevent that $('script', $element).remove(); $element.wrap('<div id="' + $container_id + '" style="position: relative; overflow: auto; width: 100%;"></div>'); $container = $(jq('#' + $container_id)); $(window).resize( function () { maximizeElement($selector, $max_height); } ); } var $offset_top = $container.offset().top; var $window_height = $(window).height(); var $height_left = $window_height - $offset_top; if ( ($max_height !== false) && ($max_height < $height_left) ) { $height_left = $max_height; } $height_left -= ($element.outerHeight() - $element.height()); $container.height($height_left); var $element_width = $(window).width() - ($element.outerWidth() - $element.width()); if ( $height_left < $element.height() ) { // needs vertical scrolling, so substract vertical scrollbar width $element_width -= scrollbarWidth(); } $element.width($element_width); } function addEvent(el, evname, func, traditional) { if ( traditional ) { eval('el.on' + evname + '=' + func); return; } if ( evname.match(/mousedown|mousemove|mouseup/) ) { $(el) .unbind(evname)// don't allow more then one .bind(evname, func); return; } if ( is.ie ) { el.attachEvent("on" + evname, func); } else { el.addEventListener(evname, func, true); } } /*function removeEvent(el, evname, func) { if (is.ie) { el.detachEvent('on' + evname, func); } else { el.removeEventListener(evname, func, true); } }*/ function addLoadEvent(func, wnd) { Application.setHook('m:OnAfterWindowLoad', func); } function replaceFireBug() { if ( 'object' !== typeof console ) { var names = [ 'log', 'debug', 'info', 'warn', 'error', 'assert', 'dir', 'dirxml', 'group', 'groupEnd', 'time', 'timeEnd', 'count', 'trace', 'profile', 'profileEnd' ]; window.console = {}; for ( var i = 0; i < names.length; ++i ) { window.console[names[i]] = function ($msg) { alert('Console object methods are not available outside Firefox/Chrome!' + "\n" + $msg); } } } } function runOnChange(elId) { var evt; var el = typeof(elId) == 'string' ? document.getElementById(elId) : elId if ( document.createEvent ) { evt = document.createEvent("HTMLEvents"); evt.initEvent("change", true, false); (evt) ? el.dispatchEvent(evt) : (el.onchange && el.onchange()); return; } if ( el.fireEvent ) { el.fireEvent('onchange'); } } function WatchClosing(win, $url) { window.setTimeout(function () { if ( win.closed ) { var req = Request.getRequest(); var $ajax_mark = ($url.indexOf('?') ? '&' : '?') + 'ajax=yes'; req.open('GET', $url + $ajax_mark, false); //!!!SYNCRONIOUS!!! REQUEST (3rd param = false!!!) req.send(null); } }, 2000 ) } function IterateUploaders($method) { if ( typeof UploadsManager != 'undefined' ) { UploadsManager.iterate($method); } } String.prototype.trim = function () { return this.replace(/\s*((\S+\s*)*)/, "$1").replace(/((\s*\S+)*)\s*/, "$1"); } String.prototype.toNumeric = function () { return parseInt(this.replace(/(auto|medium)/, '0px').replace(/[a-z]/gi, '')); } function jq($selector) { return $selector.replace(/(\[|\]|\.)/g, '\\$1'); } function setHelpLink($user_docs_url, $title_preset) { if ( !$user_docs_url ) { return; } $('#help_link', getFrame('head').document).attr('href', $user_docs_url + '/' + $title_preset); } // window management functions: function getWindowOpener($window) { // use this intead of "window.opener" if ( !$modal_windows ) { return $window.opener; } if ( $window.name == 'main' || $window.name == 'main_frame' ) { return null; } return getFrame('main').TB.findWindow($window.name, -1); } function window_close($close_callback) { // use this instead of "window.close();" if ( !$modal_windows ) { if ( $.isFunction($close_callback) ) { // use close callback, because iframe will be removed later in this method $close_callback(); } window.close(); return; } if ( window.name == 'main' ) { return; } if ( $close_callback !== undefined ) { return getFrame('main').TB.remove(null, $close_callback); } return getFrame('main').TB.remove(); } function onAfterWindowClose($redirect_url, $open_new_window, $force_skip_refresh) { if ( $open_new_window ) { var $ru = $redirect_url; // setTimeout allows to call method indirectly. Without it whole idea won't work 2nd time (try adding 2 relations one after another) setTimeout( function () { openSelector('adm', $ru + '&merge_opener_stack=1'); }, 200 ); return; } window.focus(); if ( !(($force_skip_refresh === true) || (typeof $skip_refresh != 'undefined' && $skip_refresh)) ) { window.location.href = rawurldecode($redirect_url); } } function get_control($mask, $field, $append, $prepend) { $append = $append !== undefined ? '_' + $append : ''; $prepend = $prepend !== undefined ? $prepend + '_' : ''; return document.getElementById($prepend + $mask.replace('#FIELD_NAME#', $field) + $append); } function rawurldecode(str) { if ( str.indexOf('?') != -1 ) { var $parts = str.split('?', 2); return $parts[0] + ($parts.length == 2 ? '?' + rawurldecode($parts[1]) : ''); } return decodeURIComponent((str + '').replace(/%(?![\da-f]{2})/gi, function () { // PHP tolerates poorly formed escape sequences return '%25'; })); } Array.prototype.each = function ($callback) { var $result = null; for (var $i = 0; $i < this.length; $i++) { $result = $callback.call(this[$i], $i); if ( $result === false ) { break; } } } function disable_categories($category_dropdown_id, $allowed_categories) { if ( $allowed_categories === true ) { return; } var $selected_category = false; var $categories = $(jq('#' + $category_dropdown_id)).children('option'); $categories.each( function () { var $me = $(this); var $category_id = parseInt($me.attr('value')); if ( !in_array($category_id, $allowed_categories) ) { if ( $me.prop('selected') ) { $selected_category = $me; } $me.prop('disabled', true); } } ); if ( $selected_category !== false && $allowed_categories.length > 0 ) { // when selected category became disabled -> select 1st available category $selected_category.prop('selected', false); $("option[value='" + $allowed_categories[0] + '"]', $categories).prop('selected', true); } } function crc32(str) { var table = '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F 63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC 51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E 7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D 806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA 11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F 30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D'; var crc = 0; var x = 0; var y = 0; crc = crc ^ (-1); for (var i = 0, iTop = str.length; i < iTop; i++) { y = ( crc ^ str.charCodeAt(i) ) & 0xFF; x = "0x" + table.substr(y * 9, 8); crc = ( crc >>> 8 ) ^ x; } return crc ^ (-1); } function ckeditors_apply_typekit() { if ( $typekit_id === undefined || $typekit_id.length == 0 ) { return; } CKEDITOR.on( 'instanceReady', function (ev) { setTimeout(function () { var $script = document.createElement('script'), $editor_instance = CKEDITOR.instances[ev.editor.name]; $script.src = '//use.typekit.com/' + $typekit_id + '.js'; $script.onload = function () { try { $editor_instance.window.$.Typekit.load(); } catch (e) {} }; $editor_instance.document.getHead().$.appendChild($script); }, 1000); } ); } Index: branches/5.3.x/core/admin_templates/js/jquery/thickbox/thickbox.css =================================================================== --- branches/5.3.x/core/admin_templates/js/jquery/thickbox/thickbox.css (revision 16394) +++ branches/5.3.x/core/admin_templates/js/jquery/thickbox/thickbox.css (revision 16395) @@ -1,185 +1,192 @@ /* ----------------------------------------------------------------------------------------------------------------*/ /* ---------->>> global settings needed for thickbox <<<-----------------------------------------------------------*/ /* ----------------------------------------------------------------------------------------------------------------*/ /* *{padding: 0; margin: 0;} */ /* ----------------------------------------------------------------------------------------------------------------*/ /* ---------->>> thickbox specific link and font settings <<<------------------------------------------------------*/ /* ----------------------------------------------------------------------------------------------------------------*/ #TB_window, .TB_window { font: 12px Arial, Helvetica, sans-serif; color: #333333; + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; + box-sizing: content-box; } #TB_secondLine { font: 10px Arial, Helvetica, sans-serif; color:#666666; } #TB_window a:link, .TB_window a:link {color: #666666;} #TB_window a:visited, .TB_window a:visited {color: #666666;} #TB_window a:hover, .TB_window a:hover {color: #000;} #TB_window a:active, .TB_window a:active {color: #666666;} #TB_window a:focus, .TB_window a:focus {color: #666666;} /* ----------------------------------------------------------------------------------------------------------------*/ /* ---------->>> thickbox settings <<<-----------------------------------------------------------------------------*/ /* ----------------------------------------------------------------------------------------------------------------*/ #TB_overlay { position: fixed; z-index: 1000100; top: 0px; left: 0px; height:100%; width:100%; } .TB_overlayMacFFBGHack {background: url(macFFBgHack.png) repeat;} .TB_overlayBG { background-color:#000; filter:alpha(opacity=75); -moz-opacity: 0.75; opacity: 0.75; } * html #TB_overlay { /* ie6 hack */ position: absolute; height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px'); } #TB_window, .TB_window { background: #ffffff; z-index: 1000102; color:#000000; display:none; border: 4px solid #525252; text-align:left; position: absolute; top: 10px; left: 10px; /*position: fixed; top:50%; left:50%;*/ } /** html #TB_window, * html .TB_window { /* ie6 hack */ position: absolute; margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop) + 'px'); }*/ #TB_window img#TB_Image, .TB_window img#TB_Image { display:block; margin: 15px 0 0 15px; border-right: 1px solid #ccc; border-bottom: 1px solid #ccc; border-top: 1px solid #666; border-left: 1px solid #666; } #TB_caption, .TB_caption { height:25px; padding:7px 30px 10px 25px; float:left; } +.TB_closeWindowButton > img:first-child { + vertical-align: bottom; +} + #TB_closeWindow, .TB_closeWindow { height:25px; padding:11px 25px 10px 0; float:right; } #TB_closeAjaxWindow, .TB_closeAjaxWindow { padding: 6px 10px 6px 0px; /*margin-bottom: 1px;*/ text-align: right; float: right; } #TB_ajaxWindowTitle, .TB_ajaxWindowTitle { float: left; padding: 6px 0px 6px 10px; /*margin-bottom: 1px;*/ font-weight: bold; } #TB_title, .TB_title { background-color:#e8e8e8; height:27px; } #TB_ajaxContent, .TB_ajaxContent { clear:both; padding:2px 15px 15px 15px; overflow:auto; text-align:left; line-height:1.4em; } #TB_ajaxContent.TB_modal, .TB_ajaxContent.TB_modal { padding:15px; } #TB_ajaxContent p, .TB_ajaxContent p { padding:5px 0px 5px 0px; } #TB_load { position: fixed; display:none; height:13px; width:208px; z-index: 1000103; top: 50%; left: 50%; margin: -6px 0 0 -104px; /* -height/2 0 0 -width/2 */ } * html #TB_load { /* ie6 hack */ position: absolute; margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop) + 'px'); } #TB_HideSelect { z-index: 1000099; position:fixed; top: 0; left: 0; background-color:#fff; border:none; filter:alpha(opacity=0); -moz-opacity: 0; opacity: 0; height:100%; width:100%; } * html #TB_HideSelect { /* ie6 hack */ position: absolute; height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px'); } #TB_iframeContent, .TB_iframeContent { clear:both; border:none; margin-bottom:-1px; /*margin-top:1px;*/ _margin-bottom:1px; } /* jQuery UI CSS */ -/* .ui-resizable { position: relative;} */ +.ui-resizable { position: absolute;} .ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 1099999; display: block;} .ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; } .ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0px; } .ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0px; } .ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0px; height: 100%; } .ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0px; height: 100%; } .ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; } .ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; } .ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; } .ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;} -.ui-draggable .TB_title { cursor: move; } \ No newline at end of file +.ui-draggable .TB_title { cursor: move; } Index: branches/5.3.x/core/admin_templates/js/template_manager.js =================================================================== --- branches/5.3.x/core/admin_templates/js/template_manager.js (revision 16394) +++ branches/5.3.x/core/admin_templates/js/template_manager.js (revision 16395) @@ -1,633 +1,633 @@ function TemplateManager ( $settings ) { this.languagePrefix = ''; this.pageId = 0; this.editUrl = ''; this.browseUrl = ''; this.saveLayoutUrl = ''; this.saveContentUrl = ''; this.editingMode = 0; // from {1 - browse, 2 - content, 3 - design} this.pageInfo = {editors: [], revisions: {}}; // information about page in "Content Mode" this.pageInfoUpdateTimer = null; this.revisionStatusMap = { 1: 'cms-revision-published', 2: 'cms-revision-pending', 0: 'cms-revision-declined' }; this._blocks = {}; this._blockOrder = []; this.inDrag = false; // don't process mouse over/out events while in drag mode $.extend(this, $settings); var $me = this; $(document).ready(function() { $me.init(); }); } TemplateManager.prototype.init = function () { this.searchBlocks(); if ( !this.editingMode ) { return; } // show special toolbar when in any of 3 browse modes var $template_manager = this, $head_frame = getFrame('head'), $extra_toolbar = $head_frame.$('div.front-extra-toolbar').clone(); // clone to keep original untouched $('a', $extra_toolbar).each(function () { // cut from end, because IE7 adds base_href to beginning of href var $editing_mode = $(this).attr('href'); $editing_mode = $editing_mode.substr($editing_mode.length - 1, 1); $(this).attr('href', $template_manager.browseUrl.replace('#EDITING_MODE#', $editing_mode)); if ( $editing_mode == $template_manager.editingMode ) { $(this).parents('td:first').addClass('button-active').prevAll('td:first').addClass('button-active'); } }); $head_frame.$('#extra_toolbar').html($extra_toolbar.html()); var $hover_effect = []; if ( $template_manager.editingMode > 1 ) { // all modes except for "Browse Mode" // $hover_effect.push('div.cms-section-properties-btn:first'); } if ( $template_manager.editingMode == 2 ) { // Content Mode // $hover_effect.push('div.cms-edit-btn'); // $hover_effect.push('div.admin-edit-btn'); // make all spans with phrases clickable $template_manager.setupEditTranslationButtons(document); // hide "Revision History" div on every body click (bubbled), but not a "toolbar button", that opens it $('body').click(function ($e) { var $target = $($e.target), $id = $target.attr('id'); if ( $id && ($id == 'div_history' || $target.parent().attr('id') == 'div_history') ) { return; } $('#cms-revision-dropdown:visible').hide(); }); } if ( $template_manager.editingMode == 3 ) { // Design Mode // $hover_effect.push('div.cms-save-layout-btn:first, div.cms-cancel-layout-btn:first'); $template_manager.renumberMovableElements(); $('div.movable-area').sortable({ placeholder: 'move-helper', handle: '.movable-header', items: 'div.movable-element', connectWith: ['div.movable-area'], tolerance: 'pointer', start: function (e, ui) { $template_manager.inDrag = true; ui.placeholder.height(ui.item.height()); }, stop: function (e, ui) { $template_manager.inDrag = false; // mouseout doesn't happen while in drag, so compensate it here var $header = $('.movable-header', ui.item); $('div.block-edit-block-btn-container', $header).mouseout(); }, change: function (e, ui) { $('div.cms-layout-btn-container').show(); } }); } // make requested elements fully visible on mouseover if ( $hover_effect.length ) { $($hover_effect.join(', ')) .mouseover(function (e) { $(this).css('opacity', 1); }) .mouseout(function (e) { $(this).css('opacity', 0.5); }); } // related to content revision control toolbar if ( $template_manager.revisionToolbarEnabled() ) { $template_manager.initRevisionToolbar(); } $('body') .bind('InlineEditor.Focus', function ($e, $editor_event) { $template_manager.inlineEditorFocus($editor_event); }) .bind('InlineEditor.Blur', function ($e, $editor_event) { $template_manager.inlineEditorBlur($editor_event); }); }; TemplateManager.prototype.revisionToolbarEnabled = function () { return $('#cms-revision-toolbar-layer').length == 1; }; TemplateManager.prototype.initRevisionToolbar = function () { var $template_manager = this; $('#cms-toggle-revision-toolbar').click(function ($e) { var $me = $(this); if ( $me.hasClass('opened') ) { var $height = $('#cms-revision-toolbar').height(); $('#cms-revision-toolbar-layer').animate({top: (-1) * $height}, 'fast'); $('#cms-editing-notice, #cms-revision-dropdown').hide(); setCookie('toolbar_hidden', 1); } else { $('#cms-revision-toolbar-layer').animate({top: 0}, 'fast'); setCookie('toolbar_hidden', 0); } $me.toggleClass('opened'); $e.preventDefault(); }); $('#cms-close-toolbar').click(function ($e) { var $height = $('#cms-revision-toolbar').height(); $('#cms-toggle-revision-toolbar').removeClass('opened'); $('#cms-revision-toolbar-layer').css('top', (-1) * $height); $('#cms-editing-notice, #cms-revision-dropdown').hide(); setCookie('toolbar_hidden', 1); $e.preventDefault(); }); $('#cms-close-editing-notice').click(function ($e) { $('#cms-editing-notice').hide(); $e.preventDefault(); }); $('body').on('click', '#cms-revision-dropdown .top .item', function ($e) { $('a:first', this).click(); $e.preventDefault(); }); $template_manager.requirePageInfoUpdate(); if ( !$.isEmptyObject($template_manager.pageInfo) ) { $template_manager.processPageInfo(); } }; TemplateManager.prototype.requirePageInfoUpdate = function ($now) { var $me = this; if ( $now === undefined || $now === false ) { clearTimeout(this.pageInfoUpdateTimer); this.pageInfoUpdateTimer = setInterval(function () { $me.requirePageInfoUpdate(true); }, 20 * 1000); // 20 seconds return; } $.getJSON( - $('#kf_revisions_' + this.pageId).attr('action') + '&events[page-revision]=OnGetInfo', + $('#kf_revisions_' + this.pageId).attr('action').replace('-d-', '-s-') + '&events[page-revision]=OnGetInfo', function ($data) { $me.pageInfo = $data; $me.processPageInfo(); } ); }; TemplateManager.prototype.processPageInfo = function () { this.updateCurrentRevision(); if ( $('#cms-toggle-revision-toolbar').hasClass('opened') ) { this.showEditingNotice(); } this.updateRevisionHistory(); }; TemplateManager.prototype.updateCurrentRevision = function () { var $me = this, $title = $('.revision-title', '#cms-current-revision-info'); $title.html(this.pageInfo.current_revision.title); $('.draft-saved', '#cms-current-revision-info').html(this.pageInfo.current_revision.saved); $.each(this.revisionStatusMap, function ($status, $class_name) { $title.toggleClass($class_name, $status === $me.pageInfo.current_revision.status); }); $.each(this.pageInfo.current_revision.toolbar_state, function ($button_name, $is_enabled) { a_toolbar.SetEnabled($button_name, $is_enabled); }); }; TemplateManager.prototype.showEditingNotice = function () { var $notice = $('#cms-editing-notice'); if ( this.pageInfo.editors.length ) { var $notice_span = $('span:first', $notice); if ( $notice_span.data('prev_editors') != this.pageInfo.editors.join(',') ) { // show notice, only when editors change occurs $notice_span.html(this.pageInfo.editors_warning).data('prev_editors', this.pageInfo.editors.join(',')); if ( $notice.is(':hidden') ) { $notice.fadeIn(); } } } else if ( $notice.is(':visible') ) { $notice.fadeOut(); } }; TemplateManager.prototype.updateRevisionHistory = function () { var $me = this, $revision_container = $('.top', '#cms-revision-dropdown'), $revision_mask = ' <div class="item">\ <span class="{CLASS}"><a href="{LINK}">{TITLE}</a> ({STATUS_LABEL})</span>\ <div class="cms-left">{DATETIME}</div>\ <div class="cms-right">{AUTHOR}</div>\ <div class="cms-clear"></div>\ </div>'; $revision_container.empty(); if ( $.isArray(this.pageInfo.revisions) ) { return; } $.each(this.pageInfo.revisions, function ($revision_number, $revision_info) { var $html = $revision_mask; $.each($revision_info, function ($field, $value) { $html = $html.replace(new RegExp('{' + $field.toUpperCase() + '}', 'g'), $value); }); $html = $html.replace(/{CLASS}/g, $me.revisionStatusMap[$revision_info.status]); if ( $revision_info['draft'] ) { $html = $html.replace(/{LINK}/g, $me.browseUrl.replace('#EDITING_MODE#', '2')); } else { $html = $html.replace(/{LINK}/g, $me.browseUrl.replace('#EDITING_MODE#', '2') + '&revision=' + $revision_number.substr(1)); } $revision_container.append($html); }); }; TemplateManager.prototype.inlineEditorFocus = function ($editor_event) { var $container = $($editor_event.editor.element.$).parent('.cms-edit-btn-container'); $container.removeClass('mode-inline-edit'); }; TemplateManager.prototype.inlineEditorBlur = function ($editor_event) { var $me = this, $element = $($editor_event.editor.element.$), $container = $element.parent('.cms-edit-btn-container'), $url_params = {}, $content_id = $container.data('content-id'); if ( $element.data('no_save_on_blur') === true ) { $element.data('no_save_on_blur', null); return; } $element.css('position', 'static'); $container.addClass('mode-inline-saving'); $url_params["content[" + $content_id + "][" + this.languagePrefix + "Content]"] = $editor_event.editor.getData(); $.post( this.saveContentUrl, $url_params, function ($data) { if ( $data != 'OK' ) { return; } if ( $me.revisionToolbarEnabled() ) { $me.requirePageInfoUpdate(true); } $element.css('position', 'relative'); $container.removeClass('mode-inline-saving').addClass('mode-inline-edit'); } ); }; TemplateManager.prototype.revisionToolbarClick = function ($button_name) { // console.log('button ', $button_name, ' clicked'); var $button_event_map = { 'select': 'OnSave', 'delete': 'OnDiscard', 'approve': 'OnPublish', 'decline': 'OnDecline' }; if ( $button_event_map[$button_name] !== undefined ) { $form_name = 'kf_revisions_' + this.pageId; submit_event('page-revision', $button_event_map[$button_name]); return; } switch ( $button_name ) { case 'preview': var $url = this.browseUrl.replace('#EDITING_MODE#', 0).replace(/&(admin|editing_mode)=[\d]/g, ''); window.open($url + '&preview=1'); break; case 'history': $('#cms-revision-dropdown').toggle(); break; } }; TemplateManager.prototype.setupEditTranslationButtons = function ($container) { $("span[name='cms-translate-phrase']", $container).each(function() { var $me = $(this), $parent_link = $me.parents('a:first'); if ( $parent_link.length == 0 ) { // span in not inside "a" tag $me.prepend('<div class="cms-edit-btn"><div class="cms-btn-text">Edit</div></div>'); $('div.cms-edit-btn:first', $me).click(TemplateManager.prototype.translatePhrase); $me.dblclick(function ($e) { $('div.cms-edit-btn:first', this).click(); $e.preventDefault(); }); var $effect_element = $me; } else { // span is inside "a" tag var $clone = $me.clone(); $clone.empty().attr('title', ''); // in case if "a" tag is "display: block", then make "span" the same $clone.css('display', $parent_link.css('display')); $parent_link.html($me.html()).wrap($clone); $parent_link.before('<div class="cms-edit-btn" title="' + $me.attr('title') + '"><div class="cms-btn-text">Edit</div></div>'); $parent_link.prev('div.cms-edit-btn:first').click(TemplateManager.prototype.translatePhrase); var $effect_element = $parent_link.parents("span[name='cms-translate-phrase']:first"); } $effect_element .mouseover(function($e) { $('div.cms-edit-btn', this).css('display', 'inline'); }) .mouseout(function($e) { $('div.cms-edit-btn', this).hide(); }); }); }; TemplateManager.prototype.translatePhrase = function ($e) { var $translate_url = $(this).parents("span[name='cms-translate-phrase']:first").attr('href'); if ( $translate_url.match(/javascript:(.*)/) ) { eval(RegExp.$1); } else { window.location.href = $translate_url; } $e.preventDefault(); }; TemplateManager.prototype.renumberMovableElements = function () { var $area_index = 0; // 1. dynamically assign IDs to all movable elements $('div.movable-area').each(function () { var $element_index = 0; $('div.movable-element', this).each(function () { $(this).attr('id', 'target_order_a' + $area_index + 'e' + $element_index); $element_index++; }); $area_index++; }); }; TemplateManager.prototype.saveLayout = function () { // prepare order string var $sort_order = []; $('div.movable-area').each(function ($area_index) { var $order = $(this).sortable('serialize').replace(/target_order\[\]/g, 'target_order[' + $area_index + '][]'); if ( $order ) { $sort_order.push($order); } }); $sort_order = $sort_order.join('&'); // save order string var $me = this; var $settings = { url: this.saveLayoutUrl + '&' + $sort_order + '&width=200&height=70&modal=true', caption: 'Layout Saving Result', onDataReceived: function ($data) { var $message = ''; if ( $data == 'OK' ) { $message = 'New Layout Saved'; $('div.cms-layout-btn-container').hide(); $me.renumberMovableElements(); } else { $message = 'Failed to Save New Layout'; } $data = '<div style="text-align: center;">' + $message + '<br/><br/><input type="button" class="button" value="OK" onclick="TB.remove();"/></div>'; return $data; } }; TB.setWindowTitle(window.top.document.title.replace(main_title + ' :: ', '')); TB.show($settings); }; TemplateManager.prototype.cancelLayout = function () { window.location.href = window.location.href; }; TemplateManager.prototype.onBtnClick = function ($e, $element) { var $id = $element.id.replace(/_btn$/, ''); var $block_info = this._blocks[$id]; var $url = this.editUrl.replace('#BLOCK#', $block_info.block_name + ':' + $block_info.function_name).replace('#EVENT#', 'OnLoadBlock'); direct_edit('theme-file', $url); $e.stopPropagation(); }; TemplateManager.prototype.onMouseOver = function ($e, $element) { if ( this.inDrag ) { return; } $element = $($element); if ( $element.hasClass('block-edit-design-btn-container') ) { $($element).addClass('block-edit-design-btn-container-over'); var $button_group = $('div.cms-edit-design-btn-container:first', $element); if ( $button_group.length ) { $button_group.show(); } else { $('.cms-edit-design-btn:first', $element).show(); } } else { $($element).addClass('block-edit-block-btn-container-over'); $('.cms-edit-block-btn:first', $element).show(); } $e.stopPropagation(); }; TemplateManager.prototype.onMouseOut = function ($e, $element) { if ( this.inDrag ) { return; } $element = $($element); if ( $element.hasClass('block-edit-design-btn-container') ) { $($element).removeClass('block-edit-design-btn-container-over'); var $button_group = $('div.cms-edit-design-btn-container:first', $element); if ( $button_group.length ) { $button_group.hide(); } else { $('.cms-edit-design-btn:first', $element).hide(); } } else { $($element).removeClass('block-edit-block-btn-container-over'); $('.cms-edit-block-btn:first', $element).hide(); } $e.stopPropagation(); }; TemplateManager.prototype.searchBlocks = function () { var $design_containers = $('div.block-edit-design-btn-container'); var $block_containers = $('div.block-edit-block-btn-container'); $design_containers.each(function () { var $block_container = $('div.block-edit-block-btn-container:first', this); if ( $block_container.length ) { $block_containers = $block_containers.not($block_container); // place "Edit Block" button near "Edit Design" button var $edit_design_btn = $('.cms-edit-design-btn:first', this); var $edit_block_btn = $('.cms-edit-block-btn:first', $block_container); $edit_design_btn .wrap('<div class="cms-edit-design-btn-container"></div>') .before($edit_block_btn.clone()); $edit_block_btn.remove(); // make "hint" from "Edit Block" button container main $(this).attr('title', $block_container.attr('title')); $block_container.attr('title', ''); TemplateManager.prototype.registerBlock.call(aTemplateManager, $block_container.get(0), ['hover']); } TemplateManager.prototype.registerBlock.call(aTemplateManager, this, ['dblclick']); }); $block_containers.each(function() { TemplateManager.prototype.registerBlock.call(aTemplateManager, this); }); /*$('div').each (function () { *//*var $id = $(this).attr('id'); if ( !$id || $id.match(/parser_block\[.*\].*_btn$/) || !$id.match(/parser_block\[.*\]/) ) { // skip other divs return true; }*//* TemplateManager.prototype.registerBlock.call(aTemplateManager, this); });*/ }; TemplateManager.prototype.registerBlock = function ($element, $skip_events) { var $params = $element.getAttribute('params').split(':'); this._blocks[$element.id] = { block_name: $params[0], function_name: $params[1] }; var $btn = document.getElementById($element.id + '_btn'); $($btn).click(function (ev) { TemplateManager.prototype.onBtnClick.call(aTemplateManager, ev, this); }); if ( $skip_events === undefined ) { $skip_events = []; } if ( !in_array('dblclick', $skip_events) ) { $($element).dblclick(function (ev) { TemplateManager.prototype.onBtnClick.call(aTemplateManager, ev, this); }); } if ( !in_array('hover', $skip_events) ) { $($element) .mouseover(function (ev) { TemplateManager.prototype.onMouseOver.call(aTemplateManager, ev, this); }) .mouseout(function (ev) { TemplateManager.prototype.onMouseOut.call(aTemplateManager, ev, this); }); } this._blockOrder.push($element.id); -}; \ No newline at end of file +}; Index: branches/5.3.x/core/admin_templates/js/grid.js =================================================================== --- branches/5.3.x/core/admin_templates/js/grid.js (revision 16394) +++ branches/5.3.x/core/admin_templates/js/grid.js (revision 16395) @@ -1,533 +1,532 @@ var $GridManager = new GridManager(); function GridManager () { this.Grids = Grids; // get all from global variable, in future replace all references to it with $GridManager.Grids this.AlternativeGrids = new Array(); } GridManager.prototype.AddAlternativeGrid = function ($source_grid, $destination_grid, $reciprocal) { if ($source_grid == $destination_grid) { return false; } if (typeof(this.AlternativeGrids[$source_grid]) == 'undefined') { // alternative grids not found, create empty list this.AlternativeGrids[$source_grid] = new Array(); } if (!in_array($destination_grid, this.AlternativeGrids[$source_grid])) { // alternative grids found, check if not added already this.AlternativeGrids[$source_grid].push($destination_grid); } if ($reciprocal) { this.AddAlternativeGrid($destination_grid, $source_grid); } } GridManager.prototype.ClearAlternativeGridsSelection = function ($source_prefix) { if (!this.AlternativeGrids[$source_prefix]) return false; var $i = 0; var $destination_prefix = ''; while ($i < this.AlternativeGrids[$source_prefix].length) { $destination_prefix = this.AlternativeGrids[$source_prefix][$i]; if (this.Grids[$destination_prefix]) { // alternative grid set, but not yet loaded by ajax this.Grids[$destination_prefix].ClearSelection(); } $i++; } } GridManager.prototype.CheckDependencies = function ($prefix) { if (typeof(this.Grids[$prefix]) != 'undefined') { this.Grids[$prefix].CheckDependencies('GridManager.CheckDependencies'); } } function GridItem(grid, an_element, cb, item_id, class_on, class_off) { this.Grid = grid; this.selected = false; this.id = an_element.id; this.ItemId = item_id; this.sequence = parseInt(an_element.getAttribute('sequence')); // this.class_on = class_on; if (class_off == ':original') { this.class_off = an_element.className; } else this.class_off = class_off; if (class_on.match(/(.*):(.*)/)) { var even = RegExp.$2; var odd = RegExp.$1; this.class_on = this.class_off.match(/even/) ? even : odd } else { this.class_on = class_on } this.HTMLelement = an_element; if (document.getElementById('left_'+an_element.id)) { this.LeftElement = document.getElementById('left_'+an_element.id); } else { this.LeftElement = false; } this.CheckBox = cb; this.value = this.ItemId; this.ItemType = 11; } GridItem.prototype.Init = function () { this.HTMLelement.GridItem = this; this.HTMLelement.onclick = function(ev) { this.GridItem.Click(ev); }; this.HTMLelement.ondblclick = function(ev) { this.GridItem.DblClick(ev); } if ( this.LeftElement ) { this.LeftElement.GridItem = this; this.LeftElement.onclick = function(ev) { this.GridItem.Click(ev); }; this.LeftElement.ondblclick = function(ev) { this.GridItem.DblClick(ev); } } if ( isset(this.CheckBox) ) { this.CheckBox.GridItem = this; this.CheckBox.onclick = function(ev) { this.GridItem.cbClick(ev); }; this.CheckBox.ondblclick = function(ev) { this.GridItem.DblClick(ev); } } // if ( this.Grid.MouseOverClass ) { addEvent(this.HTMLelement, 'mouseover', function(ev) { this.GridItem.MouseOver(ev)}, true); // true for traditional event model addEvent(this.HTMLelement, 'mouseout', function(ev) { this.GridItem.MouseOut(ev)}, true); if ( this.LeftElement ) { addEvent(this.LeftElement, 'mouseover', function(ev) { this.GridItem.MouseOver(ev)}, true); addEvent(this.LeftElement, 'mouseout', function(ev) { this.GridItem.MouseOut(ev)}, true); } // } } GridItem.prototype.AddClass = function(elem, class_name) { var cur_classes = elem.className; // if (cur_classes.match(class_name)) return; elem.className = cur_classes+' '+class_name; // console.log('added %s to %s resulted %s', class_name, elem.id, elem.className) } GridItem.prototype.RemoveClass = function(elem, class_name) { var cur_classes = elem.className; // console.log('looking for %s in %s', class_name, cur_classes) if (!cur_classes.match(class_name)) return; cur_classes = cur_classes.replace(class_name, ''); cur_classes = cur_classes.replace(/[ ]+/, ' '); elem.className = cur_classes; // console.log('removed %s from %s resulted %s', class_name, elem.id, elem.className) } GridItem.prototype.DisableClicking = function () { this.HTMLelement.onclick = function(ev) { return false; }; this.HTMLelement.ondblclick = function(ev) { return false; } if ( this.LeftElement ) { this.LeftElement.onclick = function(ev) { return false; }; this.LeftElement.ondblclick = function(ev) { return false; } } if ( isset(this.CheckBox) ) { this.CheckBox.onclick = function(ev) { return false; }; } } GridItem.prototype.Select = function () { if (this.selected) return; this.selected = true; this.HTMLelement.setAttribute('_row_selected', 1) this.AddClass(this.HTMLelement, this.class_on); if ( this.LeftElement ) { this.LeftElement.setAttribute('_row_selected', 1) this.AddClass(this.LeftElement, this.class_on); } if ( isset(this.CheckBox) ) { this.CheckBox.checked = true; } this.Grid.LastSelectedId = this.ItemId; this.Grid.SelectedCount++; // this is for in-portal only (used in relation select) LastCheckedItem = this; if (typeof (this.Grid.OnSelect) == 'function' ) { this.Grid.OnSelect(this.ItemId); } } GridItem.prototype.UnSelect = function ( force ) { if ( !this.selected && !force) return; this.selected = false; this.HTMLelement.removeAttribute('_row_selected') this.RemoveClass(this.HTMLelement, this.class_on); if ( this.LeftElement ) { this.LeftElement.removeAttribute('_row_selected') this.RemoveClass(this.LeftElement, this.class_on); } if ( isset(this.CheckBox) ) { this.CheckBox.checked = false; } this.Grid.SelectedCount--; if (typeof (this.Grid.OnUnSelect) == 'function' ) { this.Grid.OnUnSelect(this.ItemId); } this.Grid.LastSelectedId = null; } GridItem.prototype.ClearBrowserSelection = function() { ClearBrowserSelection(); } GridItem.prototype.Click = function (ev) { //this.ClearBrowserSelection(); this.Grid.ClearAlternativeGridsSelection('GridItem.Click'); var e = !is.ie ? ev : window.event; if (e.shiftKey && !this.Grid.RadioMode) { this.ClearBrowserSelection(); this.Grid.SelectRangeUpTo(this.sequence); } else { if (e.ctrlKey && !this.Grid.RadioMode) { this.Toggle() } else { if (!(this.Grid.RadioMode && this.Grid.LastSelectedId == this.ItemId && this.selected)) { // don't clear selection if item same as current is selected if (!this.Grid.StickySelection) { this.Grid.ClearSelection(null,'GridItem.Click'); } else { if (this.Grid.LastSelectedId == this.ItemId) { return; } } this.Toggle(); } } } this.Grid.CheckDependencies('GridItem.Click'); - e.cancelBubble = true; } GridItem.prototype.cbClick = function (ev) { var e = is.ie ? window.event : ev; if (this.Grid.RadioMode) this.Grid.ClearSelection(null,'GridItem.cbClick'); this.Grid.ClearAlternativeGridsSelection('GridItem.cbClick'); this.Toggle(); this.Grid.CheckDependencies('GridItem.cbClick'); e.cancelBubble = true; } GridItem.prototype.DblClick = function (ev) { var e = is.ie ? window.event : ev; this.Grid.Edit(); } GridItem.prototype.MouseOver = function (ev) { this.HTMLelement.setAttribute('_row_highlighted', 1) this.AddClass(this.HTMLelement, this.Grid.MouseOverClass); // if (this.Grid.MouseOverClass) {this.HTMLelement.className = this.Grid.MouseOverClass;} if ( this.LeftElement ) { this.LeftElement.setAttribute('_row_highlighted', 1) // if (this.Grid.MouseOverClass) this.LeftElement.className = this.Grid.MouseOverClass; this.AddClass(this.LeftElement, this.Grid.MouseOverClass); // this.LeftElement.className = this.LeftElement.className; // this is to make IE re-render the element } } GridItem.prototype.MouseOut = function (ev) { this.HTMLelement.removeAttribute('_row_highlighted') this.RemoveClass(this.HTMLelement, this.Grid.MouseOverClass); // if (this.Grid.MouseOverClass) {this.HTMLelement.className = this.selected ? this.class_on : this.class_off;} if ( this.LeftElement ) { this.LeftElement.removeAttribute('_row_highlighted') this.RemoveClass(this.LeftElement, this.Grid.MouseOverClass); // if (this.Grid.MouseOverClass) this.LeftElement.className = this.selected ? this.class_on : this.class_off; // this.LeftElement.className = this.LeftElement.className; // this is to make IE re-render the element } } GridItem.prototype.Toggle = function () { if (this.selected) this.UnSelect() else { this.Grid.LastSelectedSequence = this.sequence; this.Select(); } } GridItem.prototype.FallsInRange = function (from, to) { return (from <= to) ? (this.sequence >= from && this.sequence <= to) : (this.sequence >= to && this.sequence <= from); } function Grid(prefix, class_on, class_off, dbl_click, toolbar) { this.prefix = prefix; this.class_on = class_on; this.class_off = class_off; this.Items = {}; this.LastSelectedSequence = 1; this.LastSelectedId = null; this.DblClick = dbl_click; this.ToolBar = toolbar; this.SelectedCount = 0; this.AlternativeGrids = new Array(); this.DependantButtons = new Array(); this.RadioMode = false; this.MouseOverClass = false; // K3-style sticky selection, selection an item does not unselect currently selected // even w/o Ctrl key pressed this.StickySelection = false; } Grid.prototype.EnableRadioMode = function() { this.RadioMode = true; this.StickySelection = false; } Grid.prototype.AddItem = function( an_item ) { this.Items[an_item.id] = an_item; } Grid.prototype.AddItemsByIdMask = function ( tag, mask, cb_mask ) { var $item_id=0; elements = document.getElementsByTagName(tag.toUpperCase()); for (var i=0; i < elements.length; i++) { if ( typeof(elements[i].id) == 'undefined') { continue; } if ( !elements[i].id.match(mask)) continue; $item_id=RegExp.$1; cb_name = cb_mask.replace('$$ID$$',$item_id); cb = document.getElementById(cb_name); if (typeof(cb) == 'undefined') alert ('No Checkbox defined for item '+elements[i].id); this.AddItem( new GridItem( this, elements[i], cb, $item_id, this.class_on, this.class_off ) ); } } Grid.prototype.InitItems = function() { for (var i in this.Items) { this.Items[i].Init(); } this.ClearSelection( true,'Grid.InitItems' ); var a_Grid = this; addEvent(document, 'keyup', function(ev) { var e = !is.ie ? ev : window.event; switch (e.keyCode) { case 65: if (!e.ctrlKey) break; a_Grid.SelectAll(); ClearBrowserSelection() // window.setTimeout(ClearBrowserSelection, 500); break; case 27: a_Grid.ClearSelection(); break; case 33: case 37: //alert('<-') // go to prev page here break; case 34: case 39: // alert('->') // go to next page here break; case 88: ClearBrowserSelection(); break; } }); } Grid.prototype.DisableClicking = function() { for (var i in this.Items) { this.Items[i].DisableClicking(); } this.ClearSelection( true, 'Grid.DisableClicking' ); } Grid.prototype.ClearSelection = function( force, called_from ) { // alert('selection clear. force: '+force+'; called_from: '+called_from); if (typeof(force) == 'undefined') force = false; if (this.CountSelected() == 0 && !force) return; for (var i in this.Items) { this.Items[i].UnSelect(force); } this.SelectedCount = 0; this.CheckDependencies('Grid.ClearSelection'); } Grid.prototype.GetSelected = function() { var $ret = new Array(); for (var i in this.Items) { if (this.Items[i].selected) { $ret[$ret.length] = this.Items[i].ItemId; } } return $ret; } Grid.prototype.SetSelected = function($ids) { $ids = $ids.split(','); for (var i in this.Items) { if (in_array(this.Items[i].ItemId, $ids)) { this.Items[i].Select(); } } } Grid.prototype.InvertSelection = function() { for (var i in this.Items) { if( this.Items[i].selected ) { this.Items[i].UnSelect(); } else { this.Items[i].Select(); } } this.CheckDependencies('Grid.InvertSelection'); } Grid.prototype.SelectAll = function() { for (var i in this.Items) { this.Items[i].Select(); } this.CheckDependencies('Grid.SelectAll'); this.ClearAlternativeGridsSelection('Grid.SelectAll'); } Grid.prototype.SelectRangeUpTo = function( last_sequence ) { for (var i in this.Items) { if (this.Items[i].FallsInRange(this.LastSelectedSequence, last_sequence)) { this.Items[i].Select(); } } } Grid.prototype.CountSelected = function () { return this.SelectedCount; } Grid.prototype.Edit = function() { if ( this.CountSelected() == 0 ) return; this.DblClick(this.prefix); } Grid.prototype.SetDependantToolbarButtons = function($buttons, $direct, $mode) { if (!isset($direct)) $direct = true; // direct (use false for invert mode) if (!isset($mode)) $mode = 1; // enable/disable (use 2 for show/hide mode) for (var i = 0; i < $buttons.length; i++) { this.DependantButtons.push(new Array($buttons[i], $direct, $mode)); } //this.DependantButtons = buttons; this.CheckDependencies('Grid.SetDependantToolbarButtons'); } Grid.prototype.CheckDependencies = function($called_from) { var enabling = (this.CountSelected() > 0); for (var i = 0; i < this.DependantButtons.length; i++) { if (this.DependantButtons[i][2] == 1) { this.ToolBar.SetEnabled(this.DependantButtons[i][0], enabling == this.DependantButtons[i][1]); } else { this.ToolBar.SetVisible(this.DependantButtons[i][0], enabling == this.DependantButtons[i][1]); } } } Grid.prototype.ClearAlternativeGridsSelection = function (called_from) { $GridManager.ClearAlternativeGridsSelection(this.prefix); } Grid.prototype.AddAlternativeGrid = function (alt_grid, reciprocal) { var $dst_prefix = typeof('alt_grid') == 'string' ? alt_grid : alt_grid.prefix; $GridManager.AddAlternativeGrid(this.prefix, $dst_prefix, reciprocal); } Grid.prototype.FirstSelected = function () { min_sequence = null; var res = null for (var i in this.Items) { if (!this.Items[i].selected) continue; if (min_sequence == null) min_sequence = this.Items[i].sequence; if (this.Items[i].sequence <= min_sequence) { res = this.Items[i].ItemId; min_sequence = this.Items[i].sequence; } } return res; } Grid.prototype.each = function($callback) { var $result = null; for (var i in this.Items) { $result = $callback.call(this.Items[i], i); if ($result === false) { break; } } -} \ No newline at end of file +} Index: branches/5.3.x/core/admin_templates/js/cron.js =================================================================== --- branches/5.3.x/core/admin_templates/js/cron.js (revision 16394) +++ branches/5.3.x/core/admin_templates/js/cron.js (revision 16395) Property changes on: branches/5.3.x/core/admin_templates/js/cron.js ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +LF \ No newline at end of property Index: branches/5.3.x/core/admin_templates/js/toolbar.js =================================================================== --- branches/5.3.x/core/admin_templates/js/toolbar.js (revision 16394) +++ branches/5.3.x/core/admin_templates/js/toolbar.js (revision 16395) @@ -1,415 +1,426 @@ function ToolBarButton(title, alt, onclick, $hidden, prefix) { this.Title = title || ''; this.TranslateLink = false; this.CheckTitleModule(); this.Alt = RemoveTranslationLink(alt || '', false); if (this.Alt != alt) { this.TranslateLink = alt || ''; this.TranslateLink = this.TranslateLink.replace(/<a href="(.*?)".*>(.*?)<\/a>/g, '$1'); } if (this.Alt.match(/(.*)::(.*)/)) { this.Alt = RegExp.$1; this.Label = RegExp.$2 } else { this.Label = this.Alt; } if ( $.isFunction(onclick) ) { this.onClick = onclick; } else { this.onClick = function() { this.runFunction(this.Title); } } this.imgObject = null; this.Enabled = true; this.Hidden = $hidden ? true : false; this.ToolBar = null; this.Prefix = prefix ? prefix : ''; this.Separator = false; // all buttons are read-only until page is fully loaded! this.ReadOnly = true; } ToolBarButton.prototype.CheckTitleModule = function() { if (this.Title.match(/([^:]+):(.*)$/)) { // has module set directly this.Title = RegExp.$2; this.Module = RegExp.$1.toLowerCase(); if (this.Module == 'in-portal') { this.Module = 'core'; } } else { // use default module this.Module = 'core'; } } ToolBarButton.prototype.IconsPath = function($module_path) { if ( $module_path === undefined ) { $module_path = this.Module; } if ( $module_path != 'core' ) { $module_path = 'modules/' + $module_path; } return this.ToolBar.IconPath.replace('#MODULE#', $module_path) + 'toolbar/'; } ToolBarButton.prototype.GetHTML = function() { var add_style = this.ToolBar.ButtonStyle ? 'style="' + this.ToolBar.ButtonStyle + '"' : '', o = '<div class="toolbar-button" id="' + this.GetToolID('div') + '" ' + add_style + '>'; o += '<img class="' + this.Module + '-toolbar-sprite" id="' + this.GetToolID() + '" width="' + this.ToolBar.IconSize.w + '" height="' + this.ToolBar.IconSize.h + '" src="' + this.IconsPath('core') + '../spacer.gif" title="' + this.Alt + ' ">'; if ( this.ToolBar.UseLabels ) { o += '<br/>' + this.Label; } o += '</div>' return o; } ToolBarButton.prototype.GetToolID = function(item) { if ( item === undefined ) { item = 'tool'; } if ( this.Prefix == '' ) { return item == 'tool' ? this.Module + '-tb-' + this.Title : item + '_' + this.Title; } return item + '_[' + this.Prefix + '][' + this.Title + ']' } ToolBarButton.prototype.Init = function() { img = document.getElementById(this.GetToolID()); this.imgObject = img; this.Container = document.getElementById(this.GetToolID('div')) ? document.getElementById(this.GetToolID('div')) : false; this.Container.btn = this; img.btn = this; this.SetOnMouseOver(); this.SetOnMouseOut(); this.SetOnClick(); this.SetOnRightClick() if (this.Hidden) this.Hide(); } ToolBarButton.prototype.EditTitle = function() { if ( this.TranslateLink === false || this.ReadOnly ) { return true; } var $link = '', $links = this.TranslateLink.split('::'); // edit one phrase at a time for ( var $i = 0; $i < $links.length; $i++ ) { $link = htmlspecialchars_decode($links[$i]); if ( $link.match(/^javascript:(.*)/) ) { eval(RegExp.$1); break; } else if ( $link.match(/^http:\/\/(.*)/) ) { window.location.href = 'http://' + RegExp.$1; break; } } return false; } ToolBarButton.prototype.SetOnMouseOver = function() { this.Container.onmouseover = function() { $(this.btn.imgObject).addClass('hover'); this.className = 'toolbar-button-over'; }; } ToolBarButton.prototype.SetOnMouseOut = function() { this.Container.onmouseout = function() { $(this.btn.imgObject).removeClass('hover'); this.className = 'toolbar-button'; }; } ToolBarButton.prototype.runFunction = function($name) { if ( window[$name] !== undefined && $.isFunction( window[$name] ) ) { window[$name](); } } ToolBarButton.prototype.SetOnClick = function() { // we have SetOnMouseOut for this ??? /*this.Container.onmouseout = function() { this.btn.imgObject.src = this.btn.IconsPath() + this.btn.ToolBar.IconPrefix + this.btn.Title + '.gif'; };*/ this.Container.inClick = false; if (typeof(this.onClick) != 'function') { this.Container.onclick = function() { if (this.inClick || this.btn.ReadOnly) return; this.inClick = true; this.runFunction(this.btn.Title); this.inClick = false; } } else { this.Container.onclick = function() { if (this.inClick || this.btn.ReadOnly) return; this.inClick = true; this.btn.onClick(); this.inClick = false; } } // the following lines are correct, as long as mozilla understands 'pointer', but IE 'hand', // do not change the order of these lines! if (is.ie6up || is.gecko) { this.Container.style.cursor = 'pointer'; } else { // somehow set cursor hand for IE 5/ 5.5 // this.imgObject.style = 'cursor: hand'; } } ToolBarButton.prototype.SetOnRightClick = function() { this.Container.oncontextmenu = function() { return this.btn.EditTitle(); } } ToolBarButton.prototype.Disable = function() { if ( !this.Enabled ) return; $(this.imgObject).addClass('disabled'); this.Container.onmouseover = null; this.Container.onmouseout = null; this.Container.onclick = null; this.Container.style.cursor = 'default'; this.Enabled = false; this.Container.className = 'toolbar-button-disabled' } ToolBarButton.prototype.Enable = function() { if (this.Enabled) return; $(this.imgObject).removeClass('hover disabled'); this.SetOnMouseOver(); this.SetOnMouseOut(); this.SetOnClick(); this.Enabled = true; this.Container.className = 'toolbar-button' } ToolBarButton.prototype.Hide = function() { this.Container.style.display = 'none'; this.Hidden = true; } ToolBarButton.prototype.Show = function() { this.Container.style.display = ''; this.Hidden = false; } /* ----------- */ function ToolBarSeparator(title, $hidden) //extends ToolBarButton { this.Title = title; this.Hidden = $hidden ? true : false; this.Separator = true; } ToolBarSeparator.prototype = new ToolBarButton; ToolBarSeparator.prototype.GetHTML = function() { var add_style = this.ToolBar.ButtonStyle ? 'style="' + this.ToolBar.ButtonStyle + '"' : ''; var padding = this.ToolBar.UseLabels ? '12px' : '2px' return '<div id="' + this.GetToolID('div') + '" class="toolbar-button" style="padding-top: ' + padding + '; height: 32px;" ' + add_style + '><img id="' + this.GetToolID() + '" src="' + this.IconsPath() + 'tool_divider.gif"></div>'; } ToolBarSeparator.prototype.Init = function() { img = document.getElementById(this.Module + '-tb-' + this.Title); this.Container = document.getElementById(this.GetToolID('div')) ? document.getElementById(this.GetToolID('div')) : false; this.imgObject = img; img.btn = this; if (this.Hidden) { this.Hide(); } } ToolBarSeparator.prototype.Enable = function() { } ToolBarSeparator.prototype.Disable = function() { } /* ----------- */ -function ToolBarMarkup(title, html) //extends ToolBarButton +function ToolBarMarkup(title, html, $hidden) //extends ToolBarButton { this.Title = title; + this.Hidden = $hidden ? true : false; this.HTML = html; } ToolBarMarkup.prototype = new ToolBarButton; ToolBarMarkup.prototype.GetHTML = function() { - return this.HTML; + var add_style = this.ToolBar.ButtonStyle ? 'style="' + this.ToolBar.ButtonStyle + '"' : ''; + var padding = this.ToolBar.UseLabels ? '12px' : '2px'; + + return '<div id="' + this.GetToolID('div') + '" class="toolbar-button" style="padding-top: ' + padding + '; height: 32px;" ' + add_style + '>' + this.HTML + '</div>'; +} + +ToolBarMarkup.prototype.Init = function() { + this.Container = document.getElementById(this.GetToolID('div')) ? document.getElementById(this.GetToolID('div')) : false; + + if (this.Hidden) { + this.Hide(); + } } -ToolBarMarkup.prototype.Init = function() { } ToolBarMarkup.prototype.Enable = function() { } ToolBarMarkup.prototype.Disable = function() { } /* ----------- */ function ToolBar(icon_prefix, $module, $image_path) { this.Module = $module ? $module : 'core'; this.IconPrefix = icon_prefix ? icon_prefix : 'tool_'; this.IconSize = {w:32,h:32}; this.Buttons = {}; this.UseLabels = typeof($use_toolbarlabels) != 'undefined' ? $use_toolbarlabels : false; if ( $image_path !== undefined ) { this.IconPath = $image_path; } else if ( typeof(img_path) != 'undefined' ) { this.IconPath = img_path; } else { this.IconPath = ''; // alert('error: toolbar image path not set'); } } ToolBar.prototype.AddButton = function(a_button) { if ($visible_toolbar_buttons === true || in_array(a_button.Title, $visible_toolbar_buttons) || a_button.Separator) { a_button.ToolBar = this; this.Buttons[a_button.Title] = a_button; } } ToolBar.prototype._removeSeparators = function() { var $i, $buttons = [], $separator = false; // 1. convert to 0-based array && filter out trailing separators from the middle $.each(this.Buttons, function ($button_title, $button) { if ( $separator && $button.Separator ) { // duplicate separator - skip return true; } // remember separator status $buttons.push($button); $separator = $button.Separator; }); // 2. remove first separator if ( $buttons.length && $buttons[0].Separator ) { $buttons.shift(); } // 3. remove last separator if ( $buttons.length && $buttons[$buttons.length - 1].Separator ) { $buttons.pop(); } // 4. convert to associative array this.Buttons = {}; for ($i = 0; $i < $buttons.length; $i++) { this.Buttons[$buttons[$i].Title] = $buttons[$i]; } } ToolBar.prototype.Render = function($container) { // remove duplicate separators or separators at the end of button list this._removeSeparators(); if ($container) { var tmp = ''; for (var i in this.Buttons) { btn = this.Buttons[i]; tmp += btn.GetHTML(); } $container.innerHTML = tmp; // container will contain only buttons // init all buttons after because objects are not yet created directly after assigning to innerHTML for (var i in this.Buttons) { btn = this.Buttons[i]; btn.Init(); } } else { for (var i in this.Buttons) { btn = this.Buttons[i]; document.write( btn.GetHTML() ); btn.Init(); } } var $me = this; $(document).ready( function () { for (var $button_name in $me.Buttons) { $me.Buttons[$button_name].ReadOnly = false; } } ); } ToolBar.prototype.EnableButton = function(button_id) { if(this.ButtonExists(button_id)) this.Buttons[button_id].Enable(); } ToolBar.prototype.DisableButton = function(button_id) { if(this.ButtonExists(button_id)) this.Buttons[button_id].Disable(); } ToolBar.prototype.HideButton = function(button_id) { if(this.ButtonExists(button_id)) this.Buttons[button_id].Hide(); } ToolBar.prototype.ShowButton = function(button_id) { if(this.ButtonExists(button_id)) this.Buttons[button_id].Show(); } ToolBar.prototype.SetEnabled = function(button_id, $enabled) { var $ret = $enabled ? this.EnableButton(button_id) : this.DisableButton(button_id); } ToolBar.prototype.SetVisible = function(button_id, $visible) { var $ret = $visible ? this.ShowButton(button_id) : this.HideButton(button_id); } ToolBar.prototype.GetButtonImage = function(button_id) { if( this.ButtonExists(button_id) ) return this.Buttons[button_id].imgObject; } ToolBar.prototype.ButtonExists = function(button_id) { return typeof(this.Buttons[button_id]) == 'object'; } Index: branches/5.3.x/core/admin_templates/incs/grid_blocks.tpl =================================================================== --- branches/5.3.x/core/admin_templates/incs/grid_blocks.tpl (revision 16394) +++ branches/5.3.x/core/admin_templates/incs/grid_blocks.tpl (revision 16395) @@ -1,875 +1,879 @@ <inp2:m_DefineElement name="current_page"> <span class="current_page"><inp2:m_param name="page"/></span> </inp2:m_DefineElement> <inp2:m_DefineElement name="page"> <a href="javascript:go_to_page('<inp2:m_param name="PrefixSpecial"/>', <inp2:m_param name="page"/>, <inp2:m_param name="ajax"/>)" class="nav_url"><inp2:m_param name="page"/></a> </inp2:m_DefineElement> <inp2:m_DefineElement name="next_page"> <a href="javascript:go_to_page('<inp2:m_param name="PrefixSpecial"/>', <inp2:m_param name="page"/>, <inp2:m_param name="ajax"/>)" class="nav_url">></a> </inp2:m_DefineElement> <inp2:m_DefineElement name="prev_page"> <a href="javascript:go_to_page('<inp2:m_param name="PrefixSpecial"/>', <inp2:m_param name="page"/>, <inp2:m_param name="ajax"/>)" class="nav_url"><</a> </inp2:m_DefineElement> <inp2:m_DefineElement name="next_page_split"> <a href="javascript:go_to_page('<inp2:m_param name="PrefixSpecial"/>', <inp2:m_param name="page"/>, <inp2:m_param name="ajax"/>)" class="nav_url">>></a> </inp2:m_DefineElement> <inp2:m_DefineElement name="prev_page_split"> <a href="javascript:go_to_page('<inp2:m_param name="PrefixSpecial"/>', <inp2:m_param name="page"/>, <inp2:m_param name="ajax"/>)" class="nav_url"><<</a> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_pagination_elem" main_special="" ajax="0"> <inp2:m_if check="GridInfo" type="needs_pagination" pass_params="1"> <inp2:m_phrase name="la_Page"/>: <inp2:PrintPages active_block="current_page" split="10" inactive_block="page" prev_page_block="prev_page" next_page_block="next_page" prev_page_split_block="prev_page_split" next_page_split_block="next_page_split" main_special="$main_special" ajax="$ajax" grid="$grid"/> </inp2:m_if> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_pagination" SearchPrefixSpecial="" ajax="0"> <!--## Maybe not in use ##--> <table cellspacing="0" cellpadding="2" width="100%" border="0" class="pagination_bar"> <tbody> <tr> <td width="100%"> <inp2:m_RenderElement name="grid_pagination_elem" pass_params="1"/> </td> <td> <inp2:m_if check="m_ParamEquals" param="search" value="on"> <inp2:m_if check="m_ParamEquals" name="SearchPrefixSpecial" value=""> <inp2:m_RenderElement name="grid_search" grid="$grid" PrefixSpecial="$PrefixSpecial" ajax="$ajax"/> <inp2:m_else /> <inp2:m_RenderElement name="grid_search" grid="$grid" PrefixSpecial="$SearchPrefixSpecial" ajax="$ajax"/> </inp2:m_if> </inp2:m_if> </td> </tr> </tbody> </table> </inp2:m_DefineElement> <inp2:m_DefineElement name="search_main_toolbar"> <td style="white-space: nowrap; text-align: right; width: 310px;" align="right"> <div style="float: right"> <table cellpadding="0" cellspacing="0"> <tr> <td> <input type="text" id="<inp2:m_param name='prefix'/>_search_keyword" class="filter <inp2:m_ifnot check='m_Recall' var='{$prefix}_search_keyword' equals_to=''>filter-active</inp2:m_ifnot>" name="<inp2:m_param name='prefix'/>_search_keyword" value="<inp2:m_recall var='{$prefix}_search_keyword' no_null='no_null' special='1'/>" PrefixSpecial="<inp2:m_param name='prefix'/>" Grid="<inp2:m_param name='grid'/>" ajax="0" style="width:150px"/> </td> <td style="white-space: nowrap;"> <script type="text/javascript"> b_toolbar = new ToolBar(); b_toolbar.AddButton( new ToolBarButton('search', '<inp2:m_phrase label="la_ToolTip_Search" escape="1"/>', function() { search('<inp2:m_Param name="prefix"/>', '<inp2:m_Param name="grid"/>', 0); } ) ); b_toolbar.AddButton( new ToolBarButton('search_reset_alt', '<inp2:m_phrase label="la_ToolTip_SearchReset" escape="1"/>', function() { search_reset('<inp2:m_Param name="prefix"/>', '<inp2:m_Param name="grid"/>', 0); } ) ); b_toolbar.Render(); </script> </td> </tr> </table> </div> </td> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_search" ajax="0"> <td align="right" class="search-cell"> <img src="<inp2:m_TemplatesBase/>/img/spacer.gif" width="250" height="1" alt=""/><br /> <table cellspacing="0" cellpadding="0"> <tr> <td><inp2:m_phrase name="la_Search"/>: </td> <td> <input type="text" id="<inp2:m_param name='PrefixSpecial'/>_search_keyword" class="filter <inp2:m_ifnot check='m_Recall' var='{$PrefixSpecial}_search_keyword' equals_to=''>filter-active</inp2:m_ifnot>" name="<inp2:m_param name='PrefixSpecial'/>_search_keyword" value="<inp2:m_recall var='{$PrefixSpecial}_search_keyword' no_null='no_null' special='1'/>" PrefixSpecial="<inp2:m_param name='PrefixSpecial'/>" Grid="<inp2:m_param name='grid'/>" ajax="<inp2:m_param name='ajax'/>"/> <input type="text" style="display: none;"/> </td> <td style="white-space: nowrap;" id="search_buttons[<inp2:m_param name="PrefixSpecial"/>]"> </td> </tr> </table> <inp2:m_if check="m_Param" name="ajax" equals_to="0"> <script type="text/javascript"> addLoadEvent( function () { <inp2:m_RenderElement name="grid_search_buttons" pass_params="true"/> } ) </script> </inp2:m_if> </td> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_search_buttons" PrefixSpecial="" grid="" ajax="1"> var $search_box = document.getElementById('<inp2:m_param name="PrefixSpecial"/>_search_keyword'); if ($search_box) { //$search_box.onkeydown = search_keydown; $( jq('#<inp2:m_param name="PrefixSpecial"/>_search_keyword') ).keydown(search_keydown); } var $search_buttons = document.getElementById('search_buttons[<inp2:m_param name="PrefixSpecial"/>]'); if ($search_buttons) { Toolbars['<inp2:m_param name="PrefixSpecial"/>_search'] = new ToolBar('icon16_'); Toolbars['<inp2:m_param name="PrefixSpecial"/>_search'].IconSize = {w:22,h:22}; Toolbars['<inp2:m_param name="PrefixSpecial"/>_search'].UseLabels = false; Toolbars['<inp2:m_param name="PrefixSpecial"/>_search'].AddButton( new ToolBarButton( 'search', '<inp2:m_phrase name="la_ToolTip_Search" escape="1"/>', function() { search('<inp2:m_param name="PrefixSpecial"/>','<inp2:m_param name="grid"/>', <inp2:m_param name="ajax"/>) }, null, '<inp2:m_param name="PrefixSpecial"/>' ) ); Toolbars['<inp2:m_param name="PrefixSpecial"/>_search'].AddButton( new ToolBarButton( 'search_reset', '<inp2:m_phrase name="la_ToolTip_SearchReset" escape="1"/>', function() { search_reset('<inp2:m_param name="PrefixSpecial"/>','<inp2:m_param name="grid"/>', <inp2:m_param name="ajax"/>) }, null, '<inp2:m_param name="PrefixSpecial"/>' ) ); Toolbars['<inp2:m_param name="PrefixSpecial"/>_search'].Render($search_buttons); } </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_checkbox_td" format=""> <inp2:m_RenderElement name="grid_data_td" pass_params="1"/> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_checkbox_td_no_icon" format=""> <inp2:m_RenderElement name="grid_data_td" pass_params="1"/> </inp2:m_DefineElement> <inp2:m_DefineElement name="label_grid_checkbox_td" format=""> <inp2:m_RenderElement name="grid_data_label_td" pass_params="1"/> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_icon_td" format=""> <inp2:m_RenderElement name="grid_data_td" pass_params="1"/> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_radio_td" format=""> <inp2:m_RenderElement name="grid_data_td" pass_params="1"/> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_data_td" format="" no_special="1" nl2br="" first_chars="" as_label="" td_style=""> <inp2:Field field="$field" first_chars="$first_chars" nl2br="$nl2br" as_label="$as_label" grid="$grid" no_special="$no_special" format="$format"/> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_total_td"> <inp2:m_if check="FieldTotal" field="$field" function_only="1"> <inp2:FieldTotal field="$field"/> <inp2:m_else/> </inp2:m_if> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_priority_td" format="" no_special="" nl2br="" first_chars="" td_style="" currency=""> <inp2:Field field="$field" first_chars="$first_chars" currency="$currency" nl2br="$nl2br" grid="$grid" no_special="$no_special" format="$format"/> <inp2:m_ifnot check="Field" field="Priority" equals_to="0" db="db"><span class="priority"><sup><inp2:Field field="Priority"/></sup></span></inp2:m_ifnot> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_edit_td" format="" style=""> <input type="text" id="<inp2:{$PrefixSpecial}_InputName field="$field"/>" name="<inp2:{$PrefixSpecial}_InputName field="$field"/>" value="<inp2:{$PrefixSpecial}_field field="$field" grid="$grid" format="$format"/>" style="<inp2:m_Param name='style'/>"/> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_picker_td" nl2br="0" no_special="1" separator=" "> <inp2:Field name="$field" format="$separator" nl2br="$nl2br" no_special="$no_special"/> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_options_td" format=""> <select name="<inp2:InputName field="$field"/>" id="<inp2:InputName field="$field"/>"> <inp2:m_if check="FieldOption" field="$field" option="use_phrases"> <inp2:PredefinedOptions field="$field" block="inp_option_phrase" selected="selected" has_empty="1" empty_value="0"/> <inp2:m_else/> <inp2:PredefinedOptions field="$field" block="inp_option_item" selected="selected" has_empty="1" empty_value="0"/> </inp2:m_if> </select> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_date_td" format=""> <input type="text" name="<inp2:InputName field="{$field}_date"/>" id="<inp2:InputName field="{$field}_date"/>" value="<inp2:Field field="{$field}_date" format="_regional_InputDateFormat"/>" size="<inp2:Format field="{$field}_date" input_format="1" edit_size="edit_size"/>" datepickerIcon="<inp2:m_ProjectBase/>core/admin_templates/img/calendar_icon.gif"> <img src="<inp2:m_TemplatesBase/>/img/calendar_icon.gif" id="cal_img_<inp2:InputName field="{$field}"/>" width="13" height="12" style="cursor: pointer; margin-right: 5px" title="Date selector" /> <span class="small">(<inp2:Format field="{$field}_date" input_format="1" human="true"/>)</span> <script type="text/javascript"> Calendar.setup({ inputField : "<inp2:InputName field="{$field}_date"/>", ifFormat : Calendar.phpDateFormat("<inp2:Format field="{$field}_date" input_format="1"/>"), button : "cal_img_<inp2:InputName field="{$field}"/>", align : "br", singleClick : true, showsTime : true, weekNumbers : false, firstDay : <inp2:m_GetConfig var="FirstDayOfWeek"/>, onUpdate : function(cal) { runOnChange('<inp2:InputName field="{$field}_date"/>'); } }); </script> <input type="hidden" name="<inp2:InputName field="{$field}_time"/>" id="<inp2:InputName field="{$field}_time" input_format="1"/>" value=""> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_data_label_td"> <inp2:Field field="$field" grid="$grid" plus_or_as_label="1" no_special="no_special" format="$format"/> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_empty_filter"> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_column_filter"> <!--## this cheat makes sure, that columns without a filter are using like filter ##--> <inp2:m_RenderElement name="grid_like_filter" pass_params="1"/> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_options_filter" use_phrases="0" filter_width="90%"> <select class="filter <inp2:m_ifnot check='SearchField' field='$filter_field' filter_type='options' grid='$grid' equals_to=''>filter-active</inp2:m_ifnot>" name="<inp2:SearchInputName field="$filter_field" filter_type="options" grid="$grid"/>" style="width: <inp2:m_Param name="filter_width"/>"> <inp2:m_if check="m_ParamEquals" name="use_phrases" value="1"> <inp2:PredefinedSearchOptions field="$filter_field" block="inp_option_phrase" selected="selected" has_empty="1" empty_value="" filter_type="options" grid="$grid"/> <inp2:m_else/> <inp2:PredefinedSearchOptions field="$filter_field" block="inp_option_item" selected="selected" has_empty="1" empty_value="" filter_type="options" grid="$grid"/> </inp2:m_if> </select> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_multioptions_filter" use_phrases="0" filter_width="90%"> <input type="hidden" name="<inp2:SearchInputName field='$filter_field' filter_type='multioptions' grid='$grid'/>" id="<inp2:SearchInputName field='$filter_field' filter_type='multioptions' grid='$grid'/>" value="<inp2:SearchField field='$filter_field' filter_type='multioptions' grid='$grid'/>" /> <select multiple class="filter <inp2:m_ifnot check='SearchField' field='$filter_field' filter_type='multioptions' grid='$grid' equals_to=''>filter-active</inp2:m_ifnot>" id="<inp2:SearchInputName field='$filter_field' filter_type='multioptions' grid='$grid'/>_select" style="width: <inp2:m_Param name='filter_width'/>"> <inp2:m_if check="m_ParamEquals" name="use_phrases" value="1"> <inp2:PredefinedSearchOptions field="$filter_field" block="inp_option_phrase" selected="selected" filter_type="multioptions" grid="$grid"/> <inp2:m_else/> <inp2:PredefinedSearchOptions field="$filter_field" block="inp_option_item" selected="selected" filter_type="multioptions" grid="$grid"/> </inp2:m_if> </select> <script type="text/javascript"> MultiOptions.ConvertSelect('<inp2:SearchInputName field="$filter_field" filter_type="multioptions" grid="$grid"/>', '<inp2:m_Param name="ajax"/>'); </script> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_like_filter" filter_width="95%"> <input type="text" class="filter <inp2:m_ifnot check='SearchField' field='$filter_field' filter_type='like' grid='$grid' equals_to=''>filter-active</inp2:m_ifnot>" style="width: <inp2:m_Param name="filter_width"/>" name="<inp2:SearchInputName field="$filter_field" filter_type="like" grid="$grid"/>" value="<inp2:SearchField field="$filter_field" filter_type="like" grid="$grid"/>" onkeypress="search_keydown(event, '<inp2:m_Param name="PrefixSpecial"/>', '<inp2:m_Param name="grid"/>', '<inp2:m_Param name="ajax"/>')"/> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_user_like_filter" selector_template="user_selector" filter_width="95%"> <table class="range-filter"> <tr> <td style="width: 100%"> <input type="text" class="filter <inp2:m_ifnot check='SearchField' field='$filter_field' filter_type='like' grid='$grid' equals_to=''>filter-active</inp2:m_ifnot>" style="width: <inp2:m_Param name="filter_width"/>" name="<inp2:SearchInputName field="$filter_field" filter_type="like" grid="$grid"/>" id="<inp2:SearchInputName field="$filter_field" filter_type="like" grid="$grid"/>" value="<inp2:SearchField field="$filter_field" filter_type="like" grid="$grid"/>" onkeypress="search_keydown(event, '<inp2:m_Param name="PrefixSpecial"/>', '<inp2:m_Param name="grid"/>', '<inp2:m_Param name="ajax"/>')"/> </td> <td valign="middle"> <a href="<inp2:m_t t="$selector_template" pass="all,$PrefixSpecial"/>" onclick="openSelector('<inp2:m_Param name="PrefixSpecial" js_escape="1"/>', this.href, '<inp2:m_Param name="filter_field" js_escape="1"/>'); return false;"> <img src="img/icons/icon24_link_user.gif" style="cursor:hand;" width="24" height="24" border="0"> </a> </td> </tr> </table> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_picker_filter" use_phrases="0" filter_width="90%"> <select class="filter <inp2:m_ifnot check='SearchField' field='$filter_field' filter_type='picker' grid='$grid' equals_to=''>filter-active</inp2:m_ifnot>" name="<inp2:SearchInputName field="$filter_field" filter_type="picker" grid="$grid"/>" style="width: <inp2:m_Param name="filter_width"/>"> <inp2:m_if check="m_ParamEquals" name="use_phrases" value="1"> <inp2:PredefinedSearchOptions field="$filter_field" block="inp_option_phrase" selected="selected" has_empty="1" empty_value="" filter_type="picker" grid="$grid"/> <inp2:m_else/> <inp2:PredefinedSearchOptions field="$filter_field" block="inp_option_item" selected="selected" has_empty="1" empty_value="" filter_type="picker" grid="$grid"/> </inp2:m_if> </select> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_like_combo_filter" filter_width="95%"> <input type="text" autocomplete="off" class="filter <inp2:m_ifnot check='SearchField' field='$filter_field' filter_type='like' grid='$grid' equals_to=''>filter-active</inp2:m_ifnot>" style="width: <inp2:m_Param name="filter_width"/>" name="<inp2:SearchInputName field="$filter_field" filter_type="like" grid="$grid"/>" id="<inp2:SearchInputName field="$filter_field" filter_type="like" grid="$grid"/>" value="<inp2:SearchField field="$filter_field" filter_type="like" grid="$grid"/>" onkeypress="search_keydown(event, '<inp2:m_Param name="PrefixSpecial"/>', '<inp2:m_Param name="grid"/>', '<inp2:m_Param name="ajax"/>')"/> <script type="text/javascript"> $('#' + jq('<inp2:SearchInputName field="$filter_field" filter_type="like" grid="$grid"/>')).autocomplete({ source: '<inp2:m_t pass="m,{$PrefixSpecial}" field="$filter_field" {$PrefixSpecial}_event="OnSuggestValuesJSON" no_amp="1" js_escape="1"/>', select: function (event, ui) { if ( event.which === 13 ) { search('<inp2:m_Param name="PrefixSpecial"/>', '<inp2:m_Param name="grid"/>', parseInt('<inp2:m_Param name="ajax"/>')); } } }); </script> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_equals_filter" filter_width="95%"> <input type="text" class="filter <inp2:m_ifnot check='SearchField' field='$filter_field' filter_type='equals' grid='$grid' equals_to=''>filter-active</inp2:m_ifnot>" style="width: <inp2:m_Param name="filter_width"/>" name="<inp2:SearchInputName field="$filter_field" filter_type="equals" grid="$grid"/>" value="<inp2:SearchField field="$filter_field" filter_type="equals" grid="$grid"/>" onkeypress="search_keydown(event, '<inp2:m_Param name="PrefixSpecial"/>', '<inp2:m_Param name="grid"/>', '<inp2:m_Param name="ajax"/>')"/> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_range_filter" filter_width="90%"> <table class="range-filter"> <tr> <td style="width: 100%"> <input type="text" class="filter <inp2:m_ifnot check='SearchField' field='$filter_field' filter_type='range' type='from' grid='$grid' equals_to=''>filter-active</inp2:m_ifnot>" name="<inp2:SearchInputName field="$filter_field" filter_type="range" type="from" grid="$grid"/>" value="<inp2:SearchField field="$filter_field" filter_type="range" type="from" grid="$grid"/>" style="width: <inp2:m_Param name="filter_width"/>;" onkeypress="search_keydown(event, '<inp2:m_Param name="PrefixSpecial"/>', '<inp2:m_Param name="grid"/>', '<inp2:m_Param name="ajax"/>')"/> </td> <td rowspan="2" valign="middle"> <img src="<inp2:m_TemplatesBase/>/img/expand_filter.gif" width="7" height="9" alt="" onclick="filter_toggle('<inp2:SearchInputName field='$filter_field' filter_type='range' type='to' grid='$grid'/>_row', '<inp2:m_Param name='PrefixSpecial'/>');"/> </td> </tr> <tr class="to-range-filter<inp2:m_ifnot check='RangeFiltersUsed' grid='$grid'> hidden-filter</inp2:m_ifnot>" id="<inp2:SearchInputName field='$filter_field' filter_type='range' type='to' grid='$grid'/>_row"> <td style="width: 100%;<inp2:m_ifnot check='RangeFiltersUsed' grid='$grid'> display: none;</inp2:m_ifnot>"> <input type="text" class="filter <inp2:m_ifnot check='SearchField' field='$filter_field' filter_type='range' type='to' grid='$grid' equals_to=''>filter-active</inp2:m_ifnot>" name="<inp2:SearchInputName field="$filter_field" filter_type="range" type="to" grid="$grid"/>" value="<inp2:SearchField field="$filter_field" filter_type="range" type="to" grid="$grid"/>" style="width: <inp2:m_Param name="filter_width"/>;" onkeypress="search_keydown(event, '<inp2:m_Param name="PrefixSpecial"/>', '<inp2:m_Param name="grid"/>', '<inp2:m_Param name="ajax"/>')"/> </td> </tr> </table> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_date_range_filter" calendar_format="" filter_width="80%"> <table class="range-filter"> <tr> <td style="width: 100%"> <input type="text" class="filter <inp2:m_ifnot check='SearchField' field='$filter_field' filter_type='date_range' type='from' grid='$grid' equals_to=''>filter-active</inp2:m_ifnot>" style="width: <inp2:m_Param name="filter_width"/>" name="<inp2:SearchInputName field="$filter_field" filter_type="date_range" type="from" grid="$grid"/>" id="<inp2:SearchInputName field="$filter_field" filter_type="date_range" type="from" grid="$grid"/>" value="<inp2:SearchField field="$filter_field" filter_type="date_range" type="from" grid="$grid"/>" onkeypress="search_keydown(event, '<inp2:m_Param name="PrefixSpecial"/>', '<inp2:m_Param name="grid"/>', '<inp2:m_Param name="ajax"/>')"/> </td> <td> <img src="<inp2:m_TemplatesBase/>/img/calendar_icon.gif" width="13" height="12" id="cal_img_<inp2:SearchInputName field="$filter_field" filter_type="date_range" type="from" grid="$grid"/>" style="cursor: pointer; margin-right: 5px" title="Date selector" /> </td> <td rowspan="2" valign="middle"> <img src="<inp2:m_TemplatesBase/>/img/expand_filter.gif" width="7" height="9" alt="" onclick="filter_toggle('<inp2:SearchInputName field='$filter_field' filter_type='date_range' type='to' grid='$grid'/>_row', '<inp2:m_Param name='PrefixSpecial'/>');"/> </td> </tr> <tr class="to-range-filter<inp2:m_ifnot check='RangeFiltersUsed' grid='$grid'> hidden-filter</inp2:m_ifnot>" id="<inp2:SearchInputName field='$filter_field' filter_type='date_range' type='to' grid='$grid'/>_row"> <td style="width: 100%;<inp2:m_ifnot check='RangeFiltersUsed' grid='$grid'> display: none;</inp2:m_ifnot>"> <input type="text" class="filter <inp2:m_ifnot check='SearchField' field='$filter_field' filter_type='date_range' type='to' grid='$grid' equals_to=''>filter-active</inp2:m_ifnot>" style="width: <inp2:m_Param name="filter_width"/>" name="<inp2:SearchInputName field="$filter_field" filter_type="date_range" type="to" grid="$grid"/>" id="<inp2:SearchInputName field="$filter_field" filter_type="date_range" type="to" grid="$grid"/>" value="<inp2:SearchField field="$filter_field" filter_type="date_range" type="to" grid="$grid"/>" onkeypress="search_keydown(event, '<inp2:m_Param name="PrefixSpecial"/>', '<inp2:m_Param name="grid"/>', '<inp2:m_Param name="ajax"/>')"/> </td> <td<inp2:m_ifnot check='RangeFiltersUsed' grid='$grid'> style="display: none;"</inp2:m_ifnot>> <img src="<inp2:m_TemplatesBase/>/img/calendar_icon.gif" width="13" height="12" id="cal_img_<inp2:SearchInputName field="$filter_field" filter_type="date_range" type="to" grid="$grid"/>" style="cursor: pointer; margin-right: 5px" title="Date selector" /> </td> </tr> </table> <script type="text/javascript"> var $format = "<inp2:m_if check='m_Param' name='calendar_format'><inp2:m_Param name='calendar_format'/><inp2:m_else/><inp2:Format field='{$sort_field}' input_format='1'/></inp2:m_if>"; Calendar.setup({ inputField : "<inp2:SearchInputName field='$filter_field' filter_type='date_range' type='from' grid='$grid'/>", ifFormat : Calendar.phpDateFormat($format), button : "cal_img_<inp2:SearchInputName field='$filter_field' filter_type='date_range' type='from' grid='$grid'/>", align : 'br', singleClick : true, showsTime : true, weekNumbers : false, firstDay : <inp2:m_GetConfig var="FirstDayOfWeek"/> }); Calendar.setup({ inputField : "<inp2:SearchInputName field='$filter_field' filter_type='date_range' type='to' grid='$grid'/>", ifFormat : Calendar.phpDateFormat($format), button : "cal_img_<inp2:SearchInputName field='$filter_field' filter_type='date_range' type='to' grid='$grid'/>", align : 'br', singleClick : true, showsTime : true, weekNumbers : false, firstDay : <inp2:m_GetConfig var="FirstDayOfWeek"/> }); </script> </inp2:m_DefineElement> <inp2:m_DefineElement name="viewmenu_sort_block"> $Menus['<inp2:m_param name="PrefixSpecial"/>'+'_sorting_menu'].addMenuItem('<inp2:m_phrase name="$title" js_escape="1"/>','direct_sort_grid("<inp2:m_param name="PrefixSpecial"/>","<inp2:m_param name="sort_field"/>","<inp2:{$PrefixSpecial}_OrderInfo type="direction" pos="1"/>", null, <inp2:m_param name="ajax"/>);','<inp2:m_if check="{$PrefixSpecial}_IsOrder" field="$sort_field" pos="1" >2</inp2:m_if>'); </inp2:m_DefineElement> <inp2:m_DefineElement name="viewmenu_filter_block"> $Menus['<inp2:m_param name="PrefixSpecial"/>'+'_filter_menu'].addMenuItem('<inp2:m_param name="label" js_escape="1"/>','<inp2:m_param name="filter_action"/>','<inp2:m_param name="filter_status"/>'); </inp2:m_DefineElement> <inp2:m_DefineElement name="viewmenu_filter_separator"> $Menus['<inp2:m_param name="PrefixSpecial"/>'+'_filter_menu'].addMenuSeparator(); </inp2:m_DefineElement> <inp2:m_include template="incs/menu_blocks"/> <inp2:m_DefineElement name="grid_save_warning"> <inp2:m_RenderElement design="form_message" pass_params="1"> <inp2:m_phrase name="la_Warning_Save_Item"/> </inp2:m_RenderElement> <script type="text/javascript"> $edit_mode = <inp2:m_if check="m_ParamEquals" name="edit_mode" value="1">true<inp2:m_else />false</inp2:m_if>; if (Form) Form.Changed(); // window.parent.document.title += ' - MODE: ' + ($edit_mode ? 'EDIT' : 'LIVE'); </script> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_status" pagination="1"> <table class="grid-status-bar"> <tr> <td nowrap="nowrap" style="vertical-align: middle;"> <inp2:m_Phrase label="la_Records"/>: <inp2:GridInfo type="filtered"/> (<inp2:GridInfo type="from"/> - <inp2:GridInfo type="to"/>) <inp2:m_Phrase label="la_OutOf"/> <inp2:GridInfo type="total"/> </td> <td align="right" class="tablenav" valign="middle"> <inp2:m_if check="m_Param" name="pagination"> <inp2:m_RenderElement name="grid_pagination_elem" pass_params="1"/> </inp2:m_if> </td> </tr> </table> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_column_title" title="column:la_fld_{$field}" use_phrases="1"> <table style="width: auto" class="layout-only-table"><tr> <td style="vertical-align: middle; padding: 0px"> <a href="javascript:resort_grid('<inp2:m_param name="PrefixSpecial"/>','<inp2:m_param name="sort_field"/>', <inp2:m_param name="ajax"/>);" class="columntitle_small" title="<inp2:m_Phrase name='la_col_SortBy' no_editing='1' html_escape='1'/> <inp2:m_if check='m_Param' name='use_phrases'><inp2:m_Phrase name='$title' no_editing='1' html_escape='1'/><inp2:m_else/><inp2:m_Param name='title'/></inp2:m_if>"> <img alt="<inp2:m_Phrase name='la_col_SortBy' no_editing='1' html_escape='1'/> <inp2:m_if check='m_Param' name='use_phrases'><inp2:m_Phrase name='$title' no_editing='1' html_escape='1'/><inp2:m_else/><inp2:m_Param name='title'/></inp2:m_if>" src="<inp2:m_TemplatesBase/>/img/list_arrow_<inp2:Order field='$sort_field'/>.gif" width="15" height="15" border="0" align="absmiddle" /> </a> </td> <td style="vertical-align: middle; text-align: left; padding: 1px; white-space: normal"> <a href="javascript:resort_grid('<inp2:m_param name="PrefixSpecial"/>','<inp2:m_param name="sort_field"/>', <inp2:m_param name="ajax"/>);" class="columntitle_small" title="<inp2:m_Phrase name='la_col_SortBy' no_editing='1' html_escape='1'/> <inp2:m_if check='m_Param' name='use_phrases'><inp2:m_Phrase name='$title' no_editing='1' html_escape='1'/><inp2:m_else/><inp2:m_Param name='title'/></inp2:m_if>"> <inp2:m_if check="m_Param" name="use_phrases"><inp2:m_Phrase name="$title"/><inp2:m_else/><inp2:m_Param name="title"/></inp2:m_if> </a> </td> </tr></table> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_selector_icon_html" selector="checkbox"> <div style="white-space: nowrap;"> <inp2:m_if check="m_Param" name="selector"> <input type="<inp2:m_Param name='selector'/>" name="<inp2:InputName field='$IdField' IdField='$IdField'/>" id="<inp2:InputName field='$IdField' IdField='$IdField'/>"> </inp2:m_if> <inp2:ItemIcon name="module" grid="$grid" result_to_var="icon_module"/> <img src="<inp2:ModulePath module='$icon_module'/>img/itemicons/<inp2:ItemIcon grid='$grid'/>" width="16" height="16" alt=""/> </div> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_selector_html" selector="checkbox"> <inp2:m_if check="m_Param" name="selector"> <input type="<inp2:m_Param name='selector'/>" name="<inp2:InputName field='$IdField' IdField='$IdField'/>" id="<inp2:InputName field='$IdField' IdField='$IdField'/>"> </inp2:m_if> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_select_all_checkbox_html"> <input type="checkbox" onclick="Grids['<inp2:m_param name="PrefixSpecial"/>'].InvertSelection(); this.checked=false;" ondblclick="Grids['<inp2:m_param name="PrefixSpecial"/>'].ClearSelection(); this.checked=false;" /> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_column_title_no_sorting" title="column:la_fld_{$field}" use_phrases="1"> - <inp2:m_if check="m_ParamEquals" name="use_phrases" value="1"><inp2:m_phrase name="$title"/><inp2:m_else/><inp2:m_param name="title"/></inp2:m_if> + <table style="width: auto" class="layout-only-table"><tr> + <td style="vertical-align: middle; text-align: left; padding: 1px; white-space: normal"> + <inp2:m_if check="m_ParamEquals" name="use_phrases" value="1"><inp2:m_phrase name="$title"/><inp2:m_else/><inp2:m_param name="title"/></inp2:m_if> + </td> + </tr></table> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_js_block" format="" no_special="1" nl2br="" first_chars="" td_style=""> '<inp2:m_RenderElement name="$block_name" pass_params="1" js_escape="1"/>'<inp2:m_if check="m_Param" name="is_last" inverse="1">,</inp2:m_if> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_js_empty_filter_block" use_phrases="0"> '<inp2:m_RenderElement name="grid_empty_filter" pass_params="1" js_escape="1"/>'<inp2:m_if check="m_Param" name="is_last" inverse="1">,</inp2:m_if> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_js_filter_block" use_phrases="0"> '<inp2:m_RenderElement name="$filter_block" pass_params="1" js_escape="1"/>'<inp2:m_if check="m_Param" name="is_last" inverse="1">,</inp2:m_if> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_js_width_td" format="" width="" no_special="1" nl2br="" first_chars="" td_style=""> <inp2:m_Param name="width" js_escape="1"/><inp2:m_if check="m_Param" name="is_last" inverse="1">,</inp2:m_if> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid" main_prefix="" per_page="" main_special="" grid_filters="1" search="on" header_block="grid_column_title" filter_block="grid_column_filter" data_block="grid_data_td" total_block="grid_total_td" row_block="_row" ajax="0" totals="0" limited_heights="false" max_row_height="45" grid_height="auto" selector="checkbox" grid_status="1" totals_render_as="" > <!--## grid_filters - show individual filters for each column has_filters - draw filter section in "View" menu in toolbar ##--> <inp2:InitList pass_params="1"/> <!--## this is to avoid recalling prefix as an item in first iterate grid, by col-picker for instance ##--> <inp2:{$PrefixSpecial}_SaveWarning name="grid_save_warning" pass_params="1"/> <inp2:m_if check="{$PrefixSpecial}_SearchActive" grid="$grid"> <inp2:m_RenderElement design="form_message" pass_params="1"> <inp2:m_phrase name="la_Warning_Filter"/> </inp2:m_RenderElement> </inp2:m_if> <div id="grid_<inp2:m_Param name='PrefixSpecial'/>_container"></div> <inp2:m_if check="m_Param" name="grid_status"> <inp2:m_RenderElement name="grid_status" grid="$grid" PrefixSpecial="$PrefixSpecial" main_special="$main_special" search="$search" ajax="$ajax"/> </inp2:m_if> <inp2:m_if check="m_ParamEquals" name="ajax" value="0"> <inp2:m_if check="m_GetEquals" name="fw_menu_included" value=""> <link rel="stylesheet" rev="stylesheet" href="<inp2:m_Compress files='incs/nlsmenu.css'/>" type="text/css" /> <script type="text/javascript" src="<inp2:m_Compress files='js/nlsmenu.js|js/nlsmenueffect_1_2_1.js'/>"></script> <script type="text/javascript"> var menuMgr = new NlsMenuManager("mgr"); menuMgr.timeout = 500; menuMgr.flowOverFormElement = true; </script> <inp2:m_set fw_menu_included="1"/> </inp2:m_if> <script type="text/javascript"> <inp2:m_RenderElement name="grid_js" mouseover_class="grid-data-row-mouseover" selected_class="grid-data-row-selected:grid-data-row-even-selected" tag_name="tr" pass_params="true"/> </script> </inp2:m_if> <input type="hidden" id="<inp2:m_param name="PrefixSpecial"/>_Sort1" name="<inp2:m_param name="PrefixSpecial"/>_Sort1" value=""> <input type="hidden" id="<inp2:m_param name="PrefixSpecial"/>_Sort1_Dir" name="<inp2:m_param name="PrefixSpecial"/>_Sort1_Dir" value="asc"> </inp2:m_DefineElement> <inp2:m_DefineElement name="default_sorting_element" ajax="0"> <div style="text-align: center;"> <a href="#" onclick="reset_sorting('<inp2:m_Param name="prefix"/>', <inp2:m_param name="ajax"/>); return false;" title="<inp2:m_phrase name="la_col_ResetToDefaultSorting" html_escape="1"/>"> <img src="<inp2:m_TemplatesBase/>/img/list_arrow_<inp2:m_if check='{$prefix}_OrderChanged'>no<inp2:m_else/>desc</inp2:m_if>_big.gif" width="16" height="16" alt="<inp2:m_phrase name="la_col_ResetToDefaultSorting" html_escape="1"/>"/> </a> </div> </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_total_row"> GridScrollers['<inp2:m_param name="PrefixSpecial"/>'].SetFooter( [ [' ', <inp2:IterateGridFields grid="$grid" mode="total" force_block="grid_js_block" ajax="$ajax" pass_params="1"/>] ] ); </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_js" main_prefix="" per_page="" main_special="" grid_filters="1" header_block="grid_column_title" filter_block="grid_column_filter" data_block="grid_data_td" total_block="grid_total_td" row_block="_row" ajax="0" totals="0" limited_heights="false" max_row_height="45" grid_height="auto" grid_status="1" ajax="1" totals_render_as="" selector="checkbox" mouseover_class="grid-data-row-mouseover" selected_class="grid-data-row-selected:grid-data-row-even-selected" tag_name="tr" > <inp2:GridSelector grid="$grid" default="$selector" result_to_var="selector"/> // 1. create grid GridScrollers['<inp2:m_param name="PrefixSpecial"/>'] = new GridScroller('grid_<inp2:m_Param name="PrefixSpecial" />', 'auto', <inp2:m_if check="m_Param" name="grid_height" equals_to="auto">'<inp2:m_Param name="grid_height"/>'<inp2:m_else/><inp2:m_Param name="grid_height"/></inp2:m_if>); GridScrollers['<inp2:m_param name="PrefixSpecial"/>'].Spacer = 'img/spacer.gif'; GridScrollers['<inp2:m_param name="PrefixSpecial"/>'].LeftCells = <inp2:FreezerPosition grid="$grid"/>; GridScrollers['<inp2:m_param name="PrefixSpecial"/>'].BottomOffset = <inp2:m_if check="m_Param" name="grid_status">30<inp2:m_else/>0</inp2:m_if>; GridScrollers['<inp2:m_param name="PrefixSpecial"/>'].MinWidths = [<inp2:GridSelectorColumnWidth selector="$selector" icon_width="20" selector_width="30" grid="$grid"/>, <inp2:IterateGridFields grid="$grid" mode="width" block="grid_js_width_td" ajax="$ajax"/>]; GridScrollers['<inp2:m_param name="PrefixSpecial"/>'].PickerCRC = '<inp2:PickerCRC grid="$grid"/>'; GridScrollers['<inp2:m_param name="PrefixSpecial"/>'].LimitedHeights = <inp2:m_param name="limited_heights"/>; GridScrollers['<inp2:m_param name="PrefixSpecial"/>'].MaxRowHeight = <inp2:m_param name="max_row_height"/>; GridScrollers['<inp2:m_param name="PrefixSpecial"/>'].SetHeader( [ [' <inp2:m_RenderElement name="default_sorting_element" prefix="$PrefixSpecial" ajax="$ajax" js_escape="1" strip_nl="2"/>', <inp2:IterateGridFields grid="$grid" mode="header" force_block="grid_js_block" ajax="$ajax" pass_params="1"/>], ['<inp2:m_if check="m_Param" name="selector" equals_to="checkbox"><inp2:m_RenderElement name="grid_select_all_checkbox_html" pass_params="1" js_escape="1"/><inp2:m_else/> </inp2:m_if>', <inp2:m_if check="m_Param" name="grid_filters"> <inp2:IterateGridFields grid="$grid" mode="filter" force_block="grid_js_filter_block" ajax="$ajax" pass_params="1"/> <inp2:m_else/> <inp2:IterateGridFields grid="$grid" mode="filter" force_block="grid_js_empty_filter_block" ajax="$ajax" pass_params="1"/> </inp2:m_if> ] ] ) GridScrollers['<inp2:m_param name="PrefixSpecial"/>'].FieldNames = ['_CheckboxColumn', <inp2:IterateGridFields grid="$grid" mode="fields"/>]; GridScrollers['<inp2:m_param name="PrefixSpecial"/>'].SetData( [ <inp2:m_DefineElement name="js_row" td_style="" row_class_render_as="" selector_render_as="grid_selector_html" row_class=""> { 'row_class': '<inp2:m_if check="m_Param" name="row_class_render_as"><inp2:m_RenderElement name="$row_class_render_as" PrefixSpecial="$PrefixSpecial" trim="1"/><inp2:m_else/><inp2:m_Param name="row_class"/></inp2:m_if>', 'data': ['<inp2:m_RenderElement name="$selector_render_as" pass_params="1" js_escape="1"/>',<inp2:IterateGridFields grid="$grid" mode="data" force_block="grid_js_block" pass_params="1"/>] }<inp2:m_if check="m_Param" name="is_last" inverse="1">,</inp2:m_if> </inp2:m_DefineElement> <inp2:m_set {$PrefixSpecial}_sequence="1" odd_even="table-color1"/> <inp2:m_if check="UseItemIcons" grid="$grid"> <inp2:PrintList block="js_row" selector_render_as="grid_selector_icon_html" per_page="$per_page" main_special="$main_special" selector="$selector" id_field="$IdField" grid="$grid"/> <inp2:m_else/> <inp2:PrintList block="js_row" selector_render_as="grid_selector_html" per_page="$per_page" main_special="$main_special" selector="$selector" id_field="$IdField" grid="$grid"/> </inp2:m_if> ] ) GridScrollers['<inp2:m_param name="PrefixSpecial"/>'].IDs = [ <inp2:m_DefineElement name="js_id"> '<inp2:m_param name="PrefixSpecial"/>_<inp2:Field field="$IdField"/>'<inp2:m_if check="m_Param" name="is_last" inverse="1">,</inp2:m_if> </inp2:m_DefineElement> <inp2:PrintList block="js_id" per_page="$per_page" id_field="$IdField" main_special="$main_special"/> ] <inp2:m_if check="m_Param" name="totals_render_as"> <inp2:m_RenderElement name="$totals_render_as" pass_params="1"/> </inp2:m_if> GridScrollers['<inp2:m_param name="PrefixSpecial"/>'].Render('grid_<inp2:m_Param name="PrefixSpecial" />_container'); <inp2:m_ifnot check="m_Param" name="ajax"> <inp2:m_RenderElement name="grid_search_buttons" pass_params="1"/> </inp2:m_ifnot> GridScrollers['<inp2:m_param name="PrefixSpecial"/>'].SaveURL = '<inp2:m_t pass="m,$PrefixSpecial" {$PrefixSpecial}_event="OnSaveWidths" widths="#WIDTHS#" grid_name="$grid" no_amp="1" js_escape="1"/>'; <inp2:m_if check="m_Param" name="selector"> // 2. scan grid (only when using selector) Grids['<inp2:m_param name="PrefixSpecial"/>'] = new Grid('<inp2:m_param name="PrefixSpecial"/>', '<inp2:m_param name="selected_class"/>', ':original', ($allow_dbl_click === undefined || $allow_dbl_click ? edit : function() {}) , a_toolbar); Grids['<inp2:m_param name="PrefixSpecial"/>'].MouseOverClass = '<inp2:m_param name="mouseover_class"/>'; Grids['<inp2:m_param name="PrefixSpecial"/>'].StickySelection = <inp2:m_if check="m_GetConfig" name="StickyGridSelection">true<inp2:m_else/>false</inp2:m_if>; Grids['<inp2:m_param name="PrefixSpecial"/>'].AddItemsByIdMask('<inp2:m_param name="tag_name"/>', /^<inp2:m_param name="PrefixSpecial"/>_([\d\w-=]+)/, '<inp2:m_param name="PrefixSpecial"/>[$$ID$$][<inp2:m_param name="IdField"/>]'); Grids['<inp2:m_param name="PrefixSpecial"/>'].InitItems(); <inp2:m_if check="m_Param" name="selector" equals_to="radio"> Grids['<inp2:m_param name="PrefixSpecial"/>'].EnableRadioMode(); </inp2:m_if> <inp2:m_if check="{$PrefixSpecial}_UseAutoRefresh"> function refresh_grid() { // window.location.reload(); var $window_url = window.location.href; if ($window_url.indexOf('skip_session_refresh=1') == -1) { $window_url += '&skip_session_refresh=1'; } window.location.href = $window_url; } setTimeout('refresh_grid()', <inp2:{$PrefixSpecial}_AutoRefreshInterval/> * 60000); </inp2:m_if> </inp2:m_if> <inp2:m_RenderElement name="nlsmenu_declaration" pass_params="true"/> $ViewMenus = new Array('<inp2:m_param name="PrefixSpecial"/>'); </inp2:m_DefineElement> <inp2:m_DefineElement name="old_grid" main_prefix="" per_page="" main_special="" grid_filters="" search="on" header_block="grid_column_title" filter_block="grid_column_filter" data_block="grid_data_td" total_block="grid_total_td" row_block="_row" ajax="0" totals="0" selector="checkbox"> <!--## DEPRICATED. LEFT FOR EDUCATION PURPOSES. grid_filters - show individual filters for each column has_filters - draw filter section in "View" menu in toolbar ##--> <inp2:InitList pass_params="1"/> <!--## this is to avoid recalling prefix as an item in first iterate grid, by col-picker for instance ##--> <inp2:GridSelector grid="$grid" default="$selector" result_to_var="selector"/> <inp2:{$PrefixSpecial}_SaveWarning name="grid_save_warning" main_prefix="$main_prefix"/> <inp2:m_if check="{$PrefixSpecial}_SearchActive" grid="$grid"> <inp2:m_RenderElement design="form_message" pass_params="1"> <inp2:m_phrase name="la_Warning_Filter"/> </inp2:m_RenderElement> </inp2:m_if> <inp2:m_if check="m_ParamEquals" name="per_page" value="-1" inverse="1"> <inp2:m_RenderElement name="grid_pagination" grid="$grid" PrefixSpecial="$PrefixSpecial" main_special="$main_special" search="$search" ajax="$ajax"/> </inp2:m_if> <table width="100%" cellspacing="0" cellpadding="4" class="bordered"> <inp2:m_if check="m_ParamEquals" name="grid_filters" value="1"> <tr class="pagination_bar"> <inp2:{$PrefixSpecial}_IterateGridFields grid="$grid" mode="filter" block="$filter_block" ajax="$ajax" pass_params="1"/> </tr> </inp2:m_if> <tr class="grid-header-row grid-header-row-1"> <inp2:{$PrefixSpecial}_IterateGridFields grid="$grid" mode="header" block="$header_block" ajax="$ajax" pass_params="1"/> </tr> <inp2:m_DefineElement name="_row" td_style=""> <tr class="<inp2:m_odd_even odd="grid-data-row grid-data-row-even" even="grid-data-row"/>" id="<inp2:m_param name="PrefixSpecial"/>_<inp2:Field field="$IdField"/>" sequence="<inp2:m_get param="{$PrefixSpecial}_sequence"/>"><inp2:m_inc param="{$PrefixSpecial}_sequence" by="1"/> <inp2:IterateGridFields grid="$grid" mode="data" block="$data_block" pass_params="1"/> </tr> </inp2:m_DefineElement> <inp2:m_set {$PrefixSpecial}_sequence="1" odd_even="table-color1"/> <inp2:{$PrefixSpecial}_PrintList block="$row_block" per_page="$per_page" main_special="$main_special" /> <inp2:m_DefineElement name="grid_total_td"> <inp2:m_if check="m_Param" name="total"> <td style="<inp2:m_param name="td_style"/>"> <inp2:FieldTotal name="$field" function="$total"/> </td> <inp2:m_else/> <td style="<inp2:m_param name="td_style"/>"> </td> </inp2:m_if> </inp2:m_DefineElement> <inp2:m_if check="m_ParamEquals" name="totals" value="1"> <tr class="totals-row"/> <inp2:IterateGridFields grid="$grid" mode="data" block="$total_block" pass_params="1"/> </tr> </inp2:m_if> </table> <inp2:m_if check="m_ParamEquals" name="ajax" value="0"> <inp2:m_if check="m_GetEquals" name="fw_menu_included" value=""> <link rel="stylesheet" rev="stylesheet" href="<inp2:m_Compress files='incs/nlsmenu.css'/>" type="text/css" /> <script type="text/javascript" src="<inp2:m_Compress files='js/nlsmenu.js|js/nlsmenueffect_1_2_1.js'/>"></script> <script type="text/javascript"> var menuMgr = new NlsMenuManager("mgr"); menuMgr.timeout = 500; menuMgr.flowOverFormElement = true; </script> <inp2:m_set fw_menu_included="1"/> </inp2:m_if> <script type="text/javascript"> <inp2:m_RenderElement name="old_grid_js" mouseover_class="grid-data-row-mouseover" selected_class="grid-data-row-selected:grid-data-row-even-selected" tag_name="tr" pass_params="true"/> </script> </inp2:m_if> <input type="hidden" id="<inp2:m_param name="PrefixSpecial"/>_Sort1" name="<inp2:m_param name="PrefixSpecial"/>_Sort1" value=""> <input type="hidden" id="<inp2:m_param name="PrefixSpecial"/>_Sort1_Dir" name="<inp2:m_param name="PrefixSpecial"/>_Sort1_Dir" value="asc"> </inp2:m_DefineElement> <inp2:m_DefineElement name="old_grid_js" selector="checkbox" ajax="1"> <!--## DEPRICATED. LEFT FOR EDUCATION PURPOSES. ##--> <inp2:GridSelector grid="$grid" default="$selector" result_to_var="selector"/> <inp2:m_if check="m_Param" name="selector"> Grids['<inp2:m_param name="PrefixSpecial"/>'] = new Grid('<inp2:m_param name="PrefixSpecial"/>', 'grid-data-row-selected:grid-data-row-even-selected', ':original', edit, a_toolbar); Grids['<inp2:m_param name="PrefixSpecial"/>'].MouseOverClass = 'grid-data-row-mouseover'; Grids['<inp2:m_param name="PrefixSpecial"/>'].StickySelection = <inp2:m_if check="m_GetConfig" name="StickyGridSelection">true<inp2:m_else/>false</inp2:m_if>; Grids['<inp2:m_param name="PrefixSpecial"/>'].AddItemsByIdMask('<inp2:m_param name="tag_name"/>', /^<inp2:m_param name="PrefixSpecial"/>_([\d\w-]+)/, '<inp2:m_param name="PrefixSpecial"/>[$$ID$$][<inp2:m_param name="IdField"/>]'); Grids['<inp2:m_param name="PrefixSpecial"/>'].InitItems(); <inp2:m_if check="m_Param" name="selector" equals_to="radio"> Grids['<inp2:m_param name="PrefixSpecial"/>'].EnableRadioMode(); </inp2:m_if> </inp2:m_if> <inp2:m_RenderElement name="nlsmenu_declaration" pass_params="true"/> $ViewMenus = new Array('<inp2:m_param name="PrefixSpecial"/>'); </inp2:m_DefineElement> <inp2:m_DefineElement name="grid_ml_selector"> <inp2:m_if check="lang_IsMultiLanguage"> <inp2:m_phrase name="la_fld_Language"/>: <select name="language" onchange="submit_event('<inp2:m_param name='prefix'/>', 'OnPreSaveAndChangeLanguage');"> <inp2:m_DefineElement name="lang_elem"> <option value="<inp2:Field name='LanguageId'/>" <inp2:m_if check="SelectedLanguage" type="data">selected="selected"</inp2:m_if> ><inp2:Field name="LocalName" no_special='no_special' /></option> </inp2:m_DefineElement> <inp2:lang_PrintList render_as="lang_elem"/> </select> <inp2:m_else/> <inp2:m_if check="m_Param" name="pagination"> <inp2:$prefix_SelectParam possible_names="pagination_prefix,prefix" result_to_var="pagination_prefix"/> <inp2:m_RenderElement name="grid_pagination_elem" PrefixSpecial="$pagination_prefix" pass_params="1"/> </inp2:m_if> </inp2:m_if> </inp2:m_DefineElement> Index: branches/5.3.x/core/admin_templates/incs/close_popup.tpl =================================================================== --- branches/5.3.x/core/admin_templates/incs/close_popup.tpl (revision 16394) +++ branches/5.3.x/core/admin_templates/incs/close_popup.tpl (revision 16395) @@ -1,124 +1,130 @@ <inp2:m_NoDebug/> <inp2:m_Set skip_last_template="1"/> <html> <head> <title></title> <script type="text/javascript"> var $is_debug = <inp2:m_if check="m_ConstOn" name="DBG_REDIRECT">true<inp2:m_else/>false</inp2:m_if>; var $use_popups = <inp2:m_if check="adm_UsePopups">true<inp2:m_else/>false</inp2:m_if>; var $redirect_url = '<inp2:m_t t="dummy" opener="u" m_opener="u" no_amp="1" js_escape="1"/>'; var $modal_windows = <inp2:m_if check="adm_UsePopups" mode="modal">true<inp2:m_else/>false</inp2:m_if>; if ($is_debug) { document.write('<a href="#" onclick="proceed_redirect()">' + $redirect_url + '</a>'); } else { proceed_redirect(); } function isset(variable) { if(variable == null) return false; return (typeof(variable) == 'undefined') ? false : true; } function proceed_redirect() { var $opener = getFrame('main').getWindowOpener(window); if ($opener) { // using popups & close_popup called (from anywhere) try { window_close( function() { $opener.onAfterWindowClose($redirect_url, <inp2:m_if check="adm_OpenNewWindow" diff="1">true<inp2:m_else/>false</inp2:m_if>, <inp2:m_if check="m_Get" name="skip_refresh">true<inp2:m_else/>false</inp2:m_if>); } ); } catch (err) { // another website is opened in parent window alert('Error while trying to process redirect in window opener, you should probably close this window.' + "\n" + 'Error message: [' + err.message + ']'); } } else if (!$use_popups) { // not using popups (for editing), but close_popup called (e.g. from selector) window.location.href = rawurldecode($redirect_url); } } // copied from "js/script.js" because it's not included here due performance reasons function getFrame($name) { var $main_window = window; // 1. cycle through popups to get main window try { // will be error, when other site is opened in parent window var $i = 0; while ($main_window.opener) { if ($i == 10) { break; } $main_window = $main_window.opener; $i++; } } catch (err) { // catch Access/Permission Denied error // alert('getFrame.Error: [' + err.description + ']'); return window; } var $frameset = $main_window.parent.frames; + for ($i = 0; $i < $frameset.length; $i++) { - if ($frameset[$i].name == $name) { - return $frameset[$i]; - } + try { + if ($frameset[$i].name == $name) { + return $frameset[$i]; + } + } + catch ( e ) { + // Foreign domain frame detected. + } } return $main_window.parent; } // copied locally, because won't work, when called via getFrame('main').window_close(...); function window_close($close_callback) { // use this instead of "window.close();" if (!$modal_windows) { // don't use other (e.g. parent) window methods to detect type of // variables from given window, because it won't work in IE if (Object.prototype.toString.call($close_callback) === '[object Function]') { // use close callback, because iframe will be removed later in this method $close_callback(); } window.close(); return ; } if (window.name == 'main') { return ; } if ($close_callback !== undefined) { return getFrame('main').TB.remove(null, $close_callback); } return getFrame('main').TB.remove(); } function rawurldecode(str) { if ( str.indexOf('?') != -1 ) { var $parts = str.split('?', 2); return $parts[0] + ($parts.length == 2 ? '?' + rawurldecode($parts[1]) : ''); } return decodeURIComponent((str + '').replace(/%(?![\da-f]{2})/gi, function () { // PHP tolerates poorly formed escape sequences return '%25'; })); } </script> </head> <body> </body> -</html> \ No newline at end of file +</html> Index: branches/5.3.x/core/admin_templates/incs/form_blocks.tpl =================================================================== --- branches/5.3.x/core/admin_templates/incs/form_blocks.tpl (revision 16394) +++ branches/5.3.x/core/admin_templates/incs/form_blocks.tpl (revision 16395) @@ -1,1401 +1,1401 @@ <inp2:m_Set tab_index="1"/> <inp2:m_DefineElement name="combined_header" permission_type="view" perm_section="" perm_prefix="" perm_event="" system_permission="1" title_preset="" tab_preset="" additional_title_render_as="" additional_blue_bar_render_as="" pagination_prefix="" parent="1" grid="Default"> <inp2:m_if check="m_Param" name="perm_section" inverse="1"> <inp2:adm_SectionInfo section="$section" info="perm_section" result_to_var="perm_section"/> </inp2:m_if> <inp2:m_if check="m_Param" name="permission_type"> <inp2:m_RequireLogin permissions="{$perm_section}.{$permission_type}" perm_event="$perm_event" perm_prefix="$perm_prefix" system="$system_permission"/> <inp2:m_else/> <inp2:m_RequireLogin permissions="{$perm_section}" perm_event="$perm_event" perm_prefix="$perm_prefix" system="$system_permission"/> </inp2:m_if> <inp2:m_if check="m_Param" name="prefix" inverse="1"><inp2:adm_SectionInfo section="$section" info="SectionPrefix" result_to_var="prefix"/></inp2:m_if> <inp2:m_if check="m_get" var="m_wid" inverse="1"> <inp2:m_if check="m_GetConfig" name="UseSmallHeader"> <img src="img/spacer.gif" height="8" width="1" alt=""/> <inp2:m_else/> <table cellpadding="0" cellspacing="0" border="0" width="100%"> <!--## <tr<inp2:m_ifnot check="m_ModuleEnabled" module="Proj-Base"> style="background: url(<inp2:adm_SectionInfo section="$section" parent="$parent" info="module_path"/>img/logo_bg.gif) no-repeat top right; height: 55px;"</inp2:m_ifnot>> ##--> <tr> <td valign="top" class="admintitle" align="left" <!--##style="padding-top: 10px; padding-bottom: 10px;"##-->> <img width="46" height="46" src="<inp2:adm_SectionInfo section='$section' parent='$parent' info='module_path'/>img/icons/icon46_<inp2:adm_SectionInfo section='$section' parent='$parent' info='icon'/>.png" align="absmiddle" title="<inp2:adm_SectionInfo section='$section' parent='$parent' info='label' no_editing='1'/>" alt=""/><inp2:adm_SectionInfo section="$section" parent="$parent" info="label"/> </td> <inp2:m_if check="m_Param" name="additional_title_render_as"> <inp2:m_RenderElement name="$additional_title_render_as" pass_params="1"/> </inp2:m_if> </tr> </table> </inp2:m_if> <inp2:m_else/> <inp2:m_if check="m_Param" name="additional_title_render_as"> <table cellpadding="0" cellspacing="0" border="0" width="100%"> <!--## <tr<inp2:m_ifnot check="m_ModuleEnabled" module="Proj-Base"> style="background: url(<inp2:adm_SectionInfo section="$section" parent="$parent" info="module_path"/>img/logo_bg.gif) no-repeat top right; height: 55px;"</inp2:m_ifnot>> ##--> <tr> <inp2:m_RenderElement name="$additional_title_render_as" pass_params="1"/> </tr> </table> </inp2:m_if> </inp2:m_if> <inp2:$prefix_ModifyUnitConfig pass_params="1"/> <inp2:m_if check="m_Param" name="tabs"> <inp2:m_include t="$tabs" pass_params="1"/> </inp2:m_if> <inp2:m_if check="m_Param" name="tab_preset"> <inp2:m_RenderElement name="edit_tabs" prefix="$prefix" preset_name="$tab_preset"/> </inp2:m_if> <table border="0" cellpadding="2" cellspacing="0" class="page-title bordered-no-bottom" width="100%" style="height: 30px;"> <tr> <td nowrap="nowrap" style="vertical-align: middle;"> <inp2:adm_SectionInfo section="$section" info="label" result_to_var="default_title"/> <inp2:adm_SectionInfo section="$section" parent="$parent" info="label" result_to_var="group_title"/> <span class="tablenav_link" id="blue_bar"> <inp2:$prefix_SectionTitle title_preset="$title_preset" section="$section" title="$default_title" group_title="$group_title" cut_first="100" pass_params="true"/> </span> </td> <td align="right" class="tablenav" style="vertical-align: middle;"> <inp2:m_if check="m_Param" name="additional_blue_bar_render_as"> <inp2:m_RenderElement name="$additional_blue_bar_render_as" pass_params="1"/> <inp2:m_else/> <inp2:m_if check="m_Param" name="pagination"> <inp2:$prefix_SelectParam possible_names="pagination_prefix,prefix" result_to_var="pagination_prefix"/> <inp2:m_RenderElement name="grid_pagination_elem" PrefixSpecial="$pagination_prefix" pass_params="1"/> </inp2:m_if> </inp2:m_if> </td> </tr> </table> <script type="text/javascript"> var $visible_toolbar_buttons = <inp2:m_if check="{$prefix}_VisibleToolbarButtons" title_preset="$title_preset">[<inp2:$prefix_VisibleToolbarButtons title_preset="$title_preset"/>]<inp2:m_else/>true</inp2:m_if>; var $allow_dbl_click = ($visible_toolbar_buttons === true) || in_array('dbl-click', $visible_toolbar_buttons); set_window_title( $.trim( $('#blue_bar').text().replace(/\s+/g, ' ') ) + ' - <inp2:m_Phrase label="la_AdministrativeConsole" js_escape="1"/>'); setHelpLink('<inp2:lang.current_Field name="UserDocsUrl" js_escape="1"/>', '<inp2:m_Param name="title_preset" js_escape="1"/>'); </script> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_original_label"> <td><inp2:$prefix.original_Field field="$field" nl2br="1"/></td> </inp2:m_DefineElement> <inp2:m_DefineElement name="subsection" prefix="" fields="" colspan="3"> <inp2:m_if check="m_Param" name="prefix" equals_to=""> <tr class="subsectiontitle"> <td colspan="<inp2:m_param name='colspan'/>"><inp2:m_phrase label="$title"/></td> </tr> <inp2:m_else/> <inp2:m_if check="{$prefix}_FieldsVisible" fields="$fields"> <tr class="subsectiontitle"> <td colspan="<inp2:m_param name='colspan'/>"><inp2:m_phrase label="$title"/></td> <inp2:m_if check="{$prefix}_DisplayOriginal" pass_params="1"> <td><inp2:m_phrase name="$original_title"/></td> </inp2:m_if> </tr> </inp2:m_if> </inp2:m_if> </inp2:m_DefineElement> <inp2:m_DefineElement name="form_message" id="" type="warning"> <table width="100%" cellspacing="0" cellpadding="4" class="warning-table"<inp2:m_if check="m_Param" name="id"> id="<inp2:m_Param name='id'/>"</inp2:m_if>> <tr> <td valign="top" class="form-<inp2:m_Param name='type'/>"> <inp2:m_Param name="content"/> </td> </tr> </table> </inp2:m_DefineElement> <inp2:m_DefineElement name="default_field_caption_element"> <label for="<inp2:m_param name='NamePrefix'/><inp2:{$prefix}_InputName field='$field'/>"> <span class="<inp2:m_if check='{$prefix}_HasError' field='$field'>error-cell</inp2:m_if>"><inp2:m_if check="m_Param" name="title"><inp2:m_phrase label="$title"/></inp2:m_else/><inp2:m_Param name="title_text"/></inp2:m_if></span></span><inp2:m_if check="{$prefix}_IsRequired" field="$field"><span class="field-required"> *</span></inp2:m_if>:<inp2:m_if check="{$prefix}_FieldHintLabel" title_label="$title" direct_label="$hint_label"><span> <img src="<inp2:m_TemplatesBase/>/img/hint_icon.png" width="12" height="13" title="<inp2:$prefix_FieldHintLabel title_label='$title' direct_label='$hint_label' html_escape='1'/>" alt="<inp2:$prefix_FieldHintLabel title_label='$title' direct_label='$hint_label' html_escape='1'/>"/></inp2:m_if> </label> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_field_caption" title="la_fld_{$field}" title_text="" hint_label="" NamePrefix=""> <inp2:m_inc param="tab_index" by="1"/> <td class="label-cell" onmouseover="show_form_error('<inp2:m_Param name='prefix' js_escape='1'/>', '<inp2:m_Param name='field' js_escape='1'/>')" onmouseout="hide_form_error('<inp2:m_Param name='prefix' js_escape='1'/>')"> <inp2:m_if check="m_Param" name="title"> <inp2:m_RenderElement name="$caption_render_as" pass_params="1"/> <inp2:m_else/> <inp2:m_if check="m_Param" name="title_text"> <inp2:m_RenderElement name="$caption_render_as" pass_params="1"/> <inp2:m_else/> </inp2:m_if> </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"/>']['<inp2:m_Param name="field" js_escape="1"/>'] = '<inp2:m_if check="m_Param" name="title"><inp2:m_phrase label="$title" js_escape="1"/></inp2:m_else/><inp2:m_Param name="title_text" js_escape="1"/></inp2:m_if>' </script> </inp2:m_DefineElement> <!--## design default parameters only avaible in design block ##--> <inp2:m_DefineElement name="form_row" error_field_suffix="" block_name="" title="la_fld_{$field}" has_caption="1" caption_render_as="default_field_caption_element" style="" hint_label="" is_last=""> <inp2:m_if check="{$prefix}_FieldVisible" field="$field"> <tr class="<inp2:m_odd_even odd='edit-form-odd' even='edit-form-even'/>" id="<inp2:$prefix_InputName field='$field'/>_row"<inp2:m_if check="m_Param" name="row_style"> style="<inp2:m_Param name='row_style'/>"</inp2:m_if>> <inp2:m_if check="m_Param" name="has_caption"> <inp2:m_RenderElement name="inp_edit_field_caption" field="{$field}{$error_field_suffix}" pass_params="1"/> </inp2:m_if> <inp2:m_Param name="content" pass_params="1"/> <inp2:m_RenderElement name="inp_edit_error" prefix="$prefix" field="{$field}{$error_field_suffix}" block_name="$block_name"/> <inp2:m_if check="{$prefix}_DisplayOriginal" pass_params="1"> <inp2:m_RenderElement prefix="$prefix" field="$field" name="inp_original_label"/> </inp2:m_if> </tr> </inp2:m_if> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_label" style="" format="" db="" as_label="" currency="" no_special="" nl2br="0" with_hidden="0" after_text=""> <inp2:m_RenderElement design="form_row" block_name="inp_label" pass_params="1"> <td class="control-cell" valign="top"> <span style="<inp2:m_Param name='style'/>" id="<inp2:$prefix_InputName field='$field'/>"> <inp2:{$prefix}_Field field="$field" format="$format" as_label="$as_label" currency="$currency" nl2br="$nl2br" no_special="$no_special"/><inp2:m_Param name="after_text"/> </span> <inp2:m_if check="m_Param" name="with_hidden"> <input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' db='$db'/>"> </inp2:m_if> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_id_label"> <inp2:m_ifnot check="Field" field="$field" equals_to="|0"> <inp2:m_RenderElement name="inp_label" pass_params="true"/> </inp2:m_ifnot> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_error" block_name=""> <script type="text/javascript"> add_form_error('<inp2:m_Param name="prefix" js_escape="1"/>', '<inp2:m_Param name="field" js_escape="1"/>', '<inp2:{$prefix}_InputName field="$field"/>', '<inp2:{$prefix}_Error field="$field" js_escape="1"/>', '<inp2:m_Param name="block_name" js_escape="1"/>'); </script> <!--##<td class="error-cell"><inp2:{$prefix}_Error field="$field"/> </td>##--> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_box" class="" format="" maxlength="" onblur="" onchange="" size="" onkeyup="" allow_html="" edit_template="popups/editor" style="width: 100%" after_text="" autocomplete=""> <inp2:m_RenderElement design="form_row" block_name="inp_edit_box" pass_params="1"> <td class="control-cell"> <input style="<inp2:m_Param name='style'/>" type="text" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' format='$format'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" maxlength="<inp2:m_param name='maxlength'/>" class="<inp2:m_param name='class'/>" onblur="<inp2:m_Param name='onblur'/>" onkeyup="<inp2:m_Param name='onkeyup'/>" onchange="<inp2:m_Param name='onchange'/>" autocomplete="<inp2:m_Param name='autocomplete'/>"><inp2:m_Param name="after_text"/> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_timezone" onchange="" style=""> <inp2:m_RenderElement design="form_row" block_name="inp_edit_timezone" pass_params="1"> <td class="control-cell"> <script type="text/javascript"> var $timezones = { 'Africa': ['Africa/Abidjan', 'Africa/Accra', 'Africa/Addis_Ababa', 'Africa/Algiers', 'Africa/Asmara', 'Africa/Asmera', 'Africa/Bamako', 'Africa/Bangui', 'Africa/Banjul', 'Africa/Bissau', 'Africa/Blantyre', 'Africa/Brazzaville', 'Africa/Bujumbura', 'Africa/Cairo', 'Africa/Casablanca', 'Africa/Ceuta', 'Africa/Conakry', 'Africa/Dakar', 'Africa/Dar_es_Salaam', 'Africa/Djibouti', 'Africa/Douala', 'Africa/El_Aaiun', 'Africa/Freetown', 'Africa/Gaborone', 'Africa/Harare', 'Africa/Johannesburg', 'Africa/Kampala', 'Africa/Khartoum', 'Africa/Kigali', 'Africa/Kinshasa', 'Africa/Lagos', 'Africa/Libreville', 'Africa/Lome', 'Africa/Luanda', 'Africa/Lubumbashi', 'Africa/Lusaka', 'Africa/Malabo', 'Africa/Maputo', 'Africa/Maseru', 'Africa/Mbabane', 'Africa/Mogadishu', 'Africa/Monrovia', 'Africa/Nairobi', 'Africa/Ndjamena', 'Africa/Niamey', 'Africa/Nouakchott', 'Africa/Ouagadougou', 'Africa/Porto-Novo', 'Africa/Sao_Tome', 'Africa/Timbuktu', 'Africa/Tripoli', 'Africa/Tunis', 'Africa/Windhoek'], 'America': ['America/Adak', 'America/Anchorage', 'America/Anguilla', 'America/Antigua', 'America/Araguaina', 'America/Argentina/Buenos_Aires', 'America/Argentina/Catamarca', 'America/Argentina/ComodRivadavia', 'America/Argentina/Cordoba', 'America/Argentina/Jujuy', 'America/Argentina/La_Rioja', 'America/Argentina/Mendoza', 'America/Argentina/Rio_Gallegos', 'America/Argentina/Salta', 'America/Argentina/San_Juan', 'America/Argentina/San_Luis', 'America/Argentina/Tucuman', 'America/Argentina/Ushuaia', 'America/Aruba', 'America/Asuncion', 'America/Atikokan', 'America/Atka', 'America/Bahia', 'America/Barbados', 'America/Belem', 'America/Belize', 'America/Blanc-Sablon', 'America/Boa_Vista', 'America/Bogota', 'America/Boise', 'America/Buenos_Aires', 'America/Cambridge_Bay', 'America/Campo_Grande', 'America/Cancun', 'America/Caracas', 'America/Catamarca', 'America/Cayenne', 'America/Cayman', 'America/Chicago', 'America/Chihuahua', 'America/Coral_Harbour', 'America/Cordoba', 'America/Costa_Rica', 'America/Cuiaba', 'America/Curacao', 'America/Danmarkshavn', 'America/Dawson', 'America/Dawson_Creek', 'America/Denver', 'America/Detroit', 'America/Dominica', 'America/Edmonton', 'America/Eirunepe', 'America/El_Salvador', 'America/Ensenada', 'America/Fort_Wayne', 'America/Fortaleza', 'America/Glace_Bay', 'America/Godthab', 'America/Goose_Bay', 'America/Grand_Turk', 'America/Grenada', 'America/Guadeloupe', 'America/Guatemala', 'America/Guayaquil', 'America/Guyana', 'America/Halifax', 'America/Havana', 'America/Hermosillo', 'America/Indiana/Indianapolis', 'America/Indiana/Knox', 'America/Indiana/Marengo', 'America/Indiana/Petersburg', 'America/Indiana/Tell_City', 'America/Indiana/Vevay', 'America/Indiana/Vincennes', 'America/Indiana/Winamac', 'America/Indianapolis', 'America/Inuvik', 'America/Iqaluit', 'America/Jamaica', 'America/Jujuy', 'America/Juneau', 'America/Kentucky/Louisville', 'America/Kentucky/Monticello', 'America/Knox_IN', 'America/La_Paz', 'America/Lima', 'America/Los_Angeles', 'America/Louisville', 'America/Maceio', 'America/Managua', 'America/Manaus', 'America/Marigot', 'America/Martinique', 'America/Matamoros', 'America/Mazatlan', 'America/Mendoza', 'America/Menominee', 'America/Merida', 'America/Mexico_City', 'America/Miquelon', 'America/Moncton', 'America/Monterrey', 'America/Montevideo', 'America/Montreal', 'America/Montserrat', 'America/Nassau', 'America/New_York', 'America/Nipigon', 'America/Nome', 'America/Noronha', 'America/North_Dakota/Center', 'America/North_Dakota/New_Salem', 'America/Ojinaga', 'America/Panama', 'America/Pangnirtung', 'America/Paramaribo', 'America/Phoenix', 'America/Port-au-Prince', 'America/Port_of_Spain', 'America/Porto_Acre', 'America/Porto_Velho', 'America/Puerto_Rico', 'America/Rainy_River', 'America/Rankin_Inlet', 'America/Recife', 'America/Regina', 'America/Resolute', 'America/Rio_Branco', 'America/Rosario', 'America/Santa_Isabel', 'America/Santarem', 'America/Santiago', 'America/Santo_Domingo', 'America/Sao_Paulo', 'America/Scoresbysund', 'America/Shiprock', 'America/St_Barthelemy', 'America/St_Johns', 'America/St_Kitts', 'America/St_Lucia', 'America/St_Thomas', 'America/St_Vincent', 'America/Swift_Current', 'America/Tegucigalpa', 'America/Thule', 'America/Thunder_Bay', 'America/Tijuana', 'America/Toronto', 'America/Tortola', 'America/Vancouver', 'America/Virgin', 'America/Whitehorse', 'America/Winnipeg', 'America/Yakutat', 'America/Yellowknife'], 'Antarctica': ['Antarctica/Casey', 'Antarctica/Davis', 'Antarctica/DumontDUrville', 'Antarctica/Macquarie', 'Antarctica/Mawson', 'Antarctica/McMurdo', 'Antarctica/Palmer', 'Antarctica/Rothera', 'Antarctica/South_Pole', 'Antarctica/Syowa', 'Antarctica/Vostok'], 'Arctic': ['Arctic/Longyearbyen'], 'Asia': ['Asia/Aden', 'Asia/Almaty', 'Asia/Amman', 'Asia/Anadyr', 'Asia/Aqtau', 'Asia/Aqtobe', 'Asia/Ashgabat', 'Asia/Ashkhabad', 'Asia/Baghdad', 'Asia/Bahrain', 'Asia/Baku', 'Asia/Bangkok', 'Asia/Beirut', 'Asia/Bishkek', 'Asia/Brunei', 'Asia/Calcutta', 'Asia/Choibalsan', 'Asia/Chongqing', 'Asia/Chungking', 'Asia/Colombo', 'Asia/Dacca', 'Asia/Damascus', 'Asia/Dhaka', 'Asia/Dili', 'Asia/Dubai', 'Asia/Dushanbe', 'Asia/Gaza', 'Asia/Harbin', 'Asia/Ho_Chi_Minh', 'Asia/Hong_Kong', 'Asia/Hovd', 'Asia/Irkutsk', 'Asia/Istanbul', 'Asia/Jakarta', 'Asia/Jayapura', 'Asia/Jerusalem', 'Asia/Kabul', 'Asia/Kamchatka', 'Asia/Karachi', 'Asia/Kashgar', 'Asia/Kathmandu', 'Asia/Katmandu', 'Asia/Kolkata', 'Asia/Krasnoyarsk', 'Asia/Kuala_Lumpur', 'Asia/Kuching', 'Asia/Kuwait', 'Asia/Macao', 'Asia/Macau', 'Asia/Magadan', 'Asia/Makassar', 'Asia/Manila', 'Asia/Muscat', 'Asia/Nicosia', 'Asia/Novokuznetsk', 'Asia/Novosibirsk', 'Asia/Omsk', 'Asia/Oral', 'Asia/Phnom_Penh', 'Asia/Pontianak', 'Asia/Pyongyang', 'Asia/Qatar', 'Asia/Qyzylorda', 'Asia/Rangoon', 'Asia/Riyadh', 'Asia/Saigon', 'Asia/Sakhalin', 'Asia/Samarkand', 'Asia/Seoul', 'Asia/Shanghai', 'Asia/Singapore', 'Asia/Taipei', 'Asia/Tashkent', 'Asia/Tbilisi', 'Asia/Tehran', 'Asia/Tel_Aviv', 'Asia/Thimbu', 'Asia/Thimphu', 'Asia/Tokyo', 'Asia/Ujung_Pandang', 'Asia/Ulaanbaatar', 'Asia/Ulan_Bator', 'Asia/Urumqi', 'Asia/Vientiane', 'Asia/Vladivostok', 'Asia/Yakutsk', 'Asia/Yekaterinburg', 'Asia/Yerevan'], 'Atlantic': ['Atlantic/Azores', 'Atlantic/Bermuda', 'Atlantic/Canary', 'Atlantic/Cape_Verde', 'Atlantic/Faeroe', 'Atlantic/Faroe', 'Atlantic/Jan_Mayen', 'Atlantic/Madeira', 'Atlantic/Reykjavik', 'Atlantic/South_Georgia', 'Atlantic/St_Helena', 'Atlantic/Stanley'], 'Australia': ['Australia/ACT', 'Australia/Adelaide', 'Australia/Brisbane', 'Australia/Broken_Hill', 'Australia/Canberra', 'Australia/Currie', 'Australia/Darwin', 'Australia/Eucla', 'Australia/Hobart', 'Australia/LHI', 'Australia/Lindeman', 'Australia/Lord_Howe', 'Australia/Melbourne', 'Australia/North', 'Australia/NSW', 'Australia/Perth', 'Australia/Queensland', 'Australia/South', 'Australia/Sydney', 'Australia/Tasmania', 'Australia/Victoria', 'Australia/West', 'Australia/Yancowinna'], 'Europe': ['Europe/Amsterdam', 'Europe/Andorra', 'Europe/Athens', 'Europe/Belfast', 'Europe/Belgrade', 'Europe/Berlin', 'Europe/Bratislava', 'Europe/Brussels', 'Europe/Bucharest', 'Europe/Budapest', 'Europe/Chisinau', 'Europe/Copenhagen', 'Europe/Dublin', 'Europe/Gibraltar', 'Europe/Guernsey', 'Europe/Helsinki', 'Europe/Isle_of_Man', 'Europe/Istanbul', 'Europe/Jersey', 'Europe/Kaliningrad', 'Europe/Kiev', 'Europe/Lisbon', 'Europe/Ljubljana', 'Europe/London', 'Europe/Luxembourg', 'Europe/Madrid', 'Europe/Malta', 'Europe/Mariehamn', 'Europe/Minsk', 'Europe/Monaco', 'Europe/Moscow', 'Europe/Nicosia', 'Europe/Oslo', 'Europe/Paris', 'Europe/Podgorica', 'Europe/Prague', 'Europe/Riga', 'Europe/Rome', 'Europe/Samara', 'Europe/San_Marino', 'Europe/Sarajevo', 'Europe/Simferopol', 'Europe/Skopje', 'Europe/Sofia', 'Europe/Stockholm', 'Europe/Tallinn', 'Europe/Tirane', 'Europe/Tiraspol', 'Europe/Uzhgorod', 'Europe/Vaduz', 'Europe/Vatican', 'Europe/Vienna', 'Europe/Vilnius', 'Europe/Volgograd', 'Europe/Warsaw', 'Europe/Zagreb', 'Europe/Zaporozhye', 'Europe/Zurich'], 'Indian': ['Indian/Antananarivo', 'Indian/Chagos', 'Indian/Christmas', 'Indian/Cocos', 'Indian/Comoro', 'Indian/Kerguelen', 'Indian/Mahe', 'Indian/Maldives', 'Indian/Mauritius', 'Indian/Mayotte', 'Indian/Reunion'], 'Pacific': ['Pacific/Apia', 'Pacific/Auckland', 'Pacific/Chatham', 'Pacific/Easter', 'Pacific/Efate', 'Pacific/Enderbury', 'Pacific/Fakaofo', 'Pacific/Fiji', 'Pacific/Funafuti', 'Pacific/Galapagos', 'Pacific/Gambier', 'Pacific/Guadalcanal', 'Pacific/Guam', 'Pacific/Honolulu', 'Pacific/Johnston', 'Pacific/Kiritimati', 'Pacific/Kosrae', 'Pacific/Kwajalein', 'Pacific/Majuro', 'Pacific/Marquesas', 'Pacific/Midway', 'Pacific/Nauru', 'Pacific/Niue', 'Pacific/Norfolk', 'Pacific/Noumea', 'Pacific/Pago_Pago', 'Pacific/Palau', 'Pacific/Pitcairn', 'Pacific/Ponape', 'Pacific/Port_Moresby', 'Pacific/Rarotonga', 'Pacific/Saipan', 'Pacific/Samoa', 'Pacific/Tahiti', 'Pacific/Tarawa', 'Pacific/Tongatapu', 'Pacific/Truk', 'Pacific/Wake', 'Pacific/Wallis', 'Pacific/Yap'], 'Others': ['UTC'] } function change_timezone_group($timezone_group) { var $target = $( jq('#<inp2:InputName name="$field"/>') ); $target.hide().empty(); $target.append('<option value=""></option>'); if ($timezone_group == '') { return ; } for (var $i = 0; $i < $timezones[$timezone_group].length; $i++) { var $timezone = $timezones[$timezone_group][$i]; var $selected = $timezone == $current_timezone ? ' selected="selected"' : ''; $target.append('<option value="' + $timezone + '" ' + $selected + '>' + $timezone + '</option>'); } $target.show(); } var $current_timezone = '<inp2:Field name="$field"/>'; $(document).ready( function () { $('#timezone_group').change( function ($e) { change_timezone_group( $(this).val() ); } ); var $current_timezone_group = ''; for (var $timezone_group in $timezones) { if (in_array($current_timezone, $timezones[$timezone_group])) { $current_timezone_group = $timezone_group; break; } } $('#timezone_group').val($current_timezone_group).change(); } ); </script> <select id="timezone_group" tabindex="<inp2:m_Get name='tab_index'/>"> <option value=""></option> <option value="Africa">Africa</option> <option value="America">America</option> <option value="Antarctica">Antarctica</option> <option value="Arctic">Arctic</option> <option value="Asia">Asia</option> <option value="Atlantic">Atlantic</option> <option value="Australia">Australia</option> <option value="Europe">Europe</option> <option value="Indian">Indian</option> <option value="Pacific">Pacific</option> <option value="Others">Others</option> </select> <select tabindex="<inp2:m_Get name='tab_index'/>" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" onchange="<inp2:m_Param name='onchange'/>" style="<inp2:m_Param name='style'/>"></select> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_password" class="" size="" style=""> <inp2:m_RenderElement design="form_row" block_name="inp_edit_password" pass_params="1"> <td class="control-cell"> <input style="<inp2:m_Param name='style'/>" autocomplete="off" type="password" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field name='{$field}_plain'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" class="<inp2:m_param name='class'/>" /> <script type="text/javascript"> $(document).ready( function() { <inp2:m_ifnot check="{$prefix}_Field" name="{$field}_plain"> $('#' + jq('<inp2:{$prefix}_InputName field="$field"/>')).val(''); </inp2:m_ifnot> } ); </script> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_upload" class="" size="" thumbnail="" style=""> <inp2:m_RenderElement design="form_row" block_name="inp_edit_upload" pass_params="1"> <td class="control-cell"> <inp2:m_if check="m_Param" name="thumbnail"> <inp2:m_if check="{$prefix}_FieldEquals" name="$field" value="" inverse="inverse"> <img src="<inp2:{$prefix}_Field field='$field' format='resize:{$thumbnail}'/>" alt=""/><br /> <table cellpadding="0" cellspacing="0"> <tr> <td> <input type="hidden" id="<inp2:{$prefix}_InputName field='Delete{$field}'/>" name="<inp2:{$prefix}_InputName field='Delete{$field}'/>" value="0" /> <input type="checkbox" id="_cb_<inp2:{$prefix}_InputName field='Delete{$field}'/>" tabindex="<inp2:m_Get name='tab_index'/>" onchange="update_checkbox(this, document.getElementById('<inp2:{$prefix}_InputName field='Delete{$field}'/>'));"> </td> <td> <label for="_cb_<inp2:{$prefix}_InputName field='Delete{$field}'/>"><inp2:m_phrase name="la_btn_Delete"/></label> </td> </tr> </table> </inp2:m_if> <input type="file" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" class="<inp2:m_param name='class'/>"> <inp2:m_else/> <input type="file" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" class="<inp2:m_param name='class'/>"> <inp2:m_if check="{$prefix}_FieldEquals" name="$field" value="" inverse="inverse"> (<inp2:{$prefix}_Field field="$field"/>) </inp2:m_if> </inp2:m_if> <input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[upload]" id="<inp2:{$prefix}_InputName field='$field'/>[upload]" value="<inp2:{$prefix}_Field field='$field'/>"> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_box_ml"> <inp2:m_RenderElement name="inp_edit_box" format="no_default" pass_params="true"/> <!--## <inp2:m_RenderElement design="form_row" block_name="inp_edit_box_ml" pass_params="1"> <td class="label-cell" valign="top"> <span class="<inp2:m_if check='{$prefix}_HasError' field='$field'>error-cell</inp2:m_if>" > <inp2:m_phrase label="$title"/><inp2:m_if check="{$prefix}_IsRequired" field="$field"><span class="field-required"> *</span></inp2:m_if>:</span><br> <a href="javascript:PreSaveAndOpenTranslator('<inp2:m_param name='prefix'/>', '<inp2:m_param name='field'/>', 'popups/translator');" title="<inp2:m_Phrase label='la_Translate'/>"><img src="img/icons/icon24_translate.png" style="cursor:hand" border="0"></a> </td> <td class="control-cell"> <input style="<inp2:m_Param name='style'/>" type="text" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' format='no_default'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" maxlength="<inp2:m_param name='maxlength'/>" class="<inp2:m_param name='class'/>" onblur="<inp2:m_Param name='onblur'/>"> </td> </inp2:m_RenderElement> ##--> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_swf_upload" LinkPrefix="m" LinkTag="t" class="" style=""> <inp2:m_RenderElement design="form_row" block_name="inp_edit_swf_upload" pass_params="1"> <td class="control-cell"> <inp2:m_ifnot check="m_Get" name="plupload_included"> <script type="text/javascript" src="<inp2:m_Compress files='js/uploader/moxie.js|js/uploader/plupload.dev.js'/>"></script> <inp2:m_Set plupload_included="1"/> </inp2:m_ifnot> <div id="<inp2:{$prefix}_InputName field='$field'/>_container"> <button class="button button-disabled" disabled id="<inp2:{$prefix}_InputName field='$field'/>_browse_button">Browse</button> </div> <div id="<inp2:{$prefix}_InputName field='$field'/>_queueinfo" class="uploader-queue"></div> <input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[upload]" id="<inp2:{$prefix}_InputName field='$field'/>[upload]" value="<inp2:{$prefix}_Field field='$field' format='file_names'/>"> <input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[order]" id="<inp2:{$prefix}_InputName field='$field'/>[order]" value="<inp2:{$prefix}_Field field='$field' format='file_names'/>"> <input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[tmp_ids]" id="<inp2:{$prefix}_InputName field='$field'/>[tmp_ids]" value=""> <input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[tmp_names]" id="<inp2:{$prefix}_InputName field='$field'/>[tmp_names]" value=""> <input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[tmp_deleted]" id="<inp2:{$prefix}_InputName field='$field'/>[tmp_deleted]" value=""> <script type="text/javascript"> UploadsManager.AddUploader('<inp2:{$prefix}_InputName field="$field"/>', { baseUrl: '<inp2:m_TemplatesBase />', allowedFiletypesDescription : '<inp2:{$prefix}_FieldOption field="$field" option="files_description" result_to_var="files_description"/><inp2:m_Phrase name="$files_description" js_escape="1"/>', allowedFiletypes : '<inp2:{$prefix}_FieldOption field="$field" option="file_types"/>', allowedFilesize : '<inp2:{$prefix}_FieldOption field="$field" option="max_size"/>', multiple : '<inp2:{$prefix}_FieldOption field="$field" option="multiple"/>', prefix : '<inp2:m_Param name="prefix"/>', field : '<inp2:m_Param name="field"/>', thumb_format: '<inp2:{$prefix}_FieldOption field="$field" option="thumb_format"/>', urls : '<inp2:{$prefix}_Field field="$field" format="file_urls" no_special="1" js_escape="1"/>', names : '<inp2:{$prefix}_Field field="$field" format="file_names" no_special="1" js_escape="1"/>', sizes : '<inp2:{$prefix}_Field field="$field" format="file_sizes" no_special="1" js_escape="1"/>', flashsid : '<inp2:m_SID/>', uploadURL : '<inp2:{$LinkPrefix}_{$LinkTag} pass="all,$prefix" {$prefix}_event="OnUploadFile" no_amp="1" js_escape="1"/>', deleteURL : '<inp2:{$LinkPrefix}_{$LinkTag} pass="all,$prefix" {$prefix}_event="OnDeleteFile" field_id="#FIELD_ID#" file="#FILE#" no_amp="1" js_escape="1"/>', previewURL : '<inp2:{$LinkPrefix}_{$LinkTag} pass="all,$prefix" {$prefix}_event="OnViewFile" field="#FIELD#" file="#FILE#" no_amp="1" js_escape="1"/>', ajax: <inp2:m_if check="m_Get" name="ajax" equals_to="yes">true<inp2:m_else/>false</inp2:m_if> } ) </script> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_hidden" db=""> <input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' db='$db'/>"> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_date" class="" error_field_suffix="_date"> <inp2:m_RenderElement design="form_row" block_name="inp_edit_date" pass_params="1"> <td class="control-cell"> <input type="text" name="<inp2:{$prefix}_InputName field='{$field}_date'/>" id="<inp2:{$prefix}_InputName field='{$field}_date'/>" value="<inp2:{$prefix}_Field field='{$field}_date' format='_input_'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:{$prefix}_Format field='{$field}_date' input_format='1' edit_size='edit_size'/>" class="<inp2:m_param name='class'/>" datepickerIcon="<inp2:m_ProjectBase/>core/admin_templates/img/calendar_icon.gif"> <img src="img/calendar_icon.gif" id="cal_img_<inp2:{$prefix}_InputName field='{$field}'/>" style="cursor: pointer; margin-right: 5px" title="Date selector" /> <span class="small">(<inp2:{$prefix}_Format field="{$field}_date" input_format="1" human="true"/>)</span> <script type="text/javascript"> Calendar.setup({ inputField : "<inp2:{$prefix}_InputName field='{$field}_date'/>", ifFormat : Calendar.phpDateFormat("<inp2:{$prefix}_Format field='{$field}_date' input_format='1'/>"), button : "cal_img_<inp2:{$prefix}_InputName field='{$field}'/>", align : "br", singleClick : true, showsTime : true, weekNumbers : false, firstDay : <inp2:m_GetConfig var="FirstDayOfWeek"/>, onUpdate : function(cal) { runOnChange('<inp2:$prefix_InputName field='{$field}_date'/>'); } }); </script> <input type="hidden" name="<inp2:{$prefix}_InputName field='{$field}_time'/>" id="<inp2:{$prefix}_InputName field='{$field}_time' input_format='1'/>" value=""> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_time" class="" error_field_suffix="_time"> <inp2:m_RenderElement design="form_row" block_name="inp_edit_time" pass_params="1"> <td class="control-cell"> <input type="text" name="<inp2:{$prefix}_InputName field='{$field}_time'/>" id="<inp2:{$prefix}_InputName field='{$field}_time'/>" value="<inp2:{$prefix}_Field field='{$field}_time' format='_input_'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:{$prefix}_Format field='{$field}_time' input_format='1' edit_size='edit_size'/>" class="<inp2:m_param name='class'/>"> <span class="small">(<inp2:{$prefix}_Format field="{$field}_time" input_format="1" human="true"/>)</span> <input type="hidden" name="<inp2:{$prefix}_InputName field='{$field}_date'/>" id="<inp2:{$prefix}_InputName field='{$field}_date' input_format='1'/>" value=""> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_date_time" class=""> <inp2:m_RenderElement design="form_row" block_name="inp_edit_date_time" pass_params="1"> <td class="control-cell"> <input type="text" name="<inp2:{$prefix}_InputName field='{$field}_date'/>" id="<inp2:{$prefix}_InputName field='{$field}_date'/>" value="<inp2:{$prefix}_Field field='{$field}_date' format='_input_'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:{$prefix}_Format field='{$field}_date' input_format='1' edit_size='edit_size'/>" class="<inp2:m_param name='class'/>" datepickerIcon="<inp2:m_ProjectBase/>core/admin_templates/img/calendar_icon.gif"> <img src="img/calendar_icon.gif" id="cal_img_<inp2:{$prefix}_InputName field="{$field}"/>" style="cursor: pointer; margin-right: 5px" title="Date selector" /> <span class="small">(<inp2:{$prefix}_Format field="{$field}_date" input_format="1" human="true"/>)</span> - <input type="hidden" id="full_date_<inp2:{$prefix}_InputName field='{$field}'/>" value="<inp2:{$prefix}_Field field='{$field}' format=''/>" /> + <input type="hidden" id="full_date_<inp2:{$prefix}_InputName field='{$field}'/>" value="<inp2:{$prefix}_Field field='{$field}' format='picker'/>" /> <script type="text/javascript"> Calendar.setup({ inputField : "full_date_<inp2:{$prefix}_InputName field='{$field}'/>", ifFormat : Calendar.phpDateFormat("<inp2:{$prefix}_Format field='{$field}' input_format='1'/>"), button : "cal_img_<inp2:{$prefix}_InputName field='{$field}'/>", align : "br", singleClick : true, showsTime : true, weekNumbers : false, firstDay : <inp2:m_GetConfig var="FirstDayOfWeek"/>, onUpdate : function(cal) { document.getElementById('<inp2:{$prefix}_InputName field="{$field}_date"/>').value = cal.date.print( Calendar.phpDateFormat("<inp2:{$prefix}_Format field="{$field}_date" input_format="1"/>") ); document.getElementById('<inp2:{$prefix}_InputName field="{$field}_time"/>').value = cal.date.print( Calendar.phpDateFormat("<inp2:{$prefix}_Format field="{$field}_time" input_format="1"/>") ); } }); </script> <input type="text" name="<inp2:{$prefix}_InputName field='{$field}_time'/>" id="<inp2:{$prefix}_InputName field='{$field}_time'/>" value="<inp2:{$prefix}_Field field='{$field}_time' format='_input_'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:{$prefix}_Format field='{$field}_time' input_format='1' edit_size='edit_size'/>" class="<inp2:m_param name='class'/>"><span class="small"> (<inp2:{$prefix}_Format field="{$field}_time" input_format="1" human="true"/>)</span> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_date_time_combined" class=""> <inp2:m_RenderElement design="form_row" block_name="inp_edit_date_time_combined" pass_params="1"> <td class="control-cell"> <input type="text" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' format='_input_'/>" tabindex="<inp2:m_get param='tab_index'/>" size="<inp2:{$prefix}_Format field='$field' input_format='1' edit_size='edit_size'/>" class="<inp2:m_param name='class'/>" datepickerIcon="<inp2:m_ProjectBase/>core/admin_templates/img/calendar_icon.gif"/> <img src="img/calendar_icon.gif" id="cal_img_<inp2:{$prefix}_InputName field="{$field}"/>" style="cursor: pointer; margin-right: 5px" title="Date selector" /> <span class="small">(<inp2:{$prefix}_Format field="$field" input_format="1" human="true"/>)</span> <script type="text/javascript"> Calendar.setup({ inputField : "<inp2:{$prefix}_InputName field='{$field}'/>", ifFormat : Calendar.phpDateFormat("<inp2:{$prefix}_Format field='{$field}' input_format='1'/>"), button : "cal_img_<inp2:{$prefix}_InputName field='{$field}'/>", align : "br", singleClick : true, showsTime : true, weekNumbers : false, firstDay : <inp2:m_GetConfig var="FirstDayOfWeek"/> }); </script> <input type="hidden" name="<inp2:{$prefix}_InputName field='{$field}_combined'/>" value="1"/> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="textarea_field_caption_element"> <inp2:m_RenderElement name="default_field_caption_element" pass_params="1"/> <inp2:m_if check="m_Param" name="allow_html"> <br> <inp2:{$prefix}_InputName field="$field" result_to_var="input_name"/> <a href="<inp2:m_Link template='$edit_template' TargetField='$input_name' pass_through='TargetField' pass='m,$prefix'/>" onclick="openSelector('<inp2:m_Param name='prefix' js_escape='1'/>', this.href, '', '950x600'); return false;"> <img src="img/icons/icon24_link_editor.gif" border="0"> </a> </inp2:m_if> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_textarea" caption_render_as="textarea_field_caption_element" class="" format="" edit_template="popups/editor" allow_html="" style="text-align: left; width: 100%; height: 100px;" control_options="false" row_style="height: auto"> <inp2:m_RenderElement design="form_row" block_name="inp_edit_textarea" pass_params="1"> <td class="control-cell"> <textarea style="<inp2:m_Param name='style'/>" tabindex="<inp2:m_Get name='tab_index'/>" id="<inp2:{$prefix}_InputName field='$field'/>" name="<inp2:{$prefix}_InputName field='$field'/>" ><inp2:{$prefix}_Field field="$field" format="fck_ready;{$format}"/></textarea> <script type="text/javascript"> Form.addControl('<inp2:{$prefix}_InputName field="$field"/>', <inp2:m_param name="control_options"/>); </script> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_fck" class="" title="la_fld_{$field}" maxlength="" bgcolor="" body_class="" body_id="" onblur="" format="" size="" onkeyup="" style="" has_caption="0" control_options="false"> <inp2:m_RenderElement design="form_row" block_name="inp_edit_fck" pass_params="1"> <td class="control-cell" style="padding: 0px;" colspan="3" onmouseover="show_form_error('<inp2:m_Param name='prefix' js_escape='1'/>', '<inp2:m_Param name='field' js_escape='1'/>')" onmouseout="hide_form_error('<inp2:m_Param name='prefix' js_escape='1'/>')"> <inp2:FCKEditor field="$field" width="100%" height="200" bgcolor="$bgcolor" body_class="$body_class" body_id="$body_id" format="$format" late_load="1"/> <script type="text/javascript"> if (typeof(fields['<inp2:m_Param name="prefix" js_escape="1"/>']) == 'undefined') { fields['<inp2:m_Param name="prefix" js_escape="1"/>'] = new Object(); } fields['<inp2:m_Param name="prefix" js_escape="1"/>']['<inp2:m_Param name="field" js_escape="1"/>'] = '<inp2:m_phrase label="$title" js_escape="1"/>' Form.addControl('<inp2:$prefix_InputName field="$field"/>', <inp2:m_param name="control_options"/>); </script> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_codepress" style="width: 100%;" language="html" has_caption="0" control_options="false"> <inp2:m_RenderElement design="form_row" block_name="inp_edit_codepress" pass_params="1"> <td class="control-cell" colspan="3" onmouseover="show_form_error('<inp2:m_Param name='prefix' js_escape='1'/>', '<inp2:m_Param name='field' js_escape='1'/>')" onmouseout="hide_form_error('<inp2:m_Param name='prefix' js_escape='1'/>')"> <inp2:m_ifnot check="m_Get" name="codepress_included"> <script type="text/javascript" src="<inp2:m_TemplatesBase/>/themes/codepress/codepress.js"></script> <script type="text/javascript"> CodePress.path = '<inp2:m_TemplatesBase/>/themes/codepress/'; // set path here, because script tags are not found in table cells </script> <inp2:m_Set codepress_included="1"/> </inp2:m_ifnot> <textarea id="<inp2:$prefix_InputName field='$field'/>" name="<inp2:$prefix_InputName field='$field'/>" class="codepress <inp2:m_Param name='language'/>" style="<inp2:m_Param name='style'/>"><inp2:$prefix_Field field="$field"/></textarea> <script type="text/javascript"> Application.setHook( new Array ('<inp2:m_Param name="prefix" js_escape="1"/>:OnPreSaveAndGoToTab', '<inp2:m_Param name="prefix" js_escape="1"/>:OnPreSaveAndGo', '<inp2:m_Param name="prefix" js_escape="1"/>:OnSave', '<inp2:m_Param name="prefix" js_escape="1"/>:OnCreate', '<inp2:m_Param name="prefix" js_escape="1"/>:OnUpdate'), function($event) { <inp2:m_Param name="field"/>.toggleEditor(); // enable textarea back to save data $event.status = true; } ); if (typeof(fields['<inp2:m_Param name="prefix" js_escape="1"/>']) == 'undefined') { fields['<inp2:m_Param name="prefix" js_escape="1"/>'] = new Object(); } fields['<inp2:m_Param name="prefix" js_escape="1"/>']['<inp2:m_Param name="field" js_escape="1"/>'] = '<inp2:m_phrase label="$title" js_escape="1"/>' Form.addControl('<inp2:$prefix_InputName field="$field"/>', <inp2:m_param name="control_options"/>); </script> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="code_mirror_resource"> <inp2:m_if check="m_Param" name="resource_extension" equals_to="js"> <script src="<inp2:m_TemplatesBase/>/incs/code_mirror/mode/<inp2:m_Param name='resource_file'/>"></script> <inp2:m_elseif check="m_Param" name="resource_extension" equals_to="js"/> <link rel="stylesheet" href="<inp2:m_TemplatesBase/>/incs/code_mirror/mode/<inp2:m_Param name='resource_file'/>"/> </inp2:m_if> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_codemirror" caption_render_as="textarea_field_caption_element" class="" format="" edit_template="popups/editor" allow_html="" style="text-align: left; width: 100%; height: 100px;" control_options="false" row_style="height: auto" codemirror_options="lineNumbers: true, styleActiveLine: true"> <inp2:m_RenderElement design="form_row" pass_params="1"> <td class="control-cell"> <textarea tabindex="<inp2:m_Get name='tab_index'/>" id="<inp2:{$prefix}_InputName field='$field'/>" name="<inp2:{$prefix}_InputName field='$field'/>" ><inp2:{$prefix}_Field field="$field" format="fck_ready;{$format}"/></textarea> <inp2:m_ifnot check="m_Get" name="codemirror_included"> <script src="<inp2:m_Compress files='incs/code_mirror/lib/codemirror.js|incs/code_mirror/addon/selection/active-line.js'/>"></script> <link rel="stylesheet" href="<inp2:m_Compress files='incs/code_mirror/lib/codemirror.css'/>"> <inp2:m_Set codemirror_included="1"/> </inp2:m_ifnot> <inp2:adm_IncludeCodeMirrorFilesByLanguage language="$language" render_as="code_mirror_resource"/> <script type="text/javascript"> $CodeMirrorEditors["<inp2:$prefix_InputName field='$field'/>"] = {mode: "<inp2:m_Param name='language'/>", tabindex: <inp2:m_Get name="tab_index"/>, indentWithTabs: true, indentUnit: 4, lineWrapping: true<inp2:m_if check="m_Param" name="codemirror_options">, <inp2:m_Param name="codemirror_options"/></inp2:m_if>}; Form.addControl('<inp2:{$prefix}_InputName field="$field"/>', <inp2:m_param name="control_options"/>); </script> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="textarea_ml_field_caption_element"> <inp2:m_RenderElement name="default_field_caption_element" pass_params="1"/> <br> <inp2:m_if check="m_ParamEquals" name="allow_html" value="allow_html"> <inp2:{$prefix}_InputName field="$field" result_to_var="input_name"/> <a href="<inp2:m_Link template='$edit_template' TargetField='$input_name' pass_through='TargetField' pass='m,$prefix'/>" onclick="openSelector('<inp2:m_Param name='prefix' js_escape='1'/>', this.href, '', '800x575'); return false;"> <img src="img/icons/icon24_link_editor.gif" style="cursor: hand;" border="0"> </a> </inp2:m_if> <a href="javascript:PreSaveAndOpenTranslator('<inp2:m_param name="prefix"/>', '<inp2:m_param name="field"/>', 'popups/translator', 1);" title="<inp2:m_Phrase label='la_Translate'/>"><img src="img/icons/icon24_translate.png" border="0"></a> </inp2:m_DefineElement> <inp2:m_DefineElement name="source_language_field_caption_element"> <label for="<inp2:m_param name='NamePrefix'/><inp2:{$prefix}_InputName field='$field'/>"> <span class="<inp2:m_if check='{$prefix}_HasError' field='$field'>error-cell</inp2:m_if>"><inp2:$prefix_SourceLanguageTitle label="$title"/></span><inp2:m_if check="{$prefix}_IsRequired" field="$field"><span class="field-required"> *</span></inp2:m_if>:<inp2:m_if check="{$prefix}_FieldHintLabel" title_label="$title" direct_label="$hint_label"><span> <img src="<inp2:m_TemplatesBase/>/img/hint_icon.png" width="12" height="13" title="<inp2:$prefix_FieldHintLabel title_label='$title' direct_label='$hint_label' html_escape='1'/>" alt="<inp2:$prefix_FieldHintLabel title_label='$title' direct_label='$hint_label' html_escape='1'/>"/></inp2:m_if> </label> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_textarea_ml"> <inp2:m_RenderElement name="inp_edit_textarea" format="no_default" pass_params="true"/> <!--##<inp2:m_RenderElement design="form_row" block_name="inp_edit_textarea_ml" pass_params="1"> <td class="control-cell"> <textarea style="<inp2:m_Param name='style'/>" tabindex="<inp2:m_Get name='tab_index'/>" id="<inp2:{$prefix}_InputName field='$field'/>" name="<inp2:{$prefix}_InputName field='$field'/>" cols="<inp2:m_param name='cols'/>" rows="<inp2:m_param name='rows'/>" class="<inp2:m_param name='class'/>"><inp2:{$prefix}_Field field="$field" format="fck_ready,{$format}"/></textarea> <script type="text/javascript"> Form.addControl('<inp2:{$prefix}_InputName field="$field"/>', <inp2:m_param name="control_options"/>); </script> </td> </inp2:m_RenderElement>##--> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_user" class="" size="" old_style="0" onkeyup=""> <inp2:m_RenderElement design="form_row" block_name="inp_edit_user" pass_params="1"> <td class="control-cell"> <input type="text" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" class="<inp2:m_param name='class'/>" onkeyup="<inp2:m_Param name='onkeyup'/>"> <inp2:m_if check="m_ParamEquals" name="old_style" value="1"> <a href="#" onclick="return OpenUserSelector('','kernel_form','<inp2:{$prefix}_InputName field="$field"/>');"> <inp2:m_else/> <a href="<inp2:m_t t='user_selector' pass='all,$prefix'/>" onclick="openSelector('<inp2:m_param name='prefix' js_escape='1'/>', this.href, '<inp2:m_param name='field' js_escape='1'/>'); return false;"> </inp2:m_if> <img src="img/icons/icon24_link_user.gif" style="cursor:hand;" border="0"> </a> <script type="text/javascript"> function processUserSelector($field, $selector) { var $field_mask = '<inp2:$prefix_InputName name="#FIELD_NAME#"/>'; var $user_id = parseInt( $selector.Grids['u'].GetSelected() ); $( jq('#' + $field_mask.replace('#FIELD_NAME#', $field)) ).val( $selector.$user_logins[$user_id] ); } </script> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_category" class="" size="" old_style="0" onkeyup=""> <inp2:m_RenderElement design="form_row" block_name="inp_edit_category" pass_params="1"> <td class="control-cell"> <inp2:m_RenderElement name="inp_edit_hidden" pass_params="1"/> <table cellpadding="0" cellspacing="0"> <tr> <td id="<inp2:{$prefix}_InputName field='$field'/>_path"<inp2:m_ifnot check="Field" name="$field" db="db"> style="display: none;"</inp2:m_ifnot>> <inp2:$prefix_Field field='$field' db="db" result_to_var="category_id"/> <inp2:m_include template="categories/category_path" category_id="$category_id"/> </td> <td valign="middle"> <img src="img/spacer.gif" width="3" height="1" alt=""/> <a href="<inp2:adm_SelectorLink prefix='$prefix' selection_mode='single' tab_prefixes='none'/>" onclick="openSelector('<inp2:m_param name='prefix' js_escape='1'/>', this.href, '<inp2:m_param name='field' js_escape='1'/>'); return false;"> <img src="img/icons/icon24_cat.gif" width="24" height="24" border="0"/> </a> <a href="#" id="<inp2:{$prefix}_InputName field='$field'/>_disable_link"<inp2:m_ifnot check="Field" name="$field" db="db"> style="display: none;"</inp2:m_ifnot>><inp2:m_Phrase name="la_Text_Disable"/></a> <script type="text/javascript"> function processItemSelector($field, $selector) { var $item_ids = $selector.Grids[$selector.$Catalog.ActivePrefix].GetSelected(); CategorySelector.setCategory($field, $item_ids.length ? $item_ids.shift() : ''); } function CategorySelector() { } CategorySelector.getField = function ($field) { return jq('<inp2:{$prefix}_InputName field="#FIELD_NAME#"/>'.replace('#FIELD_NAME#', $field)); } CategorySelector.setCategory = function ($field, $category_id) { var $field_id = this.getField($field); $('#' + $field_id).val($category_id); $('#' + $field_id + '_path').load( '<inp2:m_Link template="categories/category_path" category_id="#ID#" no_amp="1" js_escape="1"/>'.replace('#ID#', $category_id), function () { $('#' + $field_id + '_path, #' + $field_id + '_disable_link').toggle( $category_id !== '' ); } ); } $(document).ready( function() { var $field_id = CategorySelector.getField('<inp2:m_Param name="field" js_escape="1"/>'); $('#' + $field_id + '_disable_link').click( function ($e) { CategorySelector.setCategory('<inp2:m_Param name="field" js_escape="1"/>', ''); return false; } ); } ); </script> </td> </tr> </table> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_combo_target" title="la_fld_{$field}" caption_render_as="default_field_caption_element" read_only="0" has_empty="" hint_label=""> <inp2:m_RenderElement design="form_row" block_name="inp_edit_combo_target" pass_params="1"> <td class="control-cell"> <inp2:m_if check="m_Param" name="read_only"> <inp2:$prefix_Field name="{$field}Type"/>: "<inp2:$prefix_Field name="$field"/>" <inp2:m_else/> <select tabindex="<inp2:m_get param='tab_index'/>" name="<inp2:{$prefix}_InputName field='{$field}Type'/>" id="<inp2:{$prefix}_InputName field='{$field}Type'/>"> <inp2:{$prefix}_PredefinedOptions field="{$field}Type" block="inp_option_phrase" selected="selected" has_empty="$has_empty"/> </select> <input type="text" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field'/>" tabindex="<inp2:m_get param='tab_index'/>" style="width: 250px;"/> </inp2:m_if> <script type="text/javascript"> fields['<inp2:m_Param name="prefix" js_escape="1"/>']['<inp2:m_Param name="field" js_escape="1"/>Type'] = '<inp2:m_phrase label="{$title}Type" js_escape="1"/>'; $( jq('#<inp2:$prefix_InputName name="$field" js_escape="1"/>') ).autocomplete({ source: function( request, response ) { request['type'] = $( jq('#<inp2:$prefix_InputName name="{$field}Type" js_escape="1"/>') ).val(); $.getJSON( '<inp2:m_Link template="dummy" pass="m,$prefix" {$prefix}_event="OnSuggestAddressJSON" no_amp="1" js_escape="1"/>', request, response ); } }); $(document).ready( function () { $( jq('#<inp2:$prefix_InputName name="{$field}Type"/>') ).change( function ($e) { var $me = $(this); if ($me.prop('disabled')) { return ; } var $type_selected = !isNaN( parseInt( $me.val() ) ); $( jq('#<inp2:$prefix_InputName name="$field"/>') ).prop('disabled', !$type_selected); } ) .change(); } ); </script> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="email_recipients_js" prefix="email-template" to_readonly="auto"> <script type="text/javascript"> Recipients.formatLine = function($record_index) { if (this.Records[$record_index]['RecipientName']) { // name specified if (this.Records[$record_index]['RecipientAddressType'] == 1) { // email this.ResultMask = '#RecipientType#: "#RecipientName#" <#RecipientAddress#>'; } else { // other this.ResultMask = '#RecipientType#: "#RecipientName#" <#RecipientAddress#> - #RecipientAddressType#'; } } else { // name not specified this.ResultMask = '#RecipientType#: #RecipientAddress# - #RecipientAddressType#'; } return MultiInputControl.prototype.formatLine.call(this, $record_index); } Recipients.compareRecords = function($record_a, $record_b) { var $equals = true; var $compare_fields = ['RecipientAddressType', 'RecipientAddress']; for (var $i = 0; $i < $compare_fields.length; $i++) { var $field_name = $compare_fields[$i]; if ($record_a[$field_name] !== $record_b[$field_name]) { return false; } } return $equals; } <inp2:m_if check="m_Param" name="to_readonly" equals_to="auto"> <inp2:$prefix_ToRecipientReadOnly result_to_var="to_readonly"/> </inp2:m_if> <inp2:m_if check="m_Param" name="to_readonly"> Recipients.isReadOnly = function($record_index) { return this.Records[$record_index]['RecipientType'] == <inp2:m_GetConst name="EmailTemplate::RECIPIENT_TYPE_TO"/>; } <inp2:$prefix_RestoreRecipientType/> Recipients.registerControl('RecipientType', {type: 'radio', required: true, options: <inp2:m_RenderElement name="inp_json_options" prefix="$prefix" field="RecipientType" strip_nl="2"/>, 'default': <inp2:m_GetConst name="EmailTemplate::RECIPIENT_TYPE_CC"/>}); <inp2:m_else/> Recipients.registerControl('RecipientType', {type: 'radio', required: true, options: <inp2:m_RenderElement name="inp_json_options" prefix="$prefix" field="RecipientType" strip_nl="2"/>, 'default': <inp2:$prefix_FieldOption field="RecipientType" option="default"/>}); </inp2:m_if> Recipients.registerControl('RecipientAddressType', {type: 'select', required: true, options: <inp2:m_RenderElement name="inp_json_options" prefix="$prefix" field="RecipientAddressType" strip_nl="2"/>, 'default': <inp2:$prefix_FieldOption field="RecipientAddressType" option="default"/>}); Recipients.registerControl('RecipientAddress', {type: 'textbox', required: true, 'default': '<inp2:$prefix_FieldOption field="RecipientAddress" option="default" js_escape="1"/>'}); Recipients.registerControl('RecipientName', {type: 'textbox', required: false, 'default': '<inp2:$prefix_FieldOption field="RecipientName" option="default" js_escape="1"/>'}); Recipients.LoadValues(); </script> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_option_item"> <option value="<inp2:m_param name='key'/>"<inp2:m_param name="selected"/>><inp2:m_param name="option"/></option> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_option_phrase"> <option value="<inp2:m_param name='key'/>"<inp2:m_param name="selected"/>><inp2:m_phrase label="$option"/></option> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_options" onchange="" has_empty="0" empty_value="" empty_label="" style=""> <inp2:m_RenderElement design="form_row" block_name="inp_edit_options" pass_params="1"> <td class="control-cell"> <select tabindex="<inp2:m_Get name='tab_index'/>" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" onchange="<inp2:m_Param name='onchange'/>" style="<inp2:m_Param name='style'/>"> <inp2:m_if check="{$prefix}_FieldOption" field="$field" option="use_phrases"> <inp2:{$prefix}_PredefinedOptions field="$field" block="inp_option_phrase" selected="selected" has_empty="$has_empty" empty_value="$empty_value" empty_label="$empty_label"/> <inp2:m_else/> <inp2:{$prefix}_PredefinedOptions field="$field" block="inp_option_item" selected="selected" has_empty="$has_empty" empty_value="$empty_value" empty_label="$empty_label"/> </inp2:m_if> </select> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_single_option"> <option value="<inp2:m_param name='value'/>"<inp2:m_if check="{$prefix}_Selected" field="$field" value="$value"> selected</inp2:m_if>><inp2:$prefix_OptionValue field="$field" value="$value"/></option> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_multioptions" has_empty="0" empty_value="" style=""> <inp2:m_RenderElement design="form_row" block_name="inp_edit_multioptions" pass_params="1"> <td class="control-cell"> <select multiple tabindex="<inp2:m_Get name='tab_index'/>" id="<inp2:{$prefix}_InputName field='$field'/>_select" onchange="update_multiple_options('<inp2:{$prefix}_InputName field='$field'/>');"> <inp2:m_if check="{$prefix}_FieldOption" field="$field" option="use_phrases"> <inp2:{$prefix}_PredefinedOptions field="$field" block="inp_option_phrase" selected="selected" has_empty="$has_empty" empty_value="$empty_value"/> <inp2:m_else/> <inp2:{$prefix}_PredefinedOptions field="$field" block="inp_option_item" selected="selected" has_empty="$has_empty" empty_value="$empty_value"/> </inp2:m_if> </select> <input type="hidden" id="<inp2:{$prefix}_InputName field='$field'/>" name="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' db='db'/>"/> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_radio_item" onclick="" onchange=""> <input type="radio" <inp2:m_param name="checked"/> name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field="$field"/>_<inp2:m_param name="key"/>" value="<inp2:m_param name="key"/>" tabindex="<inp2:m_Get name='tab_index'/>" onclick="<inp2:m_param name="onclick"/>" onchange="<inp2:m_param name="onchange"/>"><label for="<inp2:{$prefix}_InputName field="$field"/>_<inp2:m_param name="key"/>"><inp2:m_param name="option"/></label> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_radio_phrase" onclick="" onchange=""> <input type="radio" <inp2:m_param name="checked"/> name="<inp2:{$prefix}_InputName field="$field"/>" id="<inp2:{$prefix}_InputName field="$field"/>_<inp2:m_param name="key"/>" value="<inp2:m_param name="key"/>" tabindex="<inp2:m_Get name='tab_index'/>" onclick="<inp2:m_param name="onclick"/>" onchange="<inp2:m_param name="onchange"/>"><label for="<inp2:{$prefix}_InputName field="$field"/>_<inp2:m_param name="key"/>"><inp2:m_phrase label="$option"/></label> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_radio" onclick="" onchange=""> <inp2:m_RenderElement design="form_row" block_name="inp_edit_radio" pass_params="1"> <td class="control-cell"> <inp2:m_if check="{$prefix}_FieldOption" field="$field" option="use_phrases"> <inp2:{$prefix}_PredefinedOptions field="$field" block="inp_radio_phrase" selected="checked" onclick="$onclick" onchange="$onchange" /> <inp2:m_else /> <inp2:{$prefix}_PredefinedOptions field="$field" block="inp_radio_item" selected="checked" onclick="$onclick" onchange="$onchange" /> </inp2:m_if> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="js_option_item"> '<inp2:m_Param name="key" js_escape="1"/>': '<inp2:m_Param name="option" js_escape="1"/>'<inp2:m_ifnot check="m_Param" name="is_last">, </inp2:m_ifnot> </inp2:m_DefineElement> <inp2:m_DefineElement name="js_option_phrase"> '<inp2:m_Param name="key" js_escape="1"/>': '<inp2:m_Phrase name="$option" js_escape="1"/>'<inp2:m_ifnot check="m_Param" name="is_last">, </inp2:m_ifnot> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_json_options"> <inp2:m_if check="{$prefix}_FieldOption" field="$field" option="use_phrases"> {<inp2:{$prefix}_PredefinedOptions field="$field" block="js_option_phrase" selected="selected"/>} <inp2:m_else /> {<inp2:{$prefix}_PredefinedOptions field="$field" block="js_option_item" selected="selected"/>} </inp2:m_if> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_checkbox" field_class="" onchange="" onclick="" NamePrefix="_cb_"> <inp2:m_RenderElement design="form_row" block_name="inp_edit_checkbox" pass_params="1"> <td class="control-cell"> <input type="hidden" id="<inp2:{$prefix}_InputName field='$field'/>" name="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' db='db'/>"/> <input tabindex="<inp2:m_Get name='tab_index'/>" type="checkbox" id="_cb_<inp2:{$prefix}_InputName field='$field'/>" <inp2:{$prefix}_Field field="$field" checked="checked" db="db"/> class="<inp2:m_param name='field_class'/>" onchange="update_checkbox(this, document.getElementById('<inp2:{$prefix}_InputName field='$field'/>'));<inp2:m_param name='onchange'/>" onclick="<inp2:m_param name='onclick'/>"> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_checkbox_item"> <input type="checkbox" <inp2:m_param name='checked'/> id="<inp2:{$prefix}_InputName field='$field'/>_<inp2:m_param name='key'/>" value="<inp2:m_param name='key'/>" tabindex="<inp2:m_Get name='tab_index'/>" onclick="update_checkbox_options(/^<inp2:{$prefix}_InputName field='$field' as_preg='1'/>_([0-9A-Za-z-]+)/, '<inp2:{$prefix}_InputName field='$field'/>');"><label for="<inp2:{$prefix}_InputName field='$field'/>_<inp2:m_param name='key'/>"><inp2:m_param name="option"/></label><inp2:m_if check="m_Param" name="has_br"><br/><inp2:m_else/> </inp2:m_if> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_checkbox_phrase"> <input type="checkbox" <inp2:m_param name='checked'/> id="<inp2:{$prefix}_InputName field='$field'/>_<inp2:m_param name='key'/>" value="<inp2:m_param name='key'/>" tabindex="<inp2:m_Get name='tab_index'/>" onclick="update_checkbox_options(/^<inp2:{$prefix}_InputName field='$field' as_preg='1'/>_([0-9A-Za-z-]+)/, '<inp2:{$prefix}_InputName field='$field'/>');"><label for="<inp2:{$prefix}_InputName field='$field'/>_<inp2:m_param name='key'/>"><inp2:m_phrase label="$option"/></label><inp2:m_if check="m_Param" name="has_br"><br/><inp2:m_else/> </inp2:m_if> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_checkboxes" no_empty="" has_br="0"> <inp2:m_RenderElement design="form_row" block_name="inp_edit_checkboxes" pass_params="1"> <td class="control-cell"> <inp2:m_if check="{$prefix}_FieldOption" field="$field" option="use_phrases"> <inp2:{$prefix}_PredefinedOptions field="$field" no_empty="$no_empty" block="inp_checkbox_phrase" selected="checked" has_br="$has_br"/> <inp2:m_else/> <inp2:{$prefix}_PredefinedOptions field="$field" no_empty="$no_empty" block="inp_checkbox_item" selected="checked" has_br="$has_br"/> </inp2:m_if> <inp2:m_RenderElement prefix="$prefix" name="inp_edit_hidden" field="$field" db="db"/> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_single_checkbox"> <input type="checkbox"<inp2:m_if check="{$prefix}_Selected" field="$field" value="$value"> checked</inp2:m_if> id="<inp2:{$prefix}_InputName field='$field'/>_<inp2:m_param name='value'/>" value="<inp2:m_param name='value'/>" onclick="update_checkbox_options(/^<inp2:{$prefix}_InputName field='$field' as_preg='1'/>_([0-9A-Za-z-]+)/, '<inp2:{$prefix}_InputName field='$field'/>');"><label for="<inp2:{$prefix}_InputName field='$field'/>_<inp2:m_param name='value'/>"><inp2:$prefix_OptionValue field="$field" value="$value"/></label> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_checkbox_allow_html" field_class="" onchange="" onclick="" title="la_enable_html" hint_label="la_Warning_Enable_HTML"> <inp2:m_RenderElement name="inp_edit_checkbox" pass_params="1"/> <!--## <inp2:m_if check="{$prefix}_FieldVisible" field="$field"> <tr class="<inp2:m_odd_even odd='edit-form-odd' even='edit-form-even'/>" id="<inp2:$prefix_InputName field='$field'/>_row"> <td class="control-cell"> <input type="hidden" id="<inp2:{$prefix}_InputName field='$field'/>" name="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' db='db'/>"> <input tabindex="<inp2:m_Get name='tab_index'/>" type="checkbox" id="_cb_<inp2:{$prefix}_InputName field='$field'/>" name="_cb_<inp2:{$prefix}_InputName field='$field'/>" <inp2:{$prefix}_Field field="$field" checked="checked" db="db"/> class="<inp2:m_param name='field_class'/>" onchange="update_checkbox(this, document.getElementById('<inp2:{$prefix}_InputName field='$field'/>'));<inp2:m_param name='onchange'/>" onclick="<inp2:m_param name='onclick'/>"> </td> <inp2:m_RenderElement name="inp_edit_error" pass_params="1"/> </tr> </inp2:m_if> ##--> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_weight" class="" size="" maxlength="" onblur=""> <inp2:m_RenderElement design="form_row" block_name="inp_edit_weight" pass_params="1"> <td class="control-cell"> <inp2:m_if check="lang.current_FieldEquals" field="UnitSystem" value="1"> <input type="text" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" maxlength="<inp2:m_param name='maxlength'/>" class="<inp2:m_param name='class'/>" onblur="<inp2:m_Param name='onblur'/>"> <inp2:m_phrase label="la_kg" /> </inp2:m_if> <inp2:m_if check="lang.current_FieldEquals" field="UnitSystem" value="2"> <input type="text" name="<inp2:{$prefix}_InputName field='{$field}_a'/>" id="<inp2:{$prefix}_InputName field='{$field}_a'/>" value="<inp2:{$prefix}_Field field='{$field}_a'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" maxlength="<inp2:m_param name='maxlength'/>" class="<inp2:m_param name='class'/>" onblur="<inp2:m_Param name='onblur'/>"> <inp2:m_phrase label="la_lbs" /> <input type="text" name="<inp2:{$prefix}_InputName field='{$field}_b'/>" id="<inp2:{$prefix}_InputName field='{$field}_b'/>" value="<inp2:{$prefix}_Field field='{$field}_b'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" maxlength="<inp2:m_param name='maxlength'/>" class="<inp2:m_param name='class'/>" onblur="<inp2:m_Param name='onblur'/>"> <inp2:m_phrase label="la_oz" /> </inp2:m_if> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_minput" style="" format="" allow_add="1" allow_edit="1" allow_delete="1" allow_move="1" title=""> <inp2:m_RenderElement design="form_row" block_name="inp_edit_minput" pass_params="1"> <td class="control-cell"> <table> <tr> <td colspan="2"> <input type="button" class="button" style="width: 70px;" value="<inp2:m_Phrase name='la_btn_Add'/>" id="<inp2:$prefix_InputName field='$field'/>_add_button"/> <input type="button" class="button" style="width: 70px;" value="<inp2:m_Phrase name='la_btn_Cancel'/>" id="<inp2:$prefix_InputName field='$field'/>_cancel_button"/> </td> </tr> <tr> <td valign="top"> <select multiple tabindex="<inp2:m_Get name='tab_index'/>" id="<inp2:$prefix_InputName field='$field'/>_minput" style="<inp2:m_Param name='style'/>"> </select> </td> <td valign="top"> <inp2:m_if check="m_Param" name="allow_edit"> <input type="button" class="button" style="width: 100px;" value="<inp2:m_Phrase name='la_btn_Edit'/>" id="<inp2:$prefix_InputName field='$field'/>_edit_button"/><br /> <img src="img/spacer.gif" height="4" width="1" alt=""/><br /> </inp2:m_if> <inp2:m_if check="m_Param" name="allow_delete"> <input type="button" class="button" style="width: 100px;" value="<inp2:m_Phrase name='la_btn_Delete'/>" id="<inp2:$prefix_InputName field='$field'/>_delete_button"/><br /> </inp2:m_if> <inp2:m_if check="m_Param" name="allow_move"> <br /><br /> <input type="button" class="button" style="width: 100px;" value="<inp2:m_Phrase name='la_btn_MoveUp'/>" id="<inp2:$prefix_InputName field='$field'/>_moveup_button"/><br /> <img src="img/spacer.gif" height="4" width="1" alt=""/><br /> <input type="button" class="button" style="width: 100px;" value="<inp2:m_Phrase name='la_btn_MoveDown'/>" id="<inp2:$prefix_InputName field='$field'/>_movedown_button"/><br /> </inp2:m_if> </td> </tr> <inp2:m_RenderElement name="inp_edit_hidden" prefix="$prefix" field="$field" db="db"/> <script type="text/javascript"> var <inp2:m_Param name="field"/> = new MultiInputControl('<inp2:m_Param name="field"/>', '<inp2:{$prefix}_InputName field="#FIELD_NAME#"/>', fields['<inp2:m_Param name="prefix"/>'], '<inp2:m_Param name="format"/>'); <inp2:m_Param name="field"/>.ValidateURL = '<inp2:m_Link template="dummy" pass="m,$prefix" {$prefix}_event="OnValidateMInputFields" no_amp="1" js_escape="1"/>'; <inp2:m_if check="m_Param" name="allow_add"> <inp2:m_Param name="field"/>.SetPermission('add', true); </inp2:m_if> <inp2:m_if check="m_Param" name="allow_edit"> <inp2:m_Param name="field"/>.SetPermission('edit', true); </inp2:m_if> <inp2:m_if check="m_Param" name="allow_delete"> <inp2:m_Param name="field"/>.SetPermission('delete', true); </inp2:m_if> <inp2:m_if check="m_Param" name="allow_move"> <inp2:m_Param name="field"/>.SetPermission('move', true); </inp2:m_if> <inp2:m_Param name="field"/>.InitEvents(); <inp2:m_Param name="field"/>.SetMessage('required_error', '<inp2:m_Phrase name="la_err_required" escape="1"/>'); <inp2:m_Param name="field"/>.SetMessage('unique_error', '<inp2:m_Phrase name="la_error_unique" escape="1"/>'); <inp2:m_Param name="field"/>.SetMessage('delete_confirm', '<inp2:m_Phrase label="la_Delete_Confirm" escape="1"/>'); <inp2:m_Param name="field"/>.SetMessage('add_button', '<inp2:m_Phrase name="la_btn_Add" escape="1"/>'); <inp2:m_Param name="field"/>.SetMessage('save_button', '<inp2:m_Phrase name="la_btn_Save" escape="1"/>'); </script> </table> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_picker" has_empty="0" empty_value="" style="width: 225px;" size="15"> <inp2:m_RenderElement design="form_row" block_name="inp_edit_picker" pass_params="1"> <td class="control-cell"> <table cellpadding="0" cellspacing="0"> <tr> <td><strong><inp2:m_Phrase label="la_SelectedItems" /></strong></td> <td> </td> <td><strong><inp2:m_Phrase label="la_AvailableItems" /></strong></td> </tr> <tr> <td> <inp2:m_DefineElement name="picker_option_block"> <option value="<inp2:Field name='$key_field' />"><inp2:Field name="$value_field" /></option> </inp2:m_DefineElement> <select multiple id="<inp2:$prefix_InputName name='$field' />_selected" style="<inp2:m_param name='style'/>" size="<inp2:m_param name='size'/>"> <inp2:$optprefix.selected_PrintList render_as="picker_option_block" key_field="$option_key_field" value_field="$option_value_field" per_page="-1" requery="1" link_to_prefix="$prefix" link_to_field="$field"/> </select> </td> <td align="center"> <img src="img/icons/icon_left.gif" id="<inp2:$prefix_InputName name="$field" />_move_left_button"/><br /> <img src="img/icons/icon_right.gif" id="<inp2:$prefix_InputName name="$field" />_move_right_button"/> </td> <td> <select multiple id="<inp2:$prefix_InputName name='$field' />_available" style="<inp2:m_param name='style'/>" size="<inp2:m_param name='size'/>"> <inp2:$optprefix.available_PrintList render_as="picker_option_block" key_field="$option_key_field" value_field="$option_value_field" requery="1" per_page="-1" link_to_prefix="$prefix" link_to_field="$field"/> </select> </td> </tr> </table> <input type="hidden" name="<inp2:$prefix_InputName name='$field' />" id="<inp2:$prefix_InputName name='$field' />" value="<inp2:$prefix_Field field='$field' db='db'/>"> <input type="hidden" name="unselected_<inp2:$prefix_InputName name='$field' />" id="<inp2:$prefix_InputName name='$field' />_available_field" value=""> <script type="text/javascript"> <inp2:m_Param name="field"/> = new EditPickerControl('<inp2:m_Param name="field"/>', '<inp2:$prefix_InputName name="$field" />'); <inp2:m_Param name="field"/>.SetMessage('nothing_selected', '<inp2:m_Phrase label="la_error_SelectItemToMove" escape="1"/>'); </script> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_cron_box"> <inp2:m_RenderElement design="form_row" block_name="inp_edit_cron_box" pass_params="1"> <td class="control-cell"> <input style="width: 150px;" type="text" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field'/>" tabindex="<inp2:m_Get name='tab_index'/>"/> <select tabindex="<inp2:m_Get name='tab_index'/>" name="<inp2:{$prefix}_InputName field='{$field}Hints'/>" id="<inp2:{$prefix}_InputName field='{$field}Hints'/>" style="width: 175px;"> <inp2:{$prefix}_PredefinedOptions field="{$field}Hints" block="inp_option_item" selected="selected" has_empty="1" empty_value="" empty_label="la_opt_CronCommonSettings"/> </select> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_filler" control_options="false"> <tr class="<inp2:m_odd_even odd='edit-form-odd' even='edit-form-even'/>" style="height: auto"> <td class="label-cell-filler" ></td> <td class="control-mid-filler" ></td> <td class="control-cell-filler"> <input type="text" style="display: none;"/> <div id="form_filler" style="width: 100%; height: 5px; background-color: inherit"></div> <script type="text/javascript"> Form.addControl('form_filler', <inp2:m_param name="control_options"/>); </script> </td> </tr> </inp2:m_DefineElement> <inp2:m_DefineElement name="ajax_progress_bar"> <table width="100%" border="0" cellspacing="0" cellpadding="2" class="tableborder"> <tr class="<inp2:m_odd_even odd='table-color1' even='table-color2'/>"> <td colspan="2"> <img src="img/spacer.gif" height="10" width="1" alt="" /><br /> <!-- progress bar paddings: begin --> <table width="90%" cellpadding="2" cellspacing="0" border="0" align="center"> <tr> <td class="progress-text">0%</td> <td width="100%"> <!-- progress bar: begin --> <table cellspacing="0" cellpadding="0" width="100%" border="0" align="center" style="background-color: #FFFFFF; border: 1px solid #E6E6E6;"> <tr> <td colspan="3"><img src="img/spacer.gif" height="2" width="1" alt="" /></td> </tr> <tr> <td width="2"><img src="img/spacer.gif" height="13" width="3" alt="" /></td> <td align="center" width="100%"> <table cellspacing="0" cellpadding="0" width="100%" border="0" style="background: url(img/progress_left.gif) repeat-x;"> <tr> <td id="progress_bar[done]" style="background: url(img/progress_done.gif);" align="left"></td> <td id="progress_bar[left]" align="right"><img src="img/spacer.gif" height="9" width="1" alt="" /></td> </tr> </table> </td> <td width="1"><img src="img/spacer.gif" height="13" width="3" alt="" /></td> </tr> <tr> <td colspan="3"><img src="img/spacer.gif" height="2" width="1" alt="" /></td> </tr> </table> <!-- progress bar: end --> </td> <td class="progress-text">100%</td> </tr> </table> <!-- progress bar paddings: end --> <img src="img/spacer.gif" height="10" width="1" alt="" /><br /> </td> </tr> </table> <table width="100%" border="0" cellspacing="0" cellpadding="2" class="tableborder"> <tr class="<inp2:m_odd_even odd='table-color1' even='table-color2'/>"> <td width="50%" align="right"><inp2:m_phrase name="la_fld_PercentsCompleted"/>:</td> <td id="progress_display[percents_completed]">n/a</td> </tr> </table> <table width="100%" border="0" cellspacing="0" cellpadding="2" class="tableborder"> <tr class="<inp2:m_odd_even odd='table-color1' even='table-color2'/>"> <td width="50%" align="right"><inp2:m_phrase name="la_fld_ElapsedTime"/>:</td> <td id="progress_display[elapsed_time]">n/a</td> </tr> </table> <table width="100%" border="0" cellspacing="0" cellpadding="2" class="tableborder"> <tr class="<inp2:m_odd_even odd='table-color1' even='table-color2'/>"> <td width="50%" align="right"><inp2:m_phrase name="la_fld_EstimatedTime"/>:</td> <td id="progress_display[Estimated_time]">n/a</td> </tr> </table> <table width="100%" border="0" cellspacing="0" cellpadding="2" class="tableborder"> <tr class="<inp2:m_odd_even odd='table-color1' even='table-color2'/>"> <td align="center" colspan="2"> <input type="button" class="button" onclick="<inp2:m_param name='cancel_action'/>" value="<inp2:m_phrase name='la_Cancel'/>" /> </td> </tr> </table> </inp2:m_DefineElement> <inp2:m_DefineElement name="edit_navigation" toolbar="a_toolbar"> <inp2:m_if check="{$prefix}_IsTopmostPrefix"> <inp2:m_if check="{$prefix}_IsSingle"> <inp2:m_param name="toolbar"/>.HideButton('prev'); <inp2:m_param name="toolbar"/>.HideButton('next'); <inp2:m_else/> <inp2:m_if check="{$prefix}_IsLast"> <inp2:m_param name="toolbar"/>.DisableButton('next'); </inp2:m_if> <inp2:m_if check="{$prefix}_IsFirst"> <inp2:m_param name="toolbar"/>.DisableButton('prev'); </inp2:m_if> </inp2:m_if> </inp2:m_if> </inp2:m_DefineElement> <inp2:m_DefineElement name="toolbar_button" icon="" title="" short_title="" toolbar="a_toolbar"> <inp2:m_Param name="toolbar"/>.AddButton( new ToolBarButton( '<inp2:m_Param name="icon"/>', <inp2:m_if check="m_Param" name="short_title"> '<inp2:m_Phrase label="$title" escape="1"/>::<inp2:m_phrase label="$short_title" escape="1"/>', <inp2:m_else/> '<inp2:m_Phrase label="$title" escape="1"/>', </inp2:m_if> function() { <inp2:m_Param name="content"/> } ) ); </inp2:m_DefineElement> <inp2:m_DefineElement name="tabs_container" tabs_render_as=""> <table cellpadding="0" cellspacing="0" style="width: 100%;"> <tr> <td style="width: 20px;"> <img src="<inp2:m_TemplatesBase/>/img/spacer.gif" width="20" height="0" alt=""/><br/> <a href="#" class="scroll-left disabled"></a> </td> <td height="23" align="right"> <div id="tab-measure" style="display: none; width: 100%; height: 23px;"> </div> <div style="overflow: hidden; height: 23px;" class="tab-viewport"> <table class="tabs" cellpadding="0" cellspacing="0" height="23"> <tr> <inp2:m_RenderElement name="$tabs_render_as" pass_params="1"/> </tr> </table> </div> </td> <td class="scroll-right-container disabled"> <img src="<inp2:m_TemplatesBase/>/img/spacer.gif" width="20" height="0" alt=""/><br/> <a href="#" class="scroll-right disabled"></a> </td> </tr> </table> </inp2:m_DefineElement> <inp2:m_DefineElement name="edit_tabs_element"> <inp2:m_DefineElement name="edit_tab"> <inp2:m_RenderElement name="tab" title="$title" t="$template" main_prefix="$PrefixSpecial"/> </inp2:m_DefineElement> <inp2:{$prefix}_PrintEditTabs render_as="edit_tab" preset_name="$preset_name"/> </inp2:m_DefineElement> <inp2:m_DefineElement name="edit_tabs" preset_name="Default"> <inp2:m_if check="{$prefix}_HasEditTabs" preset_name="$preset_name"> <inp2:m_RenderElement name="tabs_container" tabs_render_as="edit_tabs_element" pass_params="1"/> </inp2:m_if> </inp2:m_DefineElement> <inp2:m_DefineElement name="ml_selector" prefix=""> <inp2:m_if check="lang_IsMultiLanguage"> <td align="right" style="padding-right: 5px;"> <table width="100%" cellpadding="0" cellspacing="0"> <tr> <td align="right"> <inp2:m_phrase name="la_fld_Language"/>: <select name="language" onchange="submit_event('<inp2:m_param name='prefix'/>', 'OnPreSaveAndChangeLanguage');"> <inp2:m_DefineElement name="lang_elem"> <option value="<inp2:Field name='LanguageId'/>" <inp2:m_if check="SelectedLanguage" type="data">selected="selected"</inp2:m_if> ><inp2:Field name="LocalName" no_special='no_special' /></option> </inp2:m_DefineElement> <inp2:lang_PrintList render_as="lang_elem"/> </select> </td> </tr> <tr> <td align="right" style="vertical-align: bottom; padding: 2px 0px 5px 2px;"> <span style="color: red">*</span> <span class="req-note"><inp2:m_Phrase name="la_text_RequiredFields"/></span> </td> </tr> </table> </td> <inp2:m_else/> <td align="right" style="vertical-align: bottom; padding: 2px 5px 5px 2px;"> <span style="color: red">*</span> <span class="req-note"><inp2:m_Phrase name="la_text_RequiredFields"/></span> </td> </inp2:m_if> </inp2:m_DefineElement> <inp2:m_DefineElement name="form_error_warning"> <inp2:m_RenderElement design="form_message" pass_params="1"> <inp2:m_Phrase name="la_Warning_NewFormError"/><br/> <span id="error_msg_<inp2:m_Param name='prefix'/>" style="font-weight: bold;"><br/></span> </inp2:m_RenderElement> </inp2:m_DefineElement> <inp2:m_DefineElement name="inp_edit_export_presets"> <inp2:m_RenderElement design="form_row" prefix="$prefix" field="ExportPresets" title="la_fld_ExportPresets"> <td class="control-cell"> <select tabindex="<inp2:m_get param="tab_index"/>" name="<inp2:$prefix_InputName field="ExportPresets"/>" id="<inp2:$prefix_InputName field="ExportPresets"/>" onchange="update_fields(this.value)"> <inp2:$prefix_PredefinedOptions field="ExportPresets" block="inp_option_item" selected="selected"/> </select> <input class="button" type="button" value="<inp2:m_Phrase label="la_Text_Delete"/>" onclick="delete_preset()"> <script type="text/javascript" src="incs/ajax.js"></script> <script type="text/javascript"> var delete_busy = false; function delete_preset() { if (!confirm('<inp2:m_Phrase label="la_ConfirmDeleteExportPreset" js_escape="1"/>')) return; var $kf = document.getElementById('kernel_form'); tmp = $kf.elements['events[<inp2:m_param name="prefix"/>]'].value; $kf.elements['events[<inp2:m_param name="prefix"/>]'].value = 'OnDeleteExportPreset'; Request.method = 'POST'; Request.params = Request.serializeForm($kf); $kf.elements['events[<inp2:m_param name="prefix"/>]'].value = tmp; Request.makeRequest('<inp2:m_t ajax="1" {$prefix}_event="OnDeleteExportPreset" no_amp="1" js_escape="1"/>', delete_busy, '', function() { presets = document.getElementById('<inp2:$prefix_InputName field="ExportPresets"/>'); for (i=0; i< presets.options.length; i++) { if (presets.options[i].selected) { presets.options.remove[i]; } } presets.options[0].selected = true; }, function() {alert('error')} ); } function update_preset_field(status) { if (status) { presets = document.getElementById('<inp2:$prefix_InputName field="ExportPresets"/>'); if ( presets.value != '' ) { for (i=0; i< presets.options.length; i++) { if (presets.options[i].selected) { preset_name = presets.options[i].text } } document.getElementById('<inp2:$prefix_InputName field="ExportPresetName"/>').value = preset_name; } } document.getElementById('<inp2:$prefix_InputName field="ExportPresetName"/>').disabled = !status; } function update_fields(preset) { $export = select_to_string(document.getElementById('<inp2:$prefix_InputName field="ExportColumns"/>')); if ($export != '') { string_to_selected($export, document.getElementById('<inp2:$prefix_InputName field="ExportColumns"/>')); move_selected('<inp2:$prefix_InputName field="ExportColumns"/>', '<inp2:$prefix_InputName field="AvailableColumns"/>'); } av = document.getElementById('<inp2:$prefix_InputName field="AvailableColumns"/>'); fields = preset.split('|'); for (var i=0; i<fields.length; i++) { fld = fields[i]; var $cur = null; for (var $i = 0; $i < av.length; $i++) { cur = av.options[$i]; if (cur.value == fld) { av.options[$i].selected = true; move_selected('<inp2:$prefix_InputName field="AvailableColumns"/>', '<inp2:$prefix_InputName field="ExportColumns"/>'); } else { av.options[$i].selected = false; } } } } $(document).ready( function () { update_preset_field(document.getElementById('<inp2:$prefix_InputName field="ExportPresetName"/>').checked); } ); </script> </td> </inp2:m_RenderElement> <inp2:m_RenderElement design="form_row" prefix="$prefix" field="ExportColumns" title="la_fld_ExportColumns"> <td class="control-cell"> <table cellpadding="0" cellspacing="0" border="0"> <tr> <td> <input type="button" value="<inp2:m_phrase name='la_btn_Up'/>" onclick="move_options_up('<inp2:$prefix_InputName field='ExportColumns'/>', 1)" class="button"> <input type="button" value="<inp2:m_phrase name='la_btn_Down'/>" onclick="move_options_down('<inp2:$prefix_InputName field='ExportColumns'/>', 1)" class="button"><br /> <img src="img/spacer.gif" width="1" height="5" alt=""><br /> </td> <td><img src="img/spacer.gif" width="45" height="1" alt=""><br></td> <td><inp2:m_phrase name="la_fld_AvailableColumns"/>:</td> </tr> <tr> <td> <select multiple id="<inp2:$prefix_InputName field='ExportColumns'/>" size="15" style="width: 225px;"> <inp2:$prefix_PredefinedOptions field="ExportColumns" block="inp_option_item" selected="selected"/> </select> <input type="hidden" id="hidden_export_columns" name="<inp2:$prefix_InputName field='ExportColumns'/>" value="<inp2:$prefix_Field field='ExportColumns'/>" /> </td> <td align="center"> <input type="button" value=" «« " onclick="move_selected('<inp2:$prefix_InputName field='AvailableColumns'/>', '<inp2:$prefix_InputName field='ExportColumns'/>')" class="button"><br> <img src="img/spacer.gif" width="1" height="4" alt=""><br> <input type="button" value=" »» " onclick="move_selected('<inp2:$prefix_InputName field='ExportColumns'/>', '<inp2:$prefix_InputName field='AvailableColumns'/>'); select_sort('<inp2:$prefix_InputName field='AvailableColumns'/>');" class="button"> </td> <td> <select multiple id="<inp2:$prefix_InputName field='AvailableColumns'/>" size="15" style="width: 225px;"> <inp2:$prefix_PredefinedOptions field="AvailableColumns" block="inp_option_item" selected="selected"/> </select> </td> </tr> </table> </td> </inp2:m_RenderElement> </inp2:m_DefineElement> Index: branches/5.3.x/core/admin_templates/incs/cms.css =================================================================== --- branches/5.3.x/core/admin_templates/incs/cms.css (revision 16394) +++ branches/5.3.x/core/admin_templates/incs/cms.css (revision 16395) @@ -1,424 +1,424 @@ /* === Common styles for "Section Properties" and "Edit" buttons === */ div.cms-btn-image { float: left; height: 15px; vertical-align: middle; } div.cms-btn-text { margin-left: 3px; float: left; white-space: nowrap; vertical-align: middle; } div.cms-btn-content { padding: 5px; min-height: 15px; } .cms-edit-btn { padding: 2px 5px; font-family: Arial, Verdana; font-size: 13px; font-weight: normal; color: black; cursor: pointer; border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; } .cms-section-properties-btn, .cms-edit-btn, .cms-edit-block-btn, .cms-edit-design-btn { width: auto; position: absolute; - z-index: 1000099; + z-index: 1000100; } .cms-edit-btn, .cms-edit-block-btn, .cms-edit-design-btn { margin-top: -10px; } div.cms-edit-design-btn-container { position: absolute; margin-left: -10px; margin-top: -10px; display: none; } div.cms-edit-design-btn-container .cms-edit-block-btn, div.cms-edit-design-btn-container .cms-edit-design-btn { float: left; display: block; position: relative; margin: 0px; } div.cms-edit-design-btn-container .cms-edit-design-btn { margin-left: 4px; } /* === Styles for "Section Properties" button === */ .cms-section-properties-btn { float: right; position: relative; border-color: #A1D0A1; background-color: #CCFF00; } div.cms-section-properties-btn-container { border: 1px dashed #A1D0A1; margin: 10px; } /* === Styles for "Edit" button === */ .cms-btn-new { cursor: pointer; background: url(@templates_base@/img/top_frame/icons/content_mode.png) #CCFF00 3px center no-repeat; padding: 2px 2px 2px 17px; border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; border: 2px solid #A1D0A1; opacity: 0.5; filter: alpha(opacity=50); color: black; font-family: Arial, Verdana; font-size: 13px; font-weight: normal; } .cms-btn-new:hover { opacity: 1; filter: alpha(opacity=100); } div.cms-edit-btn-container { position: relative; border: 1px dashed #FF6E00; } .cms-edit-btn-container[data-content-id] .cms-btn-content { padding: 0; min-height: 25px; } .cms-edit-btn-container .inline-edit-layer, .cms-edit-btn-container .inline-saving-layer { display: none; } .cms-edit-btn-container.mode-inline-edit .inline-edit-layer { display: block; } .cms-edit-btn-container .inline-edit-layer { position: absolute; background: url(@templates_base@/img/top_frame/icons/content_mode.png) center no-repeat; width: 20px; height: 20px; top: 0px; right: 0px; } .cms-edit-btn-container.mode-inline-saving .inline-saving-layer { display: block; } .cms-edit-btn-container .inline-saving-layer { position: absolute; background: url(@templates_base@/img/top_frame/icons/saving_indicator.gif) no-repeat 100% 0px rgba(255, 255, 255, .9); width: 100%; top: 0px; bottom: 0px; } div.cms-edit-btn { border: 2px solid #FF6E00; } .cms-edit-btn { margin-left: -10px; border-color: #FF6E00; background-color: #FFCC00; } .cms-edit-block-btn { border-color: #FF6E00; background-color: #FFCC00; display: none; } .cms-edit-design-btn { border-color: #A1D0A1; background-color: #CCFF00; display: none; } .admin-edit-btn { border-color: #A1D0A1; background-color: #CCFF00; } /* === Styles for "Save" and "Cancel" buttons (for layout) === */ div.cms-layout-btn-container { position: absolute; display: none; - z-index: 1000099; + z-index: 1000100; } .cms-save-layout-btn, .cms-cancel-layout-btn { float: left; width: auto; } .cms-save-layout-btn { color: white; border-color: #1C960C; background-color: #24B30D; } .cms-cancel-layout-btn { color: white; border-color: #C81900; background-color: #DE2A00; } .cms-cancel-layout-btn { margin-left: 4px; } /* === Styles for Template Editor === */ div.block-edit-block-btn-container, div.block-edit-design-btn-container { border: 1px dashed transparent; } div.block-edit-block-btn-container .cms-btn-new, div.block-edit-design-btn-container .cms-btn-new { opacity: 1; filter: alpha(opacity = 100); } div.block-edit-block-btn-container-over, div.block-edit-design-btn-container-over { border-color: #FF6E00; } /*div.block-edit-design-btn-container-over { border-color: #A1D0A1; }*/ /* === Styles for element moving/sorting in theme === */ div.movable-area { min-height: 200px; } .move-helper { border: 3px dashed #666; /*width: auto !important;*/ } /* === Styles for phrase translation links === */ span[name='cms-translate-phrase'] { text-decoration: none; border: 1px dashed transparent; padding: 3px; } span[name='cms-translate-phrase']:hover { border-color: #FF6E00; } span[name='cms-translate-phrase'] .cms-btn-text { font-size: 9px; } span[name='cms-translate-phrase'] .cms-edit-btn, span[name='cms-translate-phrase'] .admin-edit-btn { display: none; opacity: 1; filter: alpha(opacity=100); } /* === Misc Styles for "Content Revision Control" button === */ .cms-clear { clear: both; } .cms-right { float: right; } .cms-left { float: left; } /* === Current Revision Information === */ #cms-current-revision-info { color: #4b4b4b; font-size: 11px; font-family: Arial, Helvetica, sans-serif; font-weight: bold; white-space: nowrap; float: left; padding: 0px 30px; } #cms-current-revision-info span { display: block; /*color: #008c1b;*/ font-size: 16px; display: block; padding: 10px 0px 3px 0px; } #cms-current-revision-info span.cms-revision-published { color: #15b300; } #cms-current-revision-info span.cms-revision-pending { color: #ff9600; } #cms-current-revision-info span.cms-revision-declined { color: #f90000; } /* === Revision Editing Toolbar === */ #cms-revision-toolbar { background-color: #F0F1EB; border-collapse: collapse; border-color: #aaaaaa; border-style: solid; border-width: 0 1px 1px; font-size: 8pt; } #cms-revision-toolbar-layer { position: fixed; width: 630px; top: 0px; - z-index: 1000100; + z-index: 1000110; } .toolbar-button, .toolbar-button-disabled, .toolbar-button-over { float: left; text-align: center; font-size: 8pt; padding: 5px; vertical-align: middle; color: #006F99; } .toolbar-button-over { color: #000; } .toolbar-button-disabled { color: #444; } a#cms-toggle-revision-toolbar { display: block; background: url(@templates_base@/img/top_frame/revision_control/button_vp_right.png) top right no-repeat; padding-right: 24px; float: right; margin-top: -1px; text-decoration: none; } a#cms-toggle-revision-toolbar span { display: block; background: url(@templates_base@/img/top_frame/revision_control/button_vp_left.png) top left no-repeat; line-height: 30px; color: #000000; font-family: Arial, Helvetica, sans-serif; font-size: 13px; font-weight: bold; padding: 0px 10px 0px 15px; } a#cms-toggle-revision-toolbar:hover span { color: #666666; } a#cms-toggle-revision-toolbar.opened { background: url(@templates_base@/img/top_frame/revision_control/button_vp_right2.png) top right no-repeat; } #cms-close-toolbar { float: right; display: block; width: 10px; height: 10px; background: url(@templates_base@/img/top_frame/revision_control/close_black.gif) top left; overflow: hidden; margin: 10px 10px 0 0; } /* === Revision Editing Notice === */ #cms-editing-notice { width: 230px; position: fixed; - z-index: 1000101; + z-index: 1000120; margin-top: 50px; margin-left: 10px; display: none; } #cms-editing-notice .top { background: url(@templates_base@/img/top_frame/revision_control/message_background_red.png) left top no-repeat; padding: 30px 20px 10px 20px; color: #FFFFFF; font-weight: bold; font-family: Arial, Helvetica, sans-serif; } #cms-editing-notice .bottom { background: url(@templates_base@/img/top_frame/revision_control/message_background_red.png) left bottom no-repeat; height: 6px; } #cms-close-editing-notice { float:right; display:block; width:10px; height:10px; background:url(@templates_base@/img/top_frame/revision_control/close_white.gif) top left; overflow:hidden; margin:-5px -10px 0 0; } /* === Revision Dropdown === */ #cms-revision-dropdown { width: 240px; position: fixed; margin-left: 260px; margin-top: 30px; - z-index: 1000102; + z-index: 1000130; display: none; } #cms-revision-dropdown .top { border-top: 1px solid #aaaaaa; border-left: 1px solid #aaaaaa; border-right: 1px solid #aaaaaa; } #cms-revision-dropdown .item, #cms-revision-dropdown .item:hover { color: #666666; background: url(@templates_base@/img/top_frame/revision_control/history_item_background.gif) top left repeat-x #FFFFFF; padding: 10px 13px; border-top: 1px solid #e7e7e7; font-size: 11px; cursor: pointer; } #cms-revision-dropdown .item:hover { background: url(@templates_base@/img/top_frame/revision_control/history_item_background_hover.gif) top left repeat-x #F8F8F8; } #cms-revision-dropdown .item .red { color: #FF0000; padding-top: 6px; } #cms-revision-dropdown .bottom { background: url(@templates_base@/img/top_frame/revision_control/history_bottom.png) top left no-repeat; height: 6px; } .item .cms-revision-published, .item .cms-revision-pending, .item .cms-revision-declined { display: block; font-size: 13px; font-weight: bold; padding-bottom: 8px; } .item .cms-revision-published, .item .cms-revision-published a { color: #15b300 !important; } .item .cms-revision-published a:hover { color: #1ada00 !important; } .item .cms-revision-pending, .item .cms-revision-pending a { color: #ff9600 !important; } .item .cms-revision-pending a:hover { color: #ffba58 !important; } .item .cms-revision-declined, .item .cms-revision-declined a { color: #f90000 !important; } -.item .cms-revision-declined a:hover { color: #ff6666 !important; } \ No newline at end of file +.item .cms-revision-declined a:hover { color: #ff6666 !important; } Index: branches/5.3.x/core/admin_templates/index.tpl =================================================================== --- branches/5.3.x/core/admin_templates/index.tpl (revision 16394) +++ branches/5.3.x/core/admin_templates/index.tpl (revision 16395) @@ -1,66 +1,66 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <inp2:m_Set skip_last_template="1"/> <inp2:m_CheckSSL mode="required" condition="Require_AdminSSL" /> <inp2:m_CheckSSL/> <inp2:m_RequireLogin login_template="login"/> <inp2:m_NoDebug/> <html> <head> <meta http-equiv="content-type" content="text/html; charset=<inp2:m_GetConst name='CHARSET'/>"> <title><inp2:m_GetConfig var="Site_Name"/> - <inp2:m_Phrase label="la_AdministrativeConsole"/></title> <inp2:m_base_ref/> <link rel="icon" href="<inp2:m_BaseURL/>favicon.ico" type="image/x-icon" /> <link rel="shortcut icon" href="<inp2:m_BaseURL/>favicon.ico" type="image/x-icon" /> <script type="text/javascript"> window.name = 'main_frame'; var $top_height = 117; // 94; if (navigator.appName == 'Netscape') { $top_height = navigator.appVersion.substring(0, 1) != '5' ? $top_height + 2 : $top_height + 1; } <inp2:m_if check="m_GetConfig" name="ResizableFrames"> <inp2:m_if check="m_RecallEquals" name="ShowAdminMenu" value="0" persistent="1"> document.write('<frameset id="top_frameset" rows="' + $top_height + ',*" framespacing="0" scrolling="no" border="1">'); <inp2:m_else/> document.write('<frameset id="top_frameset" rows="25,*" framespacing="0" scrolling="no" border="1">'); </inp2:m_if> <inp2:m_else/> <inp2:m_if check="m_RecallEquals" name="ShowAdminMenu" value="0" persistent="1"> document.write('<frameset id="top_frameset" rows="' + $top_height + ',*" framespacing="0" scrolling="no" frameborder="0">'); <inp2:m_else/> document.write('<frameset id="top_frameset" rows="25,*" framespacing="0" scrolling="no" frameborder="0">'); </inp2:m_if> </inp2:m_if> </script> </head> <inp2:m_if check="m_GetConfig" name="ResizableFrames"> <frame src="<inp2:m_t t='head' pass='m' m_cat_id='0' m_opener='s' no_pass_through='1'/>" name="head" id="head_frame" scrolling="no" noresize frameborder="0" border="0"> <frameset id="sub_frameset" cols="<inp2:m_if check='m_RecallEquals' name='ShowAdminMenu' value='0' persistent='1'><inp2:adm_MenuFrameWidth/><inp2:m_else/>0</inp2:m_if>,*" border="1"> - <frame src="<inp2:m_t t='tree' pass='m' m_cat_id='0' m_opener='s' no_pass_through='1'/>" name="menu" target="main" scrolling="auto" marginwidth="0" marginheight="0" border="1" frameborder="1"> + <frame src="<inp2:m_t t='tree' pass='m' m_cat_id='0' m_opener='s' no_pass_through='1'/>" id="menu_frame" name="menu" target="main" scrolling="auto" marginwidth="0" marginheight="0" border="1" frameborder="1"> <inp2:m_DefineElement name="root_node"> <frame src="<inp2:m_if check='adm_MainFrameLink'><inp2:adm_MainFrameLink html_escape='1'/><inp2:m_else/><inp2:m_param name='section_url' html_escape='1'/></inp2:m_if>" name="main" marginwidth="0" marginheight="0" scrolling="auto" border="1" frameborder="1"> </inp2:m_DefineElement> <inp2:adm_PrintSection render_as="root_node" section_name="in-portal:root" use_first_child="1"/> </frameset> </frameset> <inp2:m_else/> <frame src="<inp2:m_t t='head' pass='m' m_cat_id='0' m_opener='s' no_pass_through='1'/>" name="head" scrolling="no" noresize="noresize"> <frameset id="sub_frameset" cols="<inp2:m_if check='m_RecallEquals' name='ShowAdminMenu' value='0' persistent='1'><inp2:adm_MenuFrameWidth/><inp2:m_else/>0</inp2:m_if>,*" border="0"> - <frame src="<inp2:m_t t='tree' pass='m' m_cat_id='0' m_opener='s' no_pass_through='1'/>" name="menu" target="main" noresize scrolling="auto" marginwidth="0" marginheight="0"> + <frame src="<inp2:m_t t='tree' pass='m' m_cat_id='0' m_opener='s' no_pass_through='1'/>" id="menu_frame" name="menu" target="main" noresize scrolling="auto" marginwidth="0" marginheight="0"> <inp2:m_DefineElement name="root_node"> <frame src="<inp2:m_if check='adm_MainFrameLink'><inp2:adm_MainFrameLink html_escape='1'/><inp2:m_else/><inp2:m_param name='section_url' html_escape='1'/></inp2:m_if>" name="main" marginwidth="0" marginheight="0" frameborder="no" noresize scrolling="auto"> </inp2:m_DefineElement> <inp2:adm_PrintSection render_as="root_node" section_name="in-portal:root" use_first_child="1"/> </frameset> </frameset> </inp2:m_if> <noframes> <body bgcolor="#FFFFFF"> <p></p> </body> </noframes> -</html> \ No newline at end of file +</html> Index: branches/5.3.x/core/admin_templates/user_selector.tpl =================================================================== --- branches/5.3.x/core/admin_templates/user_selector.tpl (revision 16394) +++ branches/5.3.x/core/admin_templates/user_selector.tpl (revision 16395) @@ -1,61 +1,61 @@ <inp2:adm_SetPopupSize width="582" height="504"/> <inp2:m_include t="incs/header"/> <inp2:m_RenderElement name="combined_header" prefix="u" section="in-portal:user_list" grid="UserSelector" title_preset="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() { var $opener = getWindowOpener(window); if ( $.isFunction($opener.processUserSelector) ) { $opener.processUserSelector('<inp2:m_Recall name="dst_field"/>', window); window_close(); } else { submit_event('<inp2:m_Recall name="main_prefix"/>', 'OnSelectUser'); } } var $user_logins = {}; </script> </td> <inp2:m_RenderElement name="search_main_toolbar" prefix="u" grid="UserSelector"/> </tr> </tbody> </table> <inp2:m_DefineElement name="grid_login_td" format="" nl2br="" no_special="1" 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:UserSelectorTitle js_escape="1"/>'; + $user_logins[ <inp2:Field name="$IdField"/> ] = '<inp2:UserTitle js_escape="1"/>'; </script> </inp2:m_DefineElement> <inp2:m_RenderElement name="grid" PrefixSpecial="u" IdField="PortalUserId" grid="UserSelector"/> <script type="text/javascript"> Grids['u'].SetDependantToolbarButtons( new Array('select') ); </script> <inp2:m_include t="incs/footer"/> \ No newline at end of file Index: branches/5.3.x/core/install/install_data.sql =================================================================== --- branches/5.3.x/core/install/install_data.sql (revision 16394) +++ branches/5.3.x/core/install/install_data.sql (revision 16395) @@ -1,1044 +1,1044 @@ # Section "in-portal:configure_categories": INSERT INTO SystemSettings VALUES(DEFAULT, 'Category_Sortfield', 'Name', 'In-Portal', 'in-portal:configure_categories', 'la_title_General', 'la_category_sortfield_prompt', 'select', '', '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>CustomFields WHERE (Type = 1) AND (IsSystem = 0)</SQL>', 10.01, 1, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'Category_Sortorder', 'asc', 'In-Portal', 'in-portal:configure_categories', 'la_title_General', 'la_category_sortfield_prompt', 'select', '', 'asc=la_common_Ascending||desc=la_common_Descending', 10.01, 2, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'Category_Sortfield2', 'Description', 'In-Portal', 'in-portal:configure_categories', 'la_title_General', 'la_category_sortfield2_prompt', 'select', '', '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>CustomFields WHERE (Type = 1) AND (IsSystem = 0)</SQL>', 10.02, 1, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'Category_Sortorder2', 'asc', 'In-Portal', 'in-portal:configure_categories', 'la_title_General', 'la_category_sortfield2_prompt', 'select', '', 'asc=la_common_Ascending||desc=la_common_Descending', 10.02, 2, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'Perpage_Category', '20', 'In-Portal', 'in-portal:configure_categories', 'la_title_General', 'la_category_perpage_prompt', 'text', '', '', 10.03, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'Perpage_Category_Short', '3', 'In-Portal', 'in-portal:configure_categories', 'la_title_General', 'la_category_perpage__short_prompt', 'text', '', '', 10.04, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'Category_DaysNew', '8', 'In-Portal', 'in-portal:configure_categories', 'la_title_General', 'la_category_daysnew_prompt', 'text', '', '', 10.05, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'Category_ShowPick', '', 'In-Portal', 'in-portal:configure_categories', 'la_title_General', 'la_category_showpick_prompt', 'checkbox', '', '', 10.06, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'MaxImportCategoryLevels', '10', 'In-Portal', 'in-portal:configure_categories', 'la_title_General', 'la_prompt_max_import_category_levels', 'text', '', '', 10.07, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'AllowDeleteRootCats', '1', 'In-Portal', 'in-portal:configure_categories', 'la_title_General', 'la_AllowDeleteRootCats', 'checkbox', NULL, NULL, 10.08, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'Catalog_PreselectModuleTab', '1', 'In-Portal', 'in-portal:configure_categories', 'la_title_General', 'la_config_CatalogPreselectModuleTab', 'checkbox', NULL, NULL, 10.09, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'RecycleBinFolder', '', 'In-Portal', 'in-portal:configure_categories', 'la_title_General', 'la_config_RecycleBinFolder', 'text', NULL, NULL, 10.10, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'CheckViewPermissionsInCatalog', '0', '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'); 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.12, 0, 0, 'hint:la_config_CategoryPermissionRebuildMode'); INSERT INTO SystemSettings VALUES(DEFAULT, 'FilenameSpecialCharReplacement', '-', 'In-Portal', 'in-portal:configure_categories', 'la_title_General', 'la_config_FilenameSpecialCharReplacement', 'select', NULL, '_=+_||-=+-', 10.13, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'Search_MinKeyword_Length', '3', 'In-Portal', 'in-portal:configure_categories', 'la_title_General', 'la_config_Search_MinKeyword_Length', 'text', NULL, NULL, 10.14, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'ExcludeTemplateSectionsFromSearch', '0', 'In-Portal', 'in-portal:configure_categories', 'la_title_General', 'la_config_ExcludeTemplateSectionsFromSearch', 'checkbox', '', '', 10.15, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'UpdateCountersOnFilterChange', '1', 'In-Portal', 'in-portal:configure_categories', 'la_title_General', 'la_config_UpdateCountersOnFilterChange', 'checkbox', '', '', 10.16, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'Category_MetaKey', '', 'In-Portal', 'in-portal:configure_categories', 'la_Text_MetaInfo', 'la_category_metakey', 'textarea', '', '', 20.01, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'Category_MetaDesc', '', 'In-Portal', 'in-portal:configure_categories', 'la_Text_MetaInfo', 'la_category_metadesc', 'textarea', '', '', 20.02, 0, 1, NULL); # Section "in-portal:configure_general": INSERT INTO SystemSettings VALUES(DEFAULT, 'Site_Name', 'In-Portal CMS', 'In-Portal', 'in-portal:configure_general', 'la_section_SettingsWebsite', 'la_config_website_name', 'text', '', '', 10.01, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'FirstDayOfWeek', '1', 'In-Portal', 'in-portal:configure_general', 'la_Text_Date_Time_Settings', 'la_config_first_day_of_week', 'select', '', '0=la_sunday||1=la_monday', 20.01, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'Config_Site_Time', '', 'In-Portal', 'in-portal:configure_general', 'la_Text_Date_Time_Settings', 'la_config_site_zone', 'select', '', NULL, 20.02, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'DefaultEmailSender', 'portal@user.domain.name', 'In-Portal', 'in-portal:configure_general', 'la_section_SettingsMailling', 'la_prompt_AdminMailFrom', 'text', NULL, 'size="40"', 30.01, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'SessionTimeout', '3600', 'In-Portal', 'in-portal:configure_general', 'la_section_SettingsSession', 'la_prompt_session_timeout', 'text', 'a:3:{s:4:"type";s:3:"int";s:13:"min_value_inc";i:1;s:8:"required";i:1;}', '', 40.01, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'AdminConsoleInterface', 'simple', 'In-Portal', 'in-portal:configure_general', 'la_section_SettingsAdmin', 'la_config_AdminConsoleInterface', 'select', '', 'simple=+simple||advanced=+advanced||custom=+custom', 50.01, 1, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'AllowAdminConsoleInterfaceChange', '1', 'In-Portal', 'in-portal:configure_general', 'la_section_SettingsAdmin', 'la_config_AdminConsoleInterface', 'checkbox', NULL, NULL, 50.01, 2, 0, NULL); # Section "in-portal:configure_advanced": INSERT INTO SystemSettings VALUES(DEFAULT, 'PageHitCounter', '0', 'In-Portal', 'in-portal:configure_advanced', '', '', '', NULL, NULL, 0, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'UseModRewrite', '1', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsWebsite', 'la_config_use_modrewrite', 'checkbox', '', '', 10.01, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'ModRewriteUrlEnding', '.html', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsWebsite', 'la_config_ModRewriteUrlEnding', 'select', '', '=+||/=+/||.html=+.html', 10.011, 0, 0, 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); INSERT INTO SystemSettings VALUES(DEFAULT, 'ForceModRewriteUrlEnding', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsWebsite', 'la_config_ForceModRewriteUrlEnding', 'checkbox', '', NULL, 10.012, 0, 0, 'hint:la_config_ForceModRewriteUrlEnding'); INSERT INTO SystemSettings VALUES(DEFAULT, 'UseContentLanguageNegotiation', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsWebsite', 'la_config_UseContentLanguageNegotiation', 'checkbox', '', '', 10.013, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'cms_DefaultDesign', '#default_design#', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsWebsite', 'la_config_DefaultDesignTemplate', 'text', NULL, NULL, 10.02, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'ErrorTemplate', 'error_notfound', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsWebsite', 'la_config_error_template', 'text', '', '', 10.03, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'NoPermissionTemplate', 'no_permission', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsWebsite', 'la_config_nopermission_template', 'text', '', '', 10.04, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'UsePageHitCounter', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsWebsite', 'la_config_UsePageHitCounter', 'checkbox', '', '', 10.05, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'ForceImageMagickResize', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsWebsite', 'la_config_ForceImageMagickResize', 'checkbox', '', '', 10.06, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'CheckStopWords', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsWebsite', 'la_config_CheckStopWords', 'checkbox', '', '', 10.07, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'UseVisitorTracking', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsWebsite', 'la_config_UseVisitorTracking', 'checkbox', '', '', 10.08, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'cms_DefaultTrackingCode', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsWebsite', 'la_config_DefaultTrackingCode', 'textarea', NULL, 'COLS=40 ROWS=5', 10.09, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'PerformExactSearch', '1', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsWebsite', 'la_config_PerformExactSearch', 'checkbox', '', '', '10.10', 0, 0, 'hint:la_config_PerformExactSearch'); INSERT INTO SystemSettings 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 SystemSettings 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 SystemSettings 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 SystemSettings 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'); INSERT INTO SystemSettings VALUES(DEFAULT, 'CookieSessions', '2', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSession', 'la_prompt_session_management', 'select', NULL, '0=la_opt_QueryString||1=la_opt_Cookies||2=la_opt_AutoDetect', 20.01, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'SessionCookieName', 'sid', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSession', 'la_prompt_session_cookie_name', 'text', '', '', 20.02, 0, 1, NULL); INSERT INTO SystemSettings 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); INSERT INTO SystemSettings VALUES(DEFAULT, 'KeepSessionOnBrowserClose', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSession', 'la_config_KeepSessionOnBrowserClose', 'checkbox', '', '', 20.03, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'SessionBrowserSignatureCheck', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSession', 'la_config_SessionBrowserSignatureCheck', 'checkbox', NULL, NULL, 20.04, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'SessionIPAddressCheck', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSession', 'la_config_SessionIPAddressCheck', 'checkbox', NULL, NULL, 20.05, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'UseJSRedirect', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSession', 'la_config_use_js_redirect', 'checkbox', '', '', 20.06, 0, 0, NULL); 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); INSERT INTO SystemSettings VALUES(DEFAULT, 'Require_SSL', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSSL', 'la_config_require_ssl', 'checkbox', '', '', 30.03, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'Require_AdminSSL', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSSL', 'la_config_RequireSSLAdmin', 'checkbox', '', '', 30.04, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'Force_HTTP_When_SSL_Not_Required', '1', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSSL', 'la_config_force_http', 'checkbox', '', '', 30.05, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'UseModRewriteWithSSL', '1', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSSL', 'la_config_use_modrewrite_with_ssl', 'checkbox', '', '', 30.06, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'RootPass', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_prompt_root_pass', 'password', NULL, NULL, 40.01, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'UseToolbarLabels', '1', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_UseToolbarLabels', 'checkbox', NULL, NULL, 40.02, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'UseSmallHeader', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_UseSmallHeader', 'checkbox', '', '', 40.03, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'UseColumnFreezer', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_UseColumnFreezer', 'checkbox', '', '', 40.04, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'UsePopups', '2', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_UsePopups', 'select', '', '0=la_opt_SameWindow||1=la_opt_PopupWindow||2=la_opt_ModalWindow', 40.05, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'StickyGridSelection', '1', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_StickyGridSelection', 'radio', '', '1=la_Yes||0=la_No', 40.06, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'UseDoubleSorting', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_UseDoubleSorting', 'radio', '', '1=la_Yes||0=la_No', 40.07, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'MenuFrameWidth', '200', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_prompt_MenuFrameWidth', 'text', NULL, NULL, 40.08, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'ResizableFrames', '1', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_ResizableFrames', 'checkbox', '', '', 40.09, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'AutoRefreshIntervals', '1,5,15,30,60,120,240', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_AutoRefreshIntervals', 'text', '', '', 40.10, 0, 0, NULL); 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.11, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'DebugOnlyFormConfigurator', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_DebugOnlyFormConfigurator', 'checkbox', '', '', 40.12, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'DebugOnlyPromoBlockGroupConfigurator', '1', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_DebugOnlyPromoBlockGroupConfigurator', 'checkbox', '', '', 40.13, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'RememberLastAdminTemplate', '1', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_RememberLastAdminTemplate', 'checkbox', '', '', 40.14, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'UseHTTPAuth', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_UseHTTPAuth', 'checkbox', '', '', 40.15, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'HTTPAuthUsername', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_HTTPAuthUsername', 'text', '', '', 40.16, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'HTTPAuthPassword', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_HTTPAuthPassword', 'password', NULL, NULL, 40.17, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'HTTPAuthBypassIPs', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_HTTPAuthBypassIPs', 'text', '', '', 40.18, 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); INSERT INTO SystemSettings VALUES(DEFAULT, 'Smtp_Server', NULL, 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMailling', 'la_prompt_mailserver', 'text', NULL, NULL, 50.01, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'Smtp_Port', NULL, 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMailling', 'la_prompt_mailport', 'text', NULL, NULL, 50.02, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'Smtp_Authenticate', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMailling', 'la_prompt_mailauthenticate', 'checkbox', NULL, NULL, 50.03, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'Smtp_User', NULL, 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMailling', 'la_prompt_smtp_user', 'text', NULL, NULL, 50.04, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'Smtp_Pass', NULL, 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMailling', 'la_prompt_smtp_pass', 'text', NULL, NULL, 50.05, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'Smtp_DefaultHeaders', 'X-Mailer: In-Portal', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMailling', 'la_prompt_smtpheaders', 'textarea', NULL, 'COLS=40 ROWS=5', 50.06, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'MailFunctionHeaderSeparator', '1', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMailling', 'la_config_MailFunctionHeaderSeparator', 'radio', NULL, '1=la_Linux||2=la_Windows', 50.07, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'MailingListQueuePerStep', '10', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMailling', 'la_config_MailingListQueuePerStep', 'text', NULL, NULL, 50.08, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'MailingListSendPerStep', '10', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMailling', 'la_config_MailingListSendPerStep', 'text', NULL, NULL, 50.09, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'DefaultEmailRecipients', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMailling', 'la_config_DefaultEmailRecipients', 'text', NULL, NULL, 50.10, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'EmailDelivery', '1', '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); INSERT INTO SystemSettings VALUES(DEFAULT, 'UseOutputCompression', '1', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSystem', 'la_config_UseOutputCompression', 'checkbox', '', '', 60.01, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'OutputCompressionLevel', '7', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSystem', 'la_config_OutputCompressionLevel', 'text', '', '', 60.02, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'TrimRequiredFields', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSystem', 'la_config_TrimRequiredFields', 'checkbox', '', '', 60.03, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'RunScheduledTasksFromCron', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSystem', 'la_UseCronForRegularEvent', 'checkbox', NULL, NULL, 60.04, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'UseChangeLog', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSystem', 'la_config_UseChangeLog', 'checkbox', '', '', 60.05, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'Backup_Path', '/home/alex/web/in-portal.rc/system/backupdata', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSystem', 'la_config_backup_path', 'text', '', '', 60.06, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'SystemTagCache', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSystem', 'la_prompt_syscache_enable', 'checkbox', NULL, NULL, 60.07, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'SocketBlockingMode', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsSystem', 'la_prompt_socket_blocking_mode', 'checkbox', NULL, NULL, 60.08, 0, 0, NULL); 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, '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'); INSERT INTO SystemSettings VALUES(DEFAULT, 'EmailLogRotationInterval', '2419200', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsLogs', 'la_config_EmailLogRotationInterval', '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_EmailLogKeepForever', 65.02, 0, 0, 'hint:la_config_EmailLogRotationInterval'); 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 SystemSettings VALUES(DEFAULT, 'SessionLogRotationInterval', '2629800', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsLogs', 'la_config_SessionLogRotationInterval', 'select', '', '86400=la_opt_OneDay||604800=la_opt_OneWeek||1209600=la_opt_TwoWeeks||2629800=la_opt_OneMonth||7889400=la_opt_ThreeMonths||31557600=la_opt_OneYear||63115200=la_opt_TwoYears||94672800=la_opt_ThreeYears||157788000=la_opt_FiveYears||-1=la_opt_EmailLogKeepForever', 65.05, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'CSVExportDelimiter', '1', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsCSVExport', 'la_config_CSVExportDelimiter', 'select', NULL, '0=la_opt_Tab||1=la_opt_Comma||2=la_opt_Semicolon||3=la_opt_Space||4=la_opt_Colon', 70.01, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'CSVExportEnclosure', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsCSVExport', 'la_config_CSVExportEnclosure', 'radio', NULL, '0=la_Doublequotes||1=la_Quotes', 70.02, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'CSVExportSeparator', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsCSVExport', 'la_config_CSVExportSeparator', 'radio', NULL, '0=la_Linux||1=la_Windows', 70.03, 0, 1, NULL); -INSERT INTO SystemSettings VALUES(DEFAULT, 'CSVExportEncoding', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsCSVExport', 'la_config_CSVExportEncoding', 'radio', NULL, '0=la_Unicode||1=la_Regular', 70.04, 0, 1, NULL); +INSERT INTO SystemSettings VALUES(DEFAULT, 'CSVExportEncoding', '1', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsCSVExport', 'la_config_CSVExportEncoding', 'radio', NULL, '0=la_Unicode||1=la_Regular', 70.04, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'YahooApplicationId', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_Settings3rdPartyAPI', 'la_config_YahooApplicationId', 'text', NULL, NULL, 80.01, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'l_GoogleMapsAPIKey', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_Settings3rdPartyAPI', 'la_fld_LinkGoogleMapsAPIKey', 'text', NULL, NULL, 80.02, 0, 1, NULL); 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, 'TypeKitId', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_Settings3rdPartyAPI', 'la_config_TypeKitId', 'text', NULL, NULL, 80.05, 0, 1, NULL); # Section "in-portal:configure_users": INSERT INTO SystemSettings VALUES(DEFAULT, 'User_Allow_New', '3', 'In-Portal:Users', 'in-portal:configure_users', 'la_title_General', 'la_users_allow_new', 'radio', '', '1=la_opt_UserInstantRegistration||2=la_opt_UserNotAllowedRegistration||3=la_opt_UserUponApprovalRegistration||4=la_opt_UserEmailActivation', 10.01, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'AdvancedUserManagement', '0', 'In-Portal:Users', 'in-portal:configure_users', 'la_title_General', 'la_prompt_AdvancedUserManagement', 'checkbox', NULL, NULL, 10.011, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'RegistrationUsernameRequired', '0', 'In-Portal:Users', 'in-portal:configure_users', 'la_title_General', 'la_config_RegistrationUsernameRequired', 'checkbox', NULL, NULL, 10.02, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'RegistrationCaptcha', '0', 'In-Portal:Users', 'in-portal:configure_users', 'la_title_General', 'la_registration_captcha', 'checkbox', NULL, NULL, 10.025, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'Min_UserName', '3', 'In-Portal:Users', 'in-portal:configure_users', 'la_title_General', 'la_text_min_username', 'text', '', 'style="width: 50px;"', 10.03, 1, 0, NULL); INSERT INTO SystemSettings 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); INSERT INTO SystemSettings VALUES(DEFAULT, 'Min_Password', '5', 'In-Portal:Users', 'in-portal:configure_users', 'la_title_General', 'la_text_min_password', 'text', '', '', 10.04, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'Users_AllowReset', '180', 'In-Portal:Users', 'in-portal:configure_users', 'la_title_General', 'la_prompt_allow_reset', 'text', NULL, NULL, 10.05, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'UserEmailActivationTimeout', '', 'In-Portal:Users', 'in-portal:configure_users', 'la_title_General', 'la_config_UserEmailActivationTimeout', 'text', NULL, NULL, 10.051, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'User_Password_Auto', '0', 'In-Portal:Users', 'in-portal:configure_users', 'la_title_General', 'la_users_password_auto', 'checkbox', '', '', 10.06, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'User_MembershipExpirationReminder', '10', 'In-Portal:Users', 'in-portal:configure_users', 'la_title_General', 'la_MembershipExpirationReminder', 'text', NULL, '', 10.07, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'User_NewGroup', '13', 'In-Portal:Users', 'in-portal:configure_users', 'la_title_General', 'la_users_new_group', 'select', NULL, '0=lu_none||<SQL+>SELECT GroupId as OptionValue, Name as OptionName FROM <PREFIX>UserGroups WHERE Enabled=1 AND Personal=0</SQL>', 10.08, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'User_LoggedInGroup', '15', 'In-Portal:Users', 'in-portal:configure_users', 'la_title_General', 'la_users_assign_all_to', 'select', NULL, '0=lu_none||<SQL+>SELECT GroupId as OptionValue, Name as OptionName FROM <PREFIX>UserGroups WHERE Enabled=1 AND Personal=0</SQL>', 10.09, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'User_GuestGroup', '14', 'In-Portal:Users', 'in-portal:configure_users', 'la_title_General', 'la_users_guest_group', 'select', NULL, '0=lu_none||<SQL+>SELECT GroupId as OptionValue, Name as OptionName FROM <PREFIX>UserGroups WHERE Enabled=1 AND Personal=0</SQL>', 10.1, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'User_SubscriberGroup', '12', 'In-Portal:Users', 'in-portal:configure_users', 'la_title_General', 'la_users_subscriber_group', 'select', NULL, '0=lu_none||<SQL+>SELECT GroupId as OptionValue, Name as OptionName FROM <PREFIX>UserGroups WHERE Enabled=1 AND Personal=0</SQL>', 10.11, 0, 1, NULL); INSERT INTO SystemSettings 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>UserGroups WHERE Enabled=1 AND Personal=0</SQL>', 10.12, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'User_Default_Registration_Country', '', 'In-Portal:Users', 'in-portal:configure_users', 'la_title_General', 'la_config_DefaultRegistrationCountry', 'select', NULL, '=+||<SQL+>SELECT l%3$s_Name AS OptionName, CountryStateId AS OptionValue FROM <PREFIX>CountryStates WHERE Type = 1 ORDER BY OptionName</SQL>', 10.13, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'AllowSelectGroupOnFront', '0', 'In-Portal:Users', 'in-portal:configure_users', 'la_title_General', 'la_config_AllowSelectGroupOnFront', 'checkbox', NULL, NULL, 10.14, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'DefaultSettingsUserId', '-1', 'In-Portal:Users', 'in-portal:configure_users', 'la_title_General', 'la_prompt_DefaultUserId', 'text', NULL, NULL, 10.15, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'u_MaxImageCount', '5', 'In-Portal:Users', 'in-portal:configure_users', 'la_section_ImageSettings', 'la_config_MaxImageCount', 'text', '', '', 30.01, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'u_ThumbnailImageWidth', '120', 'In-Portal:Users', 'in-portal:configure_users', 'la_section_ImageSettings', 'la_config_ThumbnailImageWidth', 'text', '', '', 30.02, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'u_ThumbnailImageHeight', '120', 'In-Portal:Users', 'in-portal:configure_users', 'la_section_ImageSettings', 'la_config_ThumbnailImageHeight', 'text', '', '', 30.03, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'u_FullImageWidth', '450', 'In-Portal:Users', 'in-portal:configure_users', 'la_section_ImageSettings', 'la_config_FullImageWidth', 'text', '', '', 30.04, 0, 0, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'u_FullImageHeight', '450', 'In-Portal:Users', 'in-portal:configure_users', 'la_section_ImageSettings', 'la_config_FullImageHeight', 'text', '', '', 30.05, 0, 0, NULL); # Section "in-portal:configuration_search": INSERT INTO SystemSettings VALUES(DEFAULT, 'SearchRel_Increase_category', '30', 'In-Portal', 'in-portal:configuration_search', 'la_config_DefaultIncreaseImportance', 'la_text_increase_importance', 'text', NULL, NULL, 0, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'SearchRel_Keyword_category', '90', 'In-Portal', 'in-portal:configuration_search', 'la_config_SearchRel_DefaultKeyword', 'la_text_keyword', 'text', NULL, NULL, 0, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'SearchRel_Pop_category', '5', 'In-Portal', 'in-portal:configuration_search', 'la_config_DefaultPop', 'la_text_popularity', 'text', NULL, NULL, 0, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'SearchRel_Rating_category', '5', 'In-Portal', 'in-portal:configuration_search', 'la_config_DefaultRating', 'la_prompt_Rating', 'text', NULL, NULL, 0, 0, 1, NULL); INSERT INTO SystemSettings VALUES(DEFAULT, 'CategoriesRebuildSerial', '0', 'In-Portal', '', '', '', '', NULL, NULL, 0, 0, 0, NULL); INSERT INTO ItemTypes VALUES (1, 'In-Portal', 'c', 'Categories', 'Name', 'CreatedById', NULL, NULL, 'la_ItemTab_Categories', 1, 'admin/category/addcategory.php', 'clsCategory', 'Category'); INSERT INTO ItemTypes VALUES (6, 'In-Portal', 'u', 'Users', 'Username', 'PortalUserId', NULL, NULL, '', 0, '', 'clsPortalUser', 'User'); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.ADD', NULL, 1, 0, 'Core', 'Add User', 0, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.ADD', NULL, 1, 1, 'Core', 'Add User', 1, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.APPROVE', NULL, 1, 0, 'Core', 'Approve User', 0, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.APPROVE', NULL, 1, 1, 'Core', 'Approve User', 1, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.VALIDATE', NULL, 1, 0, 'Core', 'Validate User', 0, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.VALIDATE', NULL, 1, 1, 'Core', 'Validate User', 1, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.DENY', NULL, 1, 0, 'Core', 'Deny User', 0, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.DENY', NULL, 1, 1, 'Core', 'Deny User', 1, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.ADD.PENDING', NULL, 1, 0, 'Core', 'Add Pending User', 0, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.ADD.PENDING', NULL, 1, 1, 'Core', 'Add Pending User', 1, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'CATEGORY.ADD', NULL, 1, 0, 'Core', 'Add Category', 0, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'CATEGORY.ADD.PENDING', NULL, 1, 0, 'Core', 'Add Pending Category', 0, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'CATEGORY.ADD.PENDING', NULL, 1, 1, 'Core', 'Add Pending Category', 1, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'CATEGORY.ADD', NULL, 1, 1, 'Core', 'Add Category', 1, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'CATEGORY.APPROVE', NULL, 1, 0, 'Core', 'Approve Category', 0, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'CATEGORY.DENY', NULL, 1, 0, 'Core', 'Deny Category', 0, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.SUBSCRIBE', NULL, 1, 0, 'Core', 'User subscribed', 0, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.SUBSCRIBE', NULL, 1, 1, 'Core', 'User subscribed', 1, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.UNSUBSCRIBE', NULL, 1, 0, 'Core', 'User unsubscribed', 0, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.UNSUBSCRIBE', NULL, 1, 1, 'Core', 'User unsubscribed', 1, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.SUGGEST', NULL, 1, 0, 'Core', 'Suggest to a friend', 0, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.SUGGEST', NULL, 1, 1, 'Core', 'Suggest to a friend', 1, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.PSWDC', NULL, 1, 0, 'Core', 'Password Confirmation', 0, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.MEMBERSHIP.EXPIRED', NULL, 1, 0, 'Core', 'Membership expired', 0, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.MEMBERSHIP.EXPIRED', NULL, 1, 0, 'Core', 'Membership expired', 1, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.MEMBERSHIP.EXPIRATION.NOTICE', NULL, 1, 0, 'Core', 'Membership expiration notice', 0, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'USER.MEMBERSHIP.EXPIRATION.NOTICE', NULL, 1, 0, 'Core', 'Membership expiration notice', 1, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'COMMON.FOOTER', NULL, 1, 0, 'Core', 'Common Footer Template', 1, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'FORM.SUBMITTED', NULL, 1, 0, 'Core', 'This e-mail is sent to a user after filling in the Contact Us form', 1, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'FORM.SUBMITTED', NULL, 1, 0, 'Core', 'This e-mail is sent to a user after filling in the Contact Us form', 0, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'FORM.SUBMISSION.REPLY.TO.USER', NULL, 1, 0, 'Core', 'Admin Reply to User Form Submission', 1, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'FORM.SUBMISSION.REPLY.FROM.USER', NULL, 1, 0, 'Core', 'User Replied to It\'s Form Submission', 1, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'FORM.SUBMISSION.REPLY.FROM.USER.BOUNCED', NULL, 1, 0, 'Core', 'Form Submission Admin Reply Delivery Failure', 1, 1, 1); INSERT INTO EmailTemplates (TemplateId, TemplateName, 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 EmailTemplates (TemplateId, TemplateName, 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); INSERT INTO EmailTemplates (TemplateId, TemplateName, ReplacementTags, Enabled, FrontEndOnly, Module, Description, Type, AllowChangingSender, AllowChangingRecipient) VALUES(DEFAULT, 'ROOT.RESET.PASSWORD', NULL, 1, 0, 'Core', 'Root Reset Password', 1, 1, 0); INSERT INTO EmailTemplates (TemplateId, TemplateName, 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 EmailTemplates (TemplateId, TemplateName, 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); INSERT INTO EmailTemplates (TemplateId, TemplateName, 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); INSERT INTO IdGenerator VALUES ('100'); INSERT INTO UserGroups VALUES(15, 'Everyone', 'Everyone', 0, 1, 0, 1, 0, NULL); INSERT INTO UserGroups VALUES(13, 'Member', '', 1054738682, 0, 0, 1, 1, NULL); INSERT INTO UserGroups VALUES(12, 'Subscribers', '', 1054738670, 0, 0, 1, 0, NULL); INSERT INTO UserGroups VALUES(14, 'Guest', 'Guest User', 0, 1, 0, 1, 0, NULL); INSERT INTO UserGroups VALUES(11, 'admin', NULL, 1054738405, 0, 0, 1, 0, NULL); INSERT INTO CountryStates (CountryStateId, Type, StateCountryId, IsoCode, ShortIsoCode) VALUES (1, 1, NULL, 'AFG', 'AF'), (2, 1, NULL, 'ALB', 'AL'), (3, 1, NULL, 'DZA', 'DZ'), (4, 1, NULL, 'ASM', 'AS'), (5, 1, NULL, 'AND', 'AD'), (6, 1, NULL, 'AGO', 'AO'), (7, 1, NULL, 'AIA', 'AI'), (8, 1, NULL, 'ATA', 'AQ'), (9, 1, NULL, 'ATG', 'AG'), (10, 1, NULL, 'ARG', 'AR'), (11, 1, NULL, 'ARM', 'AM'), (12, 1, NULL, 'ABW', 'AW'), (13, 1, NULL, 'AUS', 'AU'), (14, 1, NULL, 'AUT', 'AT'), (15, 1, NULL, 'AZE', 'AZ'), (16, 1, NULL, 'BHS', 'BS'), (17, 1, NULL, 'BHR', 'BH'), (18, 1, NULL, 'BGD', 'BD'), (19, 1, NULL, 'BRB', 'BB'), (20, 1, NULL, 'BLR', 'BY'), (21, 1, NULL, 'BEL', 'BE'), (22, 1, NULL, 'BLZ', 'BZ'), (23, 1, NULL, 'BEN', 'BJ'), (24, 1, NULL, 'BMU', 'BM'), (25, 1, NULL, 'BTN', 'BT'), (26, 1, NULL, 'BOL', 'BO'), (27, 1, NULL, 'BIH', 'BA'), (28, 1, NULL, 'BWA', 'BW'), (29, 1, NULL, 'BVT', 'BV'), (30, 1, NULL, 'BRA', 'BR'), (31, 1, NULL, 'IOT', 'IO'), (32, 1, NULL, 'BRN', 'BN'), (33, 1, NULL, 'BGR', 'BG'), (34, 1, NULL, 'BFA', 'BF'), (35, 1, NULL, 'BDI', 'BI'), (36, 1, NULL, 'KHM', 'KH'), (37, 1, NULL, 'CMR', 'CM'), (38, 1, NULL, 'CAN', 'CA'), (39, 1, NULL, 'CPV', 'CV'), (40, 1, NULL, 'CYM', 'KY'), (41, 1, NULL, 'CAF', 'CF'), (42, 1, NULL, 'TCD', 'TD'), (43, 1, NULL, 'CHL', 'CL'), (44, 1, NULL, 'CHN', 'CN'), (45, 1, NULL, 'CXR', 'CX'), (46, 1, NULL, 'CCK', 'CC'), (47, 1, NULL, 'COL', 'CO'), (48, 1, NULL, 'COM', 'KM'), (49, 1, NULL, 'COD', 'CD'), (50, 1, NULL, 'COG', 'CG'), (51, 1, NULL, 'COK', 'CK'), (52, 1, NULL, 'CRI', 'CR'), (53, 1, NULL, 'CIV', 'CI'), (54, 1, NULL, 'HRV', 'HR'), (55, 1, NULL, 'CUB', 'CU'), (56, 1, NULL, 'CYP', 'CY'), (57, 1, NULL, 'CZE', 'CZ'), (58, 1, NULL, 'DNK', 'DK'), (59, 1, NULL, 'DJI', 'DJ'), (60, 1, NULL, 'DMA', 'DM'), (61, 1, NULL, 'DOM', 'DO'), (62, 1, NULL, 'TLS', 'TL'), (63, 1, NULL, 'ECU', 'EC'), (64, 1, NULL, 'EGY', 'EG'), (65, 1, NULL, 'SLV', 'SV'), (66, 1, NULL, 'GNQ', 'GQ'), (67, 1, NULL, 'ERI', 'ER'), (68, 1, NULL, 'EST', 'EE'), (69, 1, NULL, 'ETH', 'ET'), (70, 1, NULL, 'FLK', 'FK'), (71, 1, NULL, 'FRO', 'FO'), (72, 1, NULL, 'FJI', 'FJ'), (73, 1, NULL, 'FIN', 'FI'), (74, 1, NULL, 'FRA', 'FR'), (75, 1, NULL, 'FXX', 'FX'), (76, 1, NULL, 'GUF', 'GF'), (77, 1, NULL, 'PYF', 'PF'), (78, 1, NULL, 'ATF', 'TF'), (79, 1, NULL, 'GAB', 'GA'), (80, 1, NULL, 'GMB', 'GM'), (81, 1, NULL, 'GEO', 'GE'), (82, 1, NULL, 'DEU', 'DE'), (83, 1, NULL, 'GHA', 'GH'), (84, 1, NULL, 'GIB', 'GI'), (85, 1, NULL, 'GRC', 'GR'), (86, 1, NULL, 'GRL', 'GL'), (87, 1, NULL, 'GRD', 'GD'), (88, 1, NULL, 'GLP', 'GP'), (89, 1, NULL, 'GUM', 'GU'), (90, 1, NULL, 'GTM', 'GT'), (91, 1, NULL, 'GIN', 'GN'), (92, 1, NULL, 'GNB', 'GW'), (93, 1, NULL, 'GUY', 'GY'), (94, 1, NULL, 'HTI', 'HT'), (95, 1, NULL, 'HMD', 'HM'), (96, 1, NULL, 'HND', 'HN'), (97, 1, NULL, 'HKG', 'HK'), (98, 1, NULL, 'HUN', 'HU'), (99, 1, NULL, 'ISL', 'IS'), (100, 1, NULL, 'IND', 'IN'), (101, 1, NULL, 'IDN', 'ID'), (102, 1, NULL, 'IRN', 'IR'), (103, 1, NULL, 'IRQ', 'IQ'), (104, 1, NULL, 'IRL', 'IE'), (105, 1, NULL, 'ISR', 'IL'), (106, 1, NULL, 'ITA', 'IT'), (107, 1, NULL, 'JAM', 'JM'), (108, 1, NULL, 'JPN', 'JP'), (109, 1, NULL, 'JOR', 'JO'), (110, 1, NULL, 'KAZ', 'KZ'), (111, 1, NULL, 'KEN', 'KE'), (112, 1, NULL, 'KIR', 'KI'), (113, 1, NULL, 'PRK', 'KP'), (114, 1, NULL, 'KOR', 'KR'), (115, 1, NULL, 'KWT', 'KW'), (116, 1, NULL, 'KGZ', 'KG'), (117, 1, NULL, 'LAO', 'LA'), (118, 1, NULL, 'LVA', 'LV'), (119, 1, NULL, 'LBN', 'LB'), (120, 1, NULL, 'LSO', 'LS'), (121, 1, NULL, 'LBR', 'LR'), (122, 1, NULL, 'LBY', 'LY'), (123, 1, NULL, 'LIE', 'LI'), (124, 1, NULL, 'LTU', 'LT'), (125, 1, NULL, 'LUX', 'LU'), (126, 1, NULL, 'MAC', 'MO'), (127, 1, NULL, 'MKD', 'MK'), (128, 1, NULL, 'MDG', 'MG'), (129, 1, NULL, 'MWI', 'MW'), (130, 1, NULL, 'MYS', 'MY'), (131, 1, NULL, 'MDV', 'MV'), (132, 1, NULL, 'MLI', 'ML'), (133, 1, NULL, 'MLT', 'MT'), (134, 1, NULL, 'MHL', 'MH'), (135, 1, NULL, 'MTQ', 'MQ'), (136, 1, NULL, 'MRT', 'MR'), (137, 1, NULL, 'MUS', 'MU'), (138, 1, NULL, 'MYT', 'YT'), (139, 1, NULL, 'MEX', 'MX'), (140, 1, NULL, 'FSM', 'FM'), (141, 1, NULL, 'MDA', 'MD'), (142, 1, NULL, 'MCO', 'MC'), (143, 1, NULL, 'MNG', 'MN'), (144, 1, NULL, 'MSR', 'MS'), (145, 1, NULL, 'MAR', 'MA'), (146, 1, NULL, 'MOZ', 'MZ'), (147, 1, NULL, 'MMR', 'MM'), (148, 1, NULL, 'NAM', 'NA'), (149, 1, NULL, 'NRU', 'NR'), (150, 1, NULL, 'NPL', 'NP'), (151, 1, NULL, 'NLD', 'NL'), (152, 1, NULL, 'ANT', 'AN'), (153, 1, NULL, 'NCL', 'NC'), (154, 1, NULL, 'NZL', 'NZ'), (155, 1, NULL, 'NIC', 'NI'), (156, 1, NULL, 'NER', 'NE'), (157, 1, NULL, 'NGA', 'NG'), (158, 1, NULL, 'NIU', 'NU'), (159, 1, NULL, 'NFK', 'NF'), (160, 1, NULL, 'MNP', 'MP'), (161, 1, NULL, 'NOR', 'NO'), (162, 1, NULL, 'OMN', 'OM'), (163, 1, NULL, 'PAK', 'PK'), (164, 1, NULL, 'PLW', 'PW'), (165, 1, NULL, 'PSE', 'PS'), (166, 1, NULL, 'PAN', 'PA'), (167, 1, NULL, 'PNG', 'PG'), (168, 1, NULL, 'PRY', 'PY'), (169, 1, NULL, 'PER', 'PE'), (170, 1, NULL, 'PHL', 'PH'), (171, 1, NULL, 'PCN', 'PN'), (172, 1, NULL, 'POL', 'PL'), (173, 1, NULL, 'PRT', 'PT'), (174, 1, NULL, 'PRI', 'PR'), (175, 1, NULL, 'QAT', 'QA'), (176, 1, NULL, 'REU', 'RE'), (177, 1, NULL, 'ROU', 'RO'), (178, 1, NULL, 'RUS', 'RU'), (179, 1, NULL, 'RWA', 'RW'), (180, 1, NULL, 'KNA', 'KN'), (181, 1, NULL, 'LCA', 'LC'), (182, 1, NULL, 'VCT', 'VC'), (183, 1, NULL, 'WSM', 'WS'), (184, 1, NULL, 'SMR', 'SM'), (185, 1, NULL, 'STP', 'ST'), (186, 1, NULL, 'SAU', 'SA'), (187, 1, NULL, 'SEN', 'SN'), (188, 1, NULL, 'SYC', 'SC'), (189, 1, NULL, 'SLE', 'SL'), (190, 1, NULL, 'SGP', 'SG'), (191, 1, NULL, 'SVK', 'SK'), (192, 1, NULL, 'SVN', 'SI'), (193, 1, NULL, 'SLB', 'SB'), (194, 1, NULL, 'SOM', 'SO'), (195, 1, NULL, 'ZAF', 'ZA'), (196, 1, NULL, 'SGS', 'GS'), (197, 1, NULL, 'ESP', 'ES'), (198, 1, NULL, 'LKA', 'LK'), (199, 1, NULL, 'SHN', 'SH'), (200, 1, NULL, 'SPM', 'PM'), (201, 1, NULL, 'SDN', 'SD'), (202, 1, NULL, 'SUR', 'SR'), (203, 1, NULL, 'SJM', 'SJ'), (204, 1, NULL, 'SWZ', 'SZ'), (205, 1, NULL, 'SWE', 'SE'), (206, 1, NULL, 'CHE', 'CH'), (207, 1, NULL, 'SYR', 'SY'), (208, 1, NULL, 'TWN', 'TW'), (209, 1, NULL, 'TJK', 'TJ'), (210, 1, NULL, 'TZA', 'TZ'), (211, 1, NULL, 'THA', 'TH'), (212, 1, NULL, 'TGO', 'TG'), (213, 1, NULL, 'TKL', 'TK'), (214, 1, NULL, 'TON', 'TO'), (215, 1, NULL, 'TTO', 'TT'), (216, 1, NULL, 'TUN', 'TN'), (217, 1, NULL, 'TUR', 'TR'), (218, 1, NULL, 'TKM', 'TM'), (219, 1, NULL, 'TCA', 'TC'), (220, 1, NULL, 'TUV', 'TV'), (221, 1, NULL, 'UGA', 'UG'), (222, 1, NULL, 'UKR', 'UA'), (223, 1, NULL, 'ARE', 'AE'), (224, 1, NULL, 'GBR', 'GB'), (225, 1, NULL, 'USA', 'US'), (226, 1, NULL, 'UMI', 'UM'), (227, 1, NULL, 'URY', 'UY'), (228, 1, NULL, 'UZB', 'UZ'), (229, 1, NULL, 'VUT', 'VU'), (230, 1, NULL, 'VAT', 'VA'), (231, 1, NULL, 'VEN', 'VE'), (232, 1, NULL, 'VNM', 'VN'), (233, 1, NULL, 'VGB', 'VG'), (234, 1, NULL, 'VIR', 'VI'), (235, 1, NULL, 'WLF', 'WF'), (236, 1, NULL, 'ESH', 'EH'), (237, 1, NULL, 'YEM', 'YE'), (238, 1, NULL, 'YUG', 'YU'), (239, 1, NULL, 'ZMB', 'ZM'), (240, 1, NULL, 'ZWE', 'ZW'), (370, 2, 38, 'YT', NULL), (369, 2, 38, 'SK', NULL), (368, 2, 38, 'QC', NULL), (367, 2, 38, 'PE', NULL), (366, 2, 38, 'ON', NULL), (365, 2, 38, 'NU', NULL), (364, 2, 38, 'NS', NULL), (363, 2, 38, 'NT', NULL), (362, 2, 38, 'NL', NULL), (361, 2, 38, 'NB', NULL), (360, 2, 38, 'MB', NULL), (359, 2, 38, 'BC', NULL), (358, 2, 38, 'AB', NULL), (357, 2, 225, 'DC', NULL), (356, 2, 225, 'WY', NULL), (355, 2, 225, 'WI', NULL), (354, 2, 225, 'WV', NULL), (353, 2, 225, 'WA', NULL), (352, 2, 225, 'VA', NULL), (351, 2, 225, 'VT', NULL), (350, 2, 225, 'UT', NULL), (349, 2, 225, 'TX', NULL), (348, 2, 225, 'TN', NULL), (347, 2, 225, 'SD', NULL), (346, 2, 225, 'SC', NULL), (345, 2, 225, 'RI', NULL), (344, 2, 225, 'PR', NULL), (343, 2, 225, 'PA', NULL), (342, 2, 225, 'OR', NULL), (341, 2, 225, 'OK', NULL), (340, 2, 225, 'OH', NULL), (339, 2, 225, 'ND', NULL), (338, 2, 225, 'NC', NULL), (337, 2, 225, 'NY', NULL), (336, 2, 225, 'NM', NULL), (335, 2, 225, 'NJ', NULL), (334, 2, 225, 'NH', NULL), (333, 2, 225, 'NV', NULL), (332, 2, 225, 'NE', NULL), (331, 2, 225, 'MT', NULL), (330, 2, 225, 'MO', NULL), (329, 2, 225, 'MS', NULL), (328, 2, 225, 'MN', NULL), (327, 2, 225, 'MI', NULL), (326, 2, 225, 'MA', NULL), (325, 2, 225, 'MD', NULL), (324, 2, 225, 'ME', NULL), (323, 2, 225, 'LA', NULL), (322, 2, 225, 'KY', NULL), (321, 2, 225, 'KS', NULL), (320, 2, 225, 'IA', NULL), (319, 2, 225, 'IN', NULL), (318, 2, 225, 'IL', NULL), (317, 2, 225, 'ID', NULL), (316, 2, 225, 'HI', NULL), (315, 2, 225, 'GA', NULL), (314, 2, 225, 'FL', NULL), (313, 2, 225, 'DE', NULL), (312, 2, 225, 'CT', NULL), (311, 2, 225, 'CO', NULL), (310, 2, 225, 'CA', NULL), (309, 2, 225, 'AR', NULL), (308, 2, 225, 'AZ', NULL), (307, 2, 225, 'AK', NULL), (306, 2, 225, 'AL', NULL); INSERT INTO CategoryPermissionsConfig VALUES (DEFAULT, 'CATEGORY.VIEW', 'la_PermName_Category.View_desc', 'In-Portal', 1); INSERT INTO CategoryPermissionsConfig VALUES (DEFAULT, 'CATEGORY.ADD', 'la_PermName_Category.Add_desc', 'In-Portal', 1); INSERT INTO CategoryPermissionsConfig VALUES (DEFAULT, 'CATEGORY.DELETE', 'la_PermName_Category.Delete_desc', 'In-Portal', 1); INSERT INTO CategoryPermissionsConfig VALUES (DEFAULT, 'CATEGORY.ADD.PENDING', 'la_PermName_Category.AddPending_desc', 'In-Portal', 1); INSERT INTO CategoryPermissionsConfig VALUES (DEFAULT, 'CATEGORY.MODIFY', 'la_PermName_Category.Modify_desc', 'In-Portal', 1); INSERT INTO CategoryPermissionsConfig VALUES (DEFAULT, 'CATEGORY.REVISION.ADD', 'la_PermName_Category.Revision.Add_desc', 'In-Portal', 1); INSERT INTO CategoryPermissionsConfig VALUES (DEFAULT, 'CATEGORY.REVISION.ADD.PENDING', 'la_PermName_Category.Revision.Add.Pending_desc', 'In-Portal', 1); INSERT INTO CategoryPermissionsConfig VALUES (DEFAULT, 'CATEGORY.REVISION.MODERATE', 'la_PermName_Category.Revision.Moderate_desc', 'In-Portal', 1); INSERT INTO CategoryPermissionsConfig VALUES (DEFAULT, 'CATEGORY.REVISION.HISTORY.VIEW', 'la_PermName_Category.Revision.History.View_desc', 'In-Portal', 1); INSERT INTO CategoryPermissionsConfig VALUES (DEFAULT, 'CATEGORY.REVISION.HISTORY.RESTORE', 'la_PermName_Category.Revision.History.Restore_desc', 'In-Portal', 1); INSERT INTO CategoryPermissionsConfig VALUES (DEFAULT, 'ADMIN', 'la_PermName_Admin_desc', 'Admin', 1); INSERT INTO CategoryPermissionsConfig VALUES (DEFAULT, 'LOGIN', 'la_PermName_Login_desc', 'Front', 1); INSERT INTO CategoryPermissionsConfig VALUES (DEFAULT, 'DEBUG.ITEM', 'la_PermName_Debug.Item_desc', 'Admin', 1); INSERT INTO CategoryPermissionsConfig VALUES (DEFAULT, 'DEBUG.LIST', 'la_PermName_Debug.List_desc', 'Admin', 1); INSERT INTO CategoryPermissionsConfig VALUES (DEFAULT, 'DEBUG.INFO', 'la_PermName_Debug.Info_desc', 'Admin', 1); INSERT INTO CategoryPermissionsConfig VALUES (DEFAULT, 'PROFILE.MODIFY', 'la_PermName_Profile.Modify_desc', 'Admin', 1); INSERT INTO CategoryPermissionsConfig VALUES (DEFAULT, 'SHOWLANG', 'la_PermName_ShowLang_desc', 'Admin', 1); INSERT INTO CategoryPermissionsConfig VALUES (DEFAULT, 'FAVORITES', 'la_PermName_favorites_desc', 'In-Portal', 1); INSERT INTO CategoryPermissionsConfig VALUES (DEFAULT, 'SYSTEM_ACCESS.READONLY', 'la_PermName_SystemAccess.ReadOnly_desc', 'Admin', 1); INSERT INTO CategoryPermissionsCache VALUES (DEFAULT, 0, 1, '11,12,13,14,15'); INSERT INTO Permissions VALUES (DEFAULT, 'LOGIN', 13, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'LOGIN', 12, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'LOGIN', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'ADMIN', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:root.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:system.view', 11, 1, 1, 0); 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.add', 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:user_list.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:user_list.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:user_list.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:user_list.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:admins.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:admins.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:admins.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:admins.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_lang.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_lang.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_lang.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_lang.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:phrases.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:phrases.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:phrases.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:phrases.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configemail.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configemail.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configemail.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configemail.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES(DEFAULT, 'CATEGORY.VIEW', 15, 1, 0, 1); INSERT INTO Permissions VALUES(DEFAULT, 'CATEGORY.ADD', 11, 1, 0, 1); INSERT INTO Permissions VALUES(DEFAULT, 'CATEGORY.ADD.PENDING', 13, 1, 0, 1); INSERT INTO Permissions VALUES(DEFAULT, 'CATEGORY.DELETE', 11, 1, 0, 1); INSERT INTO Permissions VALUES(DEFAULT, 'CATEGORY.MODIFY', 11, 1, 0, 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); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:service.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:service.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:scheduled_tasks.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:scheduled_tasks.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:scheduled_tasks.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:scheduled_tasks.view', 11, 1, 1, 0); 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); 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); 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); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:site.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:browse.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:advanced_view.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:reviews.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_categories.view', 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_categories.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configuration_search.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configuration_search.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configuration_custom.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configuration_custom.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configuration_custom.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configuration_custom.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:users.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:user_list.advanced:ban', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:user_list.advanced:send_email', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:user_groups.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:user_groups.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:user_groups.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:user_groups.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:user_groups.advanced:send_email', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:user_groups.advanced:manage_permissions', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_users.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_users.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_users.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:user_custom.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:user_custom.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:user_custom.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:user_custom.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:user_banlist.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:user_banlist.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:user_banlist.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:user_banlist.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:reports.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:log_summary.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:searchlog.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:searchlog.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:sessionlog.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:sessionlog.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:emaillog.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:emaillog.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:emaillog.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:visits.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:visits.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:system_logs.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:system_logs.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:system_logs.delete', 11, 1, 1, 0); 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:configure_general.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_general.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_general.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:modules.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:mod_status.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:mod_status.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:mod_status.advanced:approve', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:mod_status.advanced:decline', 11, 1, 1, 0); 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); 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); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:addmodule.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:addmodule.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:addmodule.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:tag_library.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_themes.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_themes.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_themes.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_themes.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_styles.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_styles.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_styles.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_styles.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_lang.advanced:set_primary', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_lang.advanced:import', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:configure_lang.advanced:export', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:tools.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:backup.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:restore.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:export.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:main_import.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:sql_query.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:sql_query.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:server_info.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:help.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:browse_site.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:forms.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:forms.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:forms.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:forms.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:submissions.view', 11, 1, 1, 0); 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 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); 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); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:email_queue.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:email_queue.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:session_logs.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:session_logs.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:change_logs.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:change_logs.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:change_logs.delete', 11, 1, 1, 0); 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 Permissions VALUES (DEFAULT, 'in-portal:thesaurus.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:thesaurus.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:thesaurus.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:thesaurus.delete', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:skins.view', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:skins.add', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:skins.edit', 11, 1, 1, 0); INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:skins.delete', 11, 1, 1, 0); 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); INSERT INTO AdminSkins 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 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\nbody, td {\r\n /* fix for Firefox, when font-size was not inherited in table cells */\r\n font-size: 9pt;\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 !important;\r\n border-top-width: 0px;\r\n border-collapse: collapse;\r\n}\r\n\r\n.bordered-no-bottom {\r\n border-top-width: 1px;\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\ntable.head-table {\r\n background: url(''@@base_url@@/core/admin_templates/img/top_frame/right_background.png'') top right @@HeadBgColor@@ no-repeat;\r\n}\r\n\r\n.head-table tr td, .head-table tr td a {\r\n color: @@HeadColor@@\r\n}\r\n\r\ndiv#extra_toolbar td.button-active {\r\n background: url(''@@base_url@@/core/admin_templates/img/top_frame/toolbar_button_background.gif'') bottom left repeat-x;\r\n height: 22px;\r\n}\r\n\r\ndiv#extra_toolbar td.button-active a {\r\n color: black;\r\n text-decoration: none;\r\n}\r\n\r\ntd.kx-block-header, .head-table tr td.kx-block-header{\r\n color: @@HeadBarColor@@;\r\n background: url(''@@base_url@@/core/admin_templates/img/top_frame/toolbar_background.gif'') repeat-x top left;\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 font-weight: bold;\r\n color: #0080C8;\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: #FFFFFF;\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@@/core/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@@/core/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 margin-left: 3px !important;\r\n white-space: nowrap;\r\n}\r\n\r\n.tab-active {\r\n background-color: #4487D9;\r\n}\r\n\r\n.tab a {\r\n color: #4487D9;\r\n font-weight: bold;\r\n}\r\n\r\n.tab-active a {\r\n color: #FFFFFF;\r\n font-weight: bold;\r\n}\r\n\r\na.scroll-left, a.scroll-right {\r\n cursor: pointer;\r\n display: block;\r\n float: left;\r\n height: 18px;\r\n margin: 0px 1px;\r\n width: 18px;\r\n}\r\n\r\na.scroll-left {\r\n background: transparent url(''@@base_url@@/core/admin_templates/img/tabs/left.png'') no-repeat scroll 0 0;\r\n}\r\n\r\na.scroll-right {\r\n background: transparent url(''@@base_url@@/core/admin_templates/img/tabs/right.png'') no-repeat scroll 0 0;\r\n}\r\n\r\na.disabled {\r\n visibility: hidden !important;\r\n}\r\n\r\na.scroll-left:hover, a.scroll-right:hover {\r\n background-position: 0 -18px;\r\n}\r\n\r\ntd.scroll-right-container {\r\n width: 20px;\r\n}\r\n\r\ntd.scroll-right-container.disabled, td.scroll-right-container.disabled * {\r\n width: 0px;\r\n margin: 0px;\r\n}\r\n\r\ntd.scroll-right-container.disabled br {\r\n display: none;\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.layout-only-table td {\r\n border: none !important;\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, .grid-edit-table .edit-form-odd > td, .grid-edit-table .edit-form-even > td {\r\n font-weight: normal;\r\n color: @@OddColor@@;\r\n background-color: @@OddBgColor@@;\r\n padding: 3px 5px 3px 5px;\r\n overflow: hidden;\r\n border-right: 1px solid #c9c9c9;\r\n}\r\n.grid-data-row-even td, .table-color2, .grid-edit-table .edit-form-even > td {\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, table tr.grid-data-row[_row_highlighted] 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, table tr.grid-data-row[_row_selected] td {\r\n background: #FEF2D6;\r\n}\r\n\r\n.grid-data-row-even-selected td, .grid-data-row-even[_row_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 #777;\r\n}\r\n\r\n/* Filters row */\r\ntr.grid-header-row-1 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, input.filter-active, select.filter-active {\r\n margin-bottom: 0px;\r\n border: 1px solid #aaa;\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\ndiv.filter, div.filter-active {\r\n background-color: white;\r\n border: 1px solid #AAAAAA;\r\n color: black;\r\n font-weight: normal;\r\n padding: 3px;\r\n}\r\n\r\ndiv.filter-active {\r\n background-color: #FFFF00;\r\n}\r\n\r\ndiv.multioptions_filter {\r\n position: absolute;\r\n z-index: 100;\r\n color: black;\r\n background-color: white;\r\n border: 1px solid black;\r\n padding: 3px 5px;\r\n display: none;\r\n vertical-align: middle;\r\n}\r\n\r\n/* Column titles row */\r\ntr.grid-header-row-0 td {\r\n height: 25px;\r\n font-weight: bold;\r\n background-color: @@ColumnTitlesBgColor@@;\r\n color: @@ColumnTitlesColor@@;\r\n border-bottom: 1px solid black;\r\n}\r\n\r\ntr.grid-header-row-0 td a {\r\n color: @@ColumnTitlesColor@@;\r\n}\r\n\r\ntr.grid-header-row-0 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: 1px solid #C9C9C9;\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-1 td.grid-header-col-1 {\r\n text-align: center;\r\n vertical-align: middle !important;\r\n}\r\n\r\ntr.grid-header-row-1 td.grid-header-col-1 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 !important;\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 vertical-align: middle;\r\n}\r\n\r\n/* remove top-border from first sub-section element */\r\ntable.edit-form .subsectiontitle:first-child, table.bordered .subsectiontitle:first-child {\r\n border-top-width: 0;\r\n}\r\n\r\n.subsectiontitle td {\r\n vertical-align: middle;\r\n /*padding: 3px 5px 3px 5px;*/\r\n padding: 1px 5px;\r\n}\r\n\r\n.label-cell {\r\n background: #DEE7F6 url(''@@base_url@@/core/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: 160px;\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@@/core/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@@/core/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.CodeMirror {\r\n font-size: 13px;\r\n border: 1px solid black;\r\n}\r\n\r\n.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}\r\n.CodeMirror-activeline-background {background: #e8f2ff !important;}\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.highlight-area, .code-highlight-area {\r\n border: 1px solid black;\r\n padding: 8px;\r\n font-family: monospace !important;\r\n font-size: 12px;\r\n overflow: auto;\r\n}\r\n\r\n.code-highlight-area {\r\n background-color: #F6F6F6;\r\n}\r\n\r\n.error {\r\n color: red;\r\n}\r\n.error-cell {\r\n color: red;\r\n}\r\n\r\n.field-required {\r\n color: red;\r\n}\r\n\r\n.warning-table {\r\n background-color: #F0F1EB;\r\n border: 1px solid #000000;\r\n border-collapse: collapse;\r\n border-top-width: 0px;\r\n}\r\n\r\n.form-notice, .form-warning {\r\n font-size: 11px;\r\n}\r\n\r\n.form-warning {\r\n color: red;\r\n}\r\n\r\n.form-notice {\r\n color: green;\r\n}\r\n\r\n.priority {\r\n color: red;\r\n padding-left: 1px;\r\n padding-right: 1px;\r\n font-size: 11px;\r\n}\r\n\r\n.small-statistics {\r\n font-size: 11px;\r\n color: #707070;\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/* Uploader */\r\n.uploader-queue div.file {\r\n font-size: 11px;\r\n border: 1px solid #7F99C5;\r\n padding: 3px;\r\n background-color: #DEE7F6;\r\n margin-bottom: 2px;\r\n}\r\n\r\n.uploader-queue .left {\r\n float: left;\r\n vertical-align: top;\r\n}\r\n\r\n.uploader-queue .file-label {\r\n margin-left: 5px;\r\n}\r\n\r\n.uploader-queue .preview .delete-checkbox {\r\n margin-top: -3px;\r\n}\r\n\r\n.uploader-queue .progress-container {\r\n margin: 2px 5px 0px 5px;\r\n}\r\n\r\n.uploader-queue .progress-empty {\r\n width: 150px;\r\n height: 9px;\r\n border: 1px solid black;\r\n background: url(''@@base_url@@/core/admin_templates/img/progress_left.gif'') repeat-x;\r\n}\r\n\r\n.uploader-queue .progress-full {\r\n height: 9px;\r\n background: url(''@@base_url@@/core/admin_templates/img/progress_done.gif'');\r\n}\r\n\r\n.uploader-queue .thumbnail {\r\n /*margin-bottom: 2px;*/\r\n border: 1px solid black;\r\n background-color: grey;\r\n}\r\n\r\n/* To be sorted */\r\nspan#category_path, span#category_path a {\r\n color: #FFFFFF;\r\n}\r\n\r\nspan#category_path a {\r\n text-decoration: underline;\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/* Page header (bluebar) */\r\n.page-title 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/* 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;\r\n}\r\n\r\n.tree tr td a:hover, .tree tr td a.debug-only-item:hover {\r\n color: @@TreeHoverColor@@;\r\n}\r\n\r\n.tree tr.highlighted td a, .tree tr.highlighted td a.debug-only-item {\r\n color: @@TreeHighColor@@;\r\n background-color: @@TreeHighBgColor@@;\r\n}\r\n\r\n.tree tr.highlighted td a:hover {\r\n color: @@TreeHighHoverColor@@;\r\n}\r\n\r\n.tree tr td a.debug-only-item {\r\n color: grey;\r\n}\r\n\r\n/* Ajax Dropdown */\r\n.suggest-box {\r\n border: 1px solid #999;\r\n background-color: #fff;\r\n}\r\n\r\n.suggest-item, .suggest-item-over {\r\n padding: 1px 2px 0px 2px;\r\n font-family: arial,verdana;\r\n font-size: 12px;\r\n}\r\n\r\n.suggest-item-over {\r\n background-color: #3366CC;\r\n color: #fff;\r\n}\r\n\r\n/* Dashboard Summary Boxes */\r\n.summary-box {\r\n border: 1px solid black;\r\n margin-bottom: 4px;\r\n}\r\n\r\n.summary-box .title {\r\n color: white;\r\n font-weight: bold;\r\n padding: 6px 5px;\r\n vertical-align: middle;\r\n background-color: #4A92CE;\r\n border-bottom: 1px solid black;\r\n}\r\n\r\n.summary-box .content {\r\n padding: 4px;\r\n background-color: #F6F6F6;\r\n}\r\n\r\n.summary-box .group {\r\n border-bottom: 1px solid black;\r\n margin-bottom: 10px;\r\n padding: 0 0 10px 10px;\r\n}\r\n\r\n.summary-box .group.last {\r\n border-width: 0px;\r\n margin-bottom: 0;\r\n padding-bottom: 5px;\r\n}\r\n\r\n.summary-box h4 {\r\n margin: 0;\r\n padding: 0 0 3px 0;\r\n font-size: 11px;\r\n font-weight: bold;\r\n}\r\n\r\n.summary-box .hint {\r\n font-size: 10px;\r\n color: grey;\r\n margin-bottom: 3px;\r\n}\r\n\r\n.summary-box .hint .cache-key {\r\n margin-bottom: 7px;\r\n margin-left: 3px;\r\n}\r\n\r\n.summary-box ul {\r\n margin-top: 5px;\r\n margin-bottom: 3px;\r\n padding-left: 30px;\r\n}\r\n\r\n.summary-box li {\r\n padding-bottom: 4px;\r\n}\r\n\r\nspan.cke_skin_kama {\r\n border-width: 0px !important;\r\n -moz-border-radius: 0px !important;\r\n -webkit-border-radius: 0px !important;\r\n padding: 0px !important;\r\n}\r\n\r\n.cke_wrapper{\r\n border-width: 0px !important;\r\n -moz-border-radius: 0px !important;\r\n -webkit-border-radius: 0px !important;\r\n}', 'in-portal_logo_img.jpg', 'in-portal_logo_img2.jpg', 'in-portal_logo_login.gif', 'a:22:{s:11:"HeadBgColor";a:2:{s:11:"Description";s:27:"Head frame background color";s:5:"Value";s:7:"#007BF4";}s:9:"HeadColor";a:2:{s:11:"Description";s:21:"Head frame text color";s:5:"Value";s:7:"#FFFFFF";}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:"#000000";}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:"#000000";}s:14:"TreeHoverColor";a:1:{s:5:"Value";s:7:"#009FF0";}s:13:"TreeHighColor";a:1:{s:5:"Value";s:7:"#FFFFFF";}s:18:"TreeHighHoverColor";a:1:{s:5:"Value";s:7:"#FFFFFF";}s:15:"TreeHighBgColor";a:1:{s:5:"Value";s:7:"#4A92CE";}s:11:"TreeBgColor";a:1:{s:5:"Value";s:7:"#DCECF6";}}', 1375200926, 1, 1); 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'); INSERT INTO SearchConfig VALUES ('Categories', 'NewItem', 0, 1, 'lu_fielddesc_category_newitem', 'lc_field_newitem', 'In-Portal', 'la_text_category', 18, DEFAULT, 0, 'boolean', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Categories', 'PopItem', 0, 1, 'lu_fielddesc_category_popitem', 'lc_field_popitem', 'In-Portal', 'la_text_category', 19, DEFAULT, 0, 'boolean', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Categories', 'HotItem', 0, 1, 'lu_fielddesc_category_hotitem', 'lc_field_hotitem', 'In-Portal', 'la_text_category', 17, DEFAULT, 0, 'boolean', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Categories', 'MetaDescription', 0, 1, 'lu_fielddesc_category_metadescription', 'lc_field_MetaDescription', 'In-Portal', 'la_text_category', 16, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Categories', 'ParentPath', 0, 1, 'lu_fielddesc_category_parentpath', 'lc_field_ParentPath', 'In-Portal', 'la_text_category', 15, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Categories', 'ResourceId', 0, 1, 'lu_fielddesc_category_resourceid', 'lc_field_resourceid', 'In-Portal', 'la_text_category', 14, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Categories', 'CreatedById', 0, 1, 'lu_fielddesc_category_createdbyid', 'lc_field_createdbyid', 'In-Portal', 'la_text_category', 13, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Categories', 'CachedNavbar', 0, 1, 'lu_fielddesc_category_cachednavbar', 'lc_field_CachedNavBar', 'In-Portal', 'la_text_category', 12, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Categories', 'CachedDescendantCatsQty', 0, 1, 'lu_fielddesc_category_cacheddescendantcatsqty', 'lc_field_CachedDescendantCatsQty', 'In-Portal', 'la_text_category', 11, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Categories', 'MetaKeywords', 0, 1, 'lu_fielddesc_category_metakeywords', 'lc_field_MetaKeywords', 'In-Portal', 'la_text_category', 10, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Categories', 'Priority', 0, 1, 'lu_fielddesc_category_priority', 'lc_field_priority', 'In-Portal', 'la_text_category', 9, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Categories', 'Status', 0, 1, 'lu_fielddesc_category_status', 'lc_field_status', 'In-Portal', 'la_text_category', 7, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Categories', 'EditorsPick', 0, 1, 'lu_fielddesc_category_editorspick', 'lc_field_EditorsPick', 'In-Portal', 'la_text_category', 6, DEFAULT, 0, 'boolean', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Categories', 'CreatedOn', 0, 1, 'lu_fielddesc_category_createdon', 'lc_field_createdon', 'In-Portal', 'la_text_category', 5, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Categories', 'Description', 1, 1, 'lu_fielddesc_category_description', 'lc_field_description', 'In-Portal', 'la_text_category', 4, DEFAULT, 2, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Categories', 'Name', 1, 1, 'lu_fielddesc_category_name', 'lc_field_name', 'In-Portal', 'la_text_category', 3, DEFAULT, 2, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Categories', 'ParentId', 0, 1, 'lu_fielddesc_category_parentid', 'lc_field_ParentId', 'In-Portal', 'la_text_category', 2, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Categories', 'CategoryId', 0, 1, 'lu_fielddesc_category_categoryid', 'lc_field_CategoryId', 'In-Portal', 'la_text_category', 0, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Categories', 'Modified', 0, 1, 'lu_fielddesc_category_modified', 'lc_field_modified', 'In-Portal', 'la_text_category', 20, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Categories', 'ModifiedById', 0, 1, 'lu_fielddesc_category_modifiedbyid', 'lc_field_modifiedbyid', 'In-Portal', 'la_text_category', 21, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Users', 'PortalUserId', -1, 0, 'lu_fielddesc_user_portaluserid', 'lu_field_portaluserid', 'In-Portal', 'la_text_user', 0, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Users', 'Username', -1, 0, 'lu_fielddesc_user_login', 'lu_field_login', 'In-Portal', 'la_text_user', 1, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Users', 'Password', -1, 0, 'lu_fielddesc_user_password', 'lu_field_password', 'In-Portal', 'la_text_user', 2, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Users', 'TimeZone', -1, 0, 'lu_fielddesc_user_tz', 'lu_field_tz', 'In-Portal', 'la_text_user', 17, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Users', 'dob', -1, 0, 'lu_fielddesc_user_dob', 'lu_field_dob', 'In-Portal', 'la_text_user', 16, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Users', 'Modified', -1, 0, 'lu_fielddesc_user_modified', 'lc_field_modified', 'In-Portal', 'la_text_user', 15, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Users', 'Status', -1, 0, 'lu_fielddesc_user_status', 'lc_field_status', 'In-Portal', 'la_text_user', 14, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Users', 'ResourceId', -1, 0, 'lu_fielddesc_user_resourceid', 'lc_field_resourceid', 'In-Portal', 'la_text_user', 13, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Users', 'Country', -1, 0, 'lu_fielddesc_user_country', 'lu_field_country', 'In-Portal', 'la_text_user', 12, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Users', 'Zip', -1, 0, 'lu_fielddesc_user_zip', 'lu_field_zip', 'In-Portal', 'la_text_user', 11, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Users', 'State', -1, 0, 'lu_fielddesc_user_state', 'lu_field_state', 'In-Portal', 'la_text_user', 10, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Users', 'City', -1, 0, 'lu_fielddesc_user_city', 'lu_field_city', 'In-Portal', 'la_text_user', 9, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Users', 'Street', -1, 0, 'lu_fielddesc_user_street', 'lu_field_street', 'In-Portal', 'la_text_user', 8, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Users', 'Phone', -1, 0, 'lu_fielddesc_user_phone', 'lu_field_phone', 'In-Portal', 'la_text_user', 7, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Users', 'CreatedOn', -1, 0, 'lu_fielddesc_user_createdon', 'lc_field_createdon', 'In-Portal', 'la_text_user', 6, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Users', 'Email', -1, 0, 'lu_fielddesc_user_email', 'lu_field_email', 'In-Portal', 'la_text_user', 5, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Users', 'LastName', -1, 0, 'lu_fielddesc_user_lastname', 'lu_field_lastname', 'In-Portal', 'la_text_user', 4, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO SearchConfig VALUES ('Users', 'FirstName', -1, 0, 'lu_fielddesc_user_firstname', 'lu_field_firstname', 'In-Portal', 'la_text_user', 3, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT count(*) FROM <%prefix%>Categories WHERE Status=1 ', NULL, 'la_prompt_ActiveCategories', '0', '1'); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT count(*) FROM <%prefix%>Users WHERE Status=1 ', NULL, 'la_prompt_ActiveUsers', '0', '1'); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT count(*) FROM <%prefix%>UserSessions', NULL, 'la_prompt_CurrentSessions', '0', '1'); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT COUNT(*) as CategoryCount FROM <%prefix%>Categories', NULL, 'la_prompt_TotalCategories', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS ActiveCategories FROM <%prefix%>Categories WHERE Status = 1', NULL, 'la_prompt_ActiveCategories', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS PendingCategories FROM <%prefix%>Categories WHERE Status = 2', NULL, 'la_prompt_PendingCategories', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS DisabledCategories FROM <%prefix%>Categories WHERE Status = 0', NULL, 'la_prompt_DisabledCategories', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS NewCategories FROM <%prefix%>Categories WHERE (NewItem = 1) OR ( (UNIX_TIMESTAMP() - CreatedOn) <= <%m:config name="Category_DaysNew"%>*86400 AND (NewItem = 2) )', NULL, 'la_prompt_NewCategories', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT COUNT(*) FROM <%prefix%>Categories WHERE EditorsPick = 1', NULL, 'la_prompt_CategoryEditorsPick', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT <%m:post_format field="MAX(CreatedOn)" type="date"%> FROM <%prefix%>Categories', NULL, 'la_prompt_NewestCategoryDate', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT <%m:post_format field="MAX(Modified)" type="date"%> FROM <%prefix%>Categories', NULL, 'la_prompt_LastCategoryUpdate', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS TotalUsers FROM <%prefix%>Users', NULL, 'la_prompt_TopicsUsers', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS ActiveUsers FROM <%prefix%>Users WHERE Status = 1', NULL, 'la_prompt_UsersActive', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS PendingUsers FROM <%prefix%>Users WHERE Status = 2', NULL, 'la_prompt_UsersPending', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS DisabledUsers FROM <%prefix%>Users WHERE Status = 0', NULL, 'la_prompt_UsersDisabled', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT <%m:post_format field="MAX(CreatedOn)" type="date"%> FROM <%prefix%>Users', NULL, 'la_prompt_NewestUserDate', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT COUNT( DISTINCT LOWER( Country ) ) FROM <%prefix%>Users WHERE LENGTH(Country) > 0', NULL, 'la_prompt_UsersUniqueCountries', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT COUNT( DISTINCT LOWER( State ) ) FROM <%prefix%>Users WHERE LENGTH(State) > 0', NULL, 'la_prompt_UsersUniqueStates', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS TotalUserGroups FROM <%prefix%>UserGroups', NULL, 'la_prompt_TotalUserGroups', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS BannedUsers FROM <%prefix%>Users WHERE IsBanned = 1', NULL, 'la_prompt_BannedUsers', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS NonExipedSessions FROM <%prefix%>UserSessions WHERE Status = 1', NULL, 'la_prompt_NonExpiredSessions', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS ThemeCount FROM <%prefix%>Themes', NULL, 'la_prompt_ThemeCount', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS RegionsCount FROM <%prefix%>Languages', NULL, 'la_prompt_RegionsCount', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', '<%m:sql_action sql="SHOW+TABLES" action="COUNT" field="*"%>', NULL, 'la_prompt_TablesCount', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', '<%m:sql_action sql="SHOW+TABLE+STATUS" action="SUM" field="Rows"%>', NULL, 'la_prompt_RecordsCount', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', '<%m:custom_action sql="empty" action="SysFileSize"%>', NULL, 'la_prompt_SystemFileSize', 0, 2); INSERT INTO StatItem VALUES (DEFAULT, 'In-Portal', '<%m:sql_action sql="SHOW+TABLE+STATUS" action="SUM" format_as="file" field="Data_length"%>', NULL, 'la_prompt_DataSize', 0, 2); INSERT INTO Counters VALUES (DEFAULT, 'members_count', 'SELECT COUNT(*) FROM <%PREFIX%>Users WHERE Status = 1', NULL , NULL , '3600', '0', '|Users|'); INSERT INTO Counters VALUES (DEFAULT, 'members_online', 'SELECT COUNT(*) FROM <%PREFIX%>UserSessions WHERE PortalUserId > 0', NULL , NULL , '3600', '0', '|UserSessions|'); INSERT INTO Counters VALUES (DEFAULT, 'guests_online', 'SELECT COUNT(*) FROM <%PREFIX%>UserSessions WHERE PortalUserId <= 0', NULL , NULL , '3600', '0', '|UserSessions|'); INSERT INTO Counters VALUES (DEFAULT, 'users_online', 'SELECT COUNT(*) FROM <%PREFIX%>UserSessions', NULL , NULL , '3600', '0', '|UserSessions|'); 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 PageContent VALUES (DEFAULT, 1, 1, '<span style="font-weight: bold;">In-portal</span> is a revolutionary Web Site management system that allows you to automate and facilitate management of large portal and community web sites. Regardless of whether you are running a directory site or a content news portal, a community site or an online mall, In-portal will enhance your web site management experience with innovative.</span><br><br>We are proud to present our newly developed <b>"default"</b> theme that introduces a fresh look as well totally new approach in the template system.</span><br>', NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0); INSERT INTO PromoBlockGroups VALUES (DEFAULT, 'Default Group', UNIX_TIMESTAMP(), '1', '7.00', '0.60', '1', 'fade', ''); INSERT INTO Modules VALUES ('Core', 'core/', 'InPortal\\Core', 'adm', DEFAULT, 1, 1, '', 0, NULL); INSERT INTO Modules VALUES ('In-Portal', 'core/', 'InPortal\\Core', 'm', DEFAULT, 1, 0, '', 0, NULL); Index: branches/5.3.x/core/install/upgrades.php =================================================================== --- branches/5.3.x/core/install/upgrades.php (revision 16394) +++ branches/5.3.x/core/install/upgrades.php (revision 16395) @@ -1,2501 +1,2545 @@ <?php /** * @version $Id$ * @package In-Portal * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license GNU/GPL * In-Portal is Open Source software. * This means that this software may have been modified pursuant * the GNU General Public License, and as distributed it includes * or is derivative of works licensed under the GNU General Public License * or other free or open source software licenses. * See http://www.in-portal.org/license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); $upgrade_class = 'CoreUpgrades'; /** * Class, that holds all upgrade scripts for "Core" module * */ class CoreUpgrades extends kUpgradeHelper { /** * Tables, that were renamed during 5.2.0 version upgrade * * @var Array * @access private */ private $renamedTables = Array ( 'ban-rule' => Array ('from' => 'BanRules', 'to' => 'UserBanRules'), 'conf' => Array ('from' => 'ConfigurationValues', 'to' => 'SystemSettings'), 'c' => Array ('from' => 'Category', 'to' => 'Categories'), 'cf' => Array ('from' => 'CustomField', 'to' => 'CustomFields'), 'draft' => Array ('from' => 'Drafts', 'to' => 'FormSubmissionReplyDrafts'), 'email-template' => Array ('from' => 'Events', 'to' => 'EmailEvents'), 'fav' => Array ('from' => 'Favorites', 'to' => 'UserFavorites'), 'img' => Array ('from' => 'Images', 'to' => 'CatalogImages'), '#file' => Array ('from' => 'ItemFiles', 'to' => 'CatalogFiles'), 'rev' => Array ('from' => 'ItemReview', 'to' => 'CatalogReviews'), 'lang' => Array ('from' => 'Language', 'to' => 'Languages'), 'permission-type' => Array ('from' => 'PermissionConfig', 'to' => 'CategoryPermissionsConfig'), 'phrases' => Array ('from' => 'Phrase', 'to' => 'LanguageLabels'), 'g' => Array ('from' => 'PortalGroup', 'to' => 'UserGroups'), 'user-profile' => Array ('from' => 'PersistantSessionData', 'to' => 'UserPersistentSessionData'), 'u' => Array ('from' => 'PortalUser', 'to' => 'Users'), 'u-cdata' => Array ('from' => 'PortalUserCustomData', 'to' => 'UserCustomData'), 'search' => Array ('from' => 'RelatedSearches', 'to' => 'CategoryRelatedSearches'), 'rel' => Array ('from' => 'Relationship', 'to' => 'CatalogRelationships'), 'search-log' => Array ('from' => 'SearchLog', 'to' => 'SearchLogs'), 'skin' => Array ('from' => 'Skins', 'to' => 'AdminSkins'), 'submission-log' => Array ('from' => 'SubmissionLog', 'to' => 'FormSubmissionReplies'), 'theme' => Array ('from' => 'Theme', 'to' => 'Themes'), 'ug' => Array ('from' => 'UserGroup', 'to' => 'UserGroupRelations'), 'visits' => Array ('from' => 'Visits', 'to' => 'UserVisits'), 'session-log' => Array ('from' => 'SessionLogs', 'to' => 'UserSessionLogs'), ); /** * Changes table structure, where multilingual fields of TEXT type are present * * @param string $mode when called mode {before, after) */ function Upgrade_4_1_0($mode) { if ($mode == 'before') { // don't user after, because In-Portal calls this method too $this->_toolkit->SaveConfig(); } if ($mode == 'after') { $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ $languages = $ml_helper->getLanguages(); $this->Application->UnitConfigReader->ReReadConfigs(); foreach ($this->Application->UnitConfigReader->getPrefixes() as $prefix) { $this->updateTextFields($prefix, $languages); } } } /** * Moves ReplacementTags functionality from EmailMessage to Events table * * @param string $mode when called mode {before, after) */ function Upgrade_4_1_1($mode) { if ($mode == 'after') { $sql = 'SELECT ReplacementTags, EventId FROM '.TABLE_PREFIX.'EmailMessage WHERE (ReplacementTags IS NOT NULL) AND (ReplacementTags <> "") AND (LanguageId = 1)'; $replacement_tags = $this->Conn->GetCol($sql, 'EventId'); foreach ($replacement_tags as $event_id => $replacement_tag) { $sql = 'UPDATE '.TABLE_PREFIX.'Events SET ReplacementTags = '.$this->Conn->qstr($replacement_tag).' WHERE EventId = '.$event_id; $this->Conn->Query($sql); } // drop moved field from source table $sql = 'ALTER TABLE '.TABLE_PREFIX.'EmailMessage DROP `ReplacementTags`'; $this->Conn->Query($sql); } } /** * Callback function, that makes all ml fields of text type null with same default value * * @param string $prefix * @param Array $languages * * @return boolean */ function updateTextFields($prefix, $languages) { $config = $this->Application->getUnitConfig($prefix); if ( $config->getTableName() === false || $config->getFields() === false ) { // invalid config found or prefix not found return false; } $table_name = $config->getTableName(); $table_structure = $this->Conn->Query('DESCRIBE ' . $table_name, 'Field'); if ( !$table_structure ) { // table not found return false; } $sqls = Array(); foreach ( $config->getFields() as $field => $options ) { if ( isset($options['formatter']) && $options['formatter'] == 'kMultiLanguage' && !isset($options['master_field']) ) { // update all l<lang_id>_<field_name> fields (new format) foreach ( $languages as $language_id ) { $ml_field = 'l' . $language_id . '_' . $field; if ( $table_structure[$ml_field]['Type'] == 'text' ) { $sqls[] = 'CHANGE ' . $ml_field . ' ' . $ml_field . ' TEXT NULL DEFAULT NULL'; } } // update <field_name> if found (old format) if ( isset($table_structure[$field]) && $table_structure[$field]['Type'] == 'text' ) { $sqls[] = 'CHANGE ' . $field . ' ' . $field . ' TEXT NULL DEFAULT NULL'; } } } if ( $sqls ) { $sql = 'ALTER TABLE ' . $table_name . ' ' . implode(', ', $sqls); $this->Conn->Query($sql); } return true; } /** * Replaces In-Portal tags in Forgot Password related email events to K4 ones * * @param string $mode when called mode {before, after) */ function Upgrade_4_2_0($mode) { if ($mode == 'after') { // 1. get event ids based on their name and type combination $event_names = Array ( 'USER.PSWD_' . EmailTemplate::TEMPLATE_TYPE_ADMIN, 'USER.PSWD_' . EmailTemplate::TEMPLATE_TYPE_FRONTEND, 'USER.PSWDC_' . EmailTemplate::TEMPLATE_TYPE_FRONTEND, ); $event_sql = Array (); foreach ($event_names as $mixed_event) { list ($event_name, $event_type) = explode('_', $mixed_event, 2); $event_sql[] = 'Event = "'.$event_name.'" AND Type = '.$event_type; } $sql = 'SELECT EventId FROM '.TABLE_PREFIX.'Events WHERE ('.implode(') OR (', $event_sql).')'; $event_ids = implode(',', $this->Conn->GetCol($sql)); // 2. replace In-Portal tags to K4 tags $replacements = Array ( '<inp:touser _Field="Password" />' => '<inp2:u_ForgottenPassword />', '<inp:m_confirm_password_link />' => '<inp2:u_ConfirmPasswordLink no_amp="1"/>', ); foreach ($replacements as $old_tag => $new_tag) { $sql = 'UPDATE '.TABLE_PREFIX.'EmailMessage SET Template = REPLACE(Template, '.$this->Conn->qstr($old_tag).', '.$this->Conn->qstr($new_tag).') WHERE EventId IN ('.$event_ids.')'; $this->Conn->Query($sql); } } } /** * Makes admin primary language same as front-end - not needed, done in SQL * * @param string $mode when called mode {before, after) */ function Upgrade_4_2_1($mode) { } function Upgrade_4_2_2($mode) { if ( $mode == 'before' ) { $sql = 'SELECT LanguageId FROM ' . TABLE_PREFIX . 'Language WHERE PrimaryLang = 1'; if ( $this->Conn->GetOne($sql) ) { return; } $this->Conn->Query('UPDATE ' . TABLE_PREFIX . 'Language SET PrimaryLang = 1 ORDER BY LanguageId LIMIT 1'); } } /** * Adds index to "dob" field in "PortalUser" table when it's missing * * @param string $mode when called mode {before, after) */ function Upgrade_4_3_1($mode) { if ($mode == 'after') { $sql = 'DESCRIBE ' . TABLE_PREFIX . 'PortalUser'; $structure = $this->Conn->Query($sql); foreach ($structure as $field_info) { if ($field_info['Field'] == 'dob') { if (!$field_info['Key']) { $sql = 'ALTER TABLE ' . TABLE_PREFIX . 'PortalUser ADD INDEX (dob)'; $this->Conn->Query($sql); } break; } } } } /** * Removes duplicate phrases, update file paths in database * * @param string $mode when called mode {before, after) */ function Upgrade_4_3_9($mode) { // 1. find In-Portal old <inp: tags $sql = 'SELECT EmailMessageId FROM '.TABLE_PREFIX.'EmailMessage WHERE Template LIKE \'%<inp:%\''; $event_ids = implode(',', $this->Conn->GetCol($sql)); // 2. replace In-Portal old <inp: tags to K4 tags $replacements = Array ( '<inp:m_category_field _Field="Name" _StripHTML="1"' => '<inp2:c_Field name="Name"', '<inp:touser _Field="password"' => '<inp2:u_Field name="Password_plain"', '<inp:touser _Field="UserName"' => '<inp2:u_Field name="Login"', '<inp:touser _Field="' => '<inp2:u_Field name="', '<inp:m_page_title' => '<inp2:m_BaseUrl', '<inp:m_theme_url _page="current"' => '<inp2:m_BaseUrl', '<inp:topic _field="text"' => '<inp2:bb-post_Field name="PostingText"', '<inp:topic _field="link" _Template="inbulletin/post_list"' => '<inp2:bb_TopicLink template="__default__"', ); if ($event_ids) { foreach ($replacements as $old_tag => $new_tag) { $sql = 'UPDATE '.TABLE_PREFIX.'EmailMessage SET Template = REPLACE(Template, '.$this->Conn->qstr($old_tag).', '.$this->Conn->qstr($new_tag).') WHERE EventId IN ('.$event_ids.')'; $this->Conn->Query($sql); } } if ($mode == 'after') { $this->_insertInPortalData(); $this->_moveDatabaseFolders(); // in case, when In-Portal module is enabled -> turn AdvancedUserManagement on too if ($this->Application->findModule('Name', 'In-Portal')) { $sql = 'UPDATE ' . TABLE_PREFIX . 'ConfigurationValues SET VariableValue = 1 WHERE VariableName = "AdvancedUserManagement"'; $this->Conn->Query($sql); } } if ($mode == 'languagepack') { $this->_removeDuplicatePhrases(); } } function _insertInPortalData() { $data = Array ( 'ConfigurationAdmin' => Array ( 'UniqueField' => 'VariableName', 'Records' => Array ( 'AllowDeleteRootCats' => "('AllowDeleteRootCats', 'la_Text_General', 'la_AllowDeleteRootCats', 'checkbox', NULL , NULL , 10.09, 0, 0)", 'Catalog_PreselectModuleTab' => "('Catalog_PreselectModuleTab', 'la_Text_General', 'la_config_CatalogPreselectModuleTab', 'checkbox', NULL, NULL, 10.10, 0, 1)", 'RecycleBinFolder' => "('RecycleBinFolder', 'la_Text_General', 'la_config_RecycleBinFolder', 'text', NULL , NULL , 10.11, 0, 0)", 'AdvancedUserManagement' => "('AdvancedUserManagement', 'la_Text_General', 'la_prompt_AdvancedUserManagement', 'checkbox', NULL, NULL, '10.011', 0, 1)", ), ), 'ConfigurationValues' => Array ( 'UniqueField' => 'VariableName', 'Records' => Array ( 'AdvancedUserManagement' => "(DEFAULT, 'AdvancedUserManagement', 0, 'In-Portal:Users', 'in-portal:configure_users')", ), ), 'ItemTypes' => Array ( 'UniqueField' => 'ItemType', 'Records' => Array ( '1' => "(1, 'In-Portal', 'c', 'Category', 'Name', 'CreatedById', NULL, NULL, 'la_ItemTab_Categories', 1, 'admin/category/addcategory.php', 'clsCategory', 'Category')", '6' => "(6, 'In-Portal', 'u', 'PortalUser', 'Login', 'PortalUserId', NULL, NULL, '', 0, '', 'clsPortalUser', 'User')", ), ), 'PermissionConfig' => Array ( 'UniqueField' => 'PermissionName', 'Records' => Array ( 'CATEGORY.ADD' => "(DEFAULT, 'CATEGORY.ADD', 'lu_PermName_Category.Add_desc', 'lu_PermName_Category.Add_error', 'In-Portal')", 'CATEGORY.DELETE' => "(DEFAULT, 'CATEGORY.DELETE', 'lu_PermName_Category.Delete_desc', 'lu_PermName_Category.Delete_error', 'In-Portal')", 'CATEGORY.ADD.PENDING' => "(DEFAULT, 'CATEGORY.ADD.PENDING', 'lu_PermName_Category.AddPending_desc', 'lu_PermName_Category.AddPending_error', 'In-Portal')", 'CATEGORY.MODIFY' => "(DEFAULT, 'CATEGORY.MODIFY', 'lu_PermName_Category.Modify_desc', 'lu_PermName_Category.Modify_error', 'In-Portal')", 'ADMIN' => "(DEFAULT, 'ADMIN', 'lu_PermName_Admin_desc', 'lu_PermName_Admin_error', 'Admin')", 'LOGIN' => "(DEFAULT, 'LOGIN', 'lu_PermName_Login_desc', 'lu_PermName_Admin_error', 'Front')", 'DEBUG.ITEM' => "(DEFAULT, 'DEBUG.ITEM', 'lu_PermName_Debug.Item_desc', '', 'Admin')", 'DEBUG.LIST' => "(DEFAULT, 'DEBUG.LIST', 'lu_PermName_Debug.List_desc', '', 'Admin')", 'DEBUG.INFO' => "(DEFAULT, 'DEBUG.INFO', 'lu_PermName_Debug.Info_desc', '', 'Admin')", 'PROFILE.MODIFY' => "(DEFAULT, 'PROFILE.MODIFY', 'lu_PermName_Profile.Modify_desc', '', 'Admin')", 'SHOWLANG' => "(DEFAULT, 'SHOWLANG', 'lu_PermName_ShowLang_desc', '', 'Admin')", 'FAVORITES' => "(DEFAULT, 'FAVORITES', 'lu_PermName_favorites_desc', 'lu_PermName_favorites_error', 'In-Portal')", 'SYSTEM_ACCESS.READONLY' => "(DEFAULT, 'SYSTEM_ACCESS.READONLY', 'la_PermName_SystemAccess.ReadOnly_desc', 'la_PermName_SystemAccess.ReadOnly_error', 'Admin')", ), ), 'Permissions' => Array ( 'UniqueField' => 'Permission;GroupId;Type;CatId', 'Records' => Array ( 'LOGIN;12;1;0' => "(DEFAULT, 'LOGIN', 12, 1, 1, 0)", 'in-portal:site.view;11;1;0' => "(DEFAULT, 'in-portal:site.view', 11, 1, 1, 0)", 'in-portal:browse.view;11;1;0' => "(DEFAULT, 'in-portal:browse.view', 11, 1, 1, 0)", 'in-portal:advanced_view.view;11;1;0' => "(DEFAULT, 'in-portal:advanced_view.view', 11, 1, 1, 0)", 'in-portal:reviews.view;11;1;0' => "(DEFAULT, 'in-portal:reviews.view', 11, 1, 1, 0)", 'in-portal:configure_categories.view;11;1;0' => "(DEFAULT, 'in-portal:configure_categories.view', 11, 1, 1, 0)", 'in-portal:configure_categories.edit;11;1;0' => "(DEFAULT, 'in-portal:configure_categories.edit', 11, 1, 1, 0)", 'in-portal:configuration_search.view;11;1;0' => "(DEFAULT, 'in-portal:configuration_search.view', 11, 1, 1, 0)", 'in-portal:configuration_search.edit;11;1;0' => "(DEFAULT, 'in-portal:configuration_search.edit', 11, 1, 1, 0)", 'in-portal:configuration_email.view;11;1;0' => "(DEFAULT, 'in-portal:configuration_email.view', 11, 1, 1, 0)", 'in-portal:configuration_email.edit;11;1;0' => "(DEFAULT, 'in-portal:configuration_email.edit', 11, 1, 1, 0)", 'in-portal:configuration_custom.view;11;1;0' => "(DEFAULT, 'in-portal:configuration_custom.view', 11, 1, 1, 0)", 'in-portal:configuration_custom.add;11;1;0' => "(DEFAULT, 'in-portal:configuration_custom.add', 11, 1, 1, 0)", 'in-portal:configuration_custom.edit;11;1;0' => "(DEFAULT, 'in-portal:configuration_custom.edit', 11, 1, 1, 0)", 'in-portal:configuration_custom.delete;11;1;0' => "(DEFAULT, 'in-portal:configuration_custom.delete', 11, 1, 1, 0)", 'in-portal:users.view;11;1;0' => "(DEFAULT, 'in-portal:users.view', 11, 1, 1, 0)", 'in-portal:user_list.advanced:ban;11;1;0' => "(DEFAULT, 'in-portal:user_list.advanced:ban', 11, 1, 1, 0)", 'in-portal:user_list.advanced:send_email;11;1;0' => "(DEFAULT, 'in-portal:user_list.advanced:send_email', 11, 1, 1, 0)", 'in-portal:user_groups.view;11;1;0' => "(DEFAULT, 'in-portal:user_groups.view', 11, 1, 1, 0)", 'in-portal:user_groups.add;11;1;0' => "(DEFAULT, 'in-portal:user_groups.add', 11, 1, 1, 0)", 'in-portal:user_groups.edit;11;1;0' => "(DEFAULT, 'in-portal:user_groups.edit', 11, 1, 1, 0)", 'in-portal:user_groups.delete;11;1;0' => "(DEFAULT, 'in-portal:user_groups.delete', 11, 1, 1, 0)", 'in-portal:user_groups.advanced:send_email;11;1;0' => "(DEFAULT, 'in-portal:user_groups.advanced:send_email', 11, 1, 1, 0)", 'in-portal:user_groups.advanced:manage_permissions;11;1;0' => "(DEFAULT, 'in-portal:user_groups.advanced:manage_permissions', 11, 1, 1, 0)", 'in-portal:configure_users.view;11;1;0' => "(DEFAULT, 'in-portal:configure_users.view', 11, 1, 1, 0)", 'in-portal:configure_users.edit;11;1;0' => "(DEFAULT, 'in-portal:configure_users.edit', 11, 1, 1, 0)", 'in-portal:user_email.view;11;1;0' => "(DEFAULT, 'in-portal:user_email.view', 11, 1, 1, 0)", 'in-portal:user_email.edit;11;1;0' => "(DEFAULT, 'in-portal:user_email.edit', 11, 1, 1, 0)", 'in-portal:user_custom.view;11;1;0' => "(DEFAULT, 'in-portal:user_custom.view', 11, 1, 1, 0)", 'in-portal:user_custom.add;11;1;0' => "(DEFAULT, 'in-portal:user_custom.add', 11, 1, 1, 0)", 'in-portal:user_custom.edit;11;1;0' => "(DEFAULT, 'in-portal:user_custom.edit', 11, 1, 1, 0)", 'in-portal:user_custom.delete;11;1;0' => "(DEFAULT, 'in-portal:user_custom.delete', 11, 1, 1, 0)", 'in-portal:user_banlist.view;11;1;0' => "(DEFAULT, 'in-portal:user_banlist.view', 11, 1, 1, 0)", 'in-portal:user_banlist.add;11;1;0' => "(DEFAULT, 'in-portal:user_banlist.add', 11, 1, 1, 0)", 'in-portal:user_banlist.edit;11;1;0' => "(DEFAULT, 'in-portal:user_banlist.edit', 11, 1, 1, 0)", 'in-portal:user_banlist.delete;11;1;0' => "(DEFAULT, 'in-portal:user_banlist.delete', 11, 1, 1, 0)", 'in-portal:reports.view;11;1;0' => "(DEFAULT, 'in-portal:reports.view', 11, 1, 1, 0)", 'in-portal:log_summary.view;11;1;0' => "(DEFAULT, 'in-portal:log_summary.view', 11, 1, 1, 0)", 'in-portal:searchlog.view;11;1;0' => "(DEFAULT, 'in-portal:searchlog.view', 11, 1, 1, 0)", 'in-portal:searchlog.delete;11;1;0' => "(DEFAULT, 'in-portal:searchlog.delete', 11, 1, 1, 0)", 'in-portal:sessionlog.view;11;1;0' => "(DEFAULT, 'in-portal:sessionlog.view', 11, 1, 1, 0)", 'in-portal:sessionlog.delete;11;1;0' => "(DEFAULT, 'in-portal:sessionlog.delete', 11, 1, 1, 0)", 'in-portal:emaillog.view;11;1;0' => "(DEFAULT, 'in-portal:emaillog.view', 11, 1, 1, 0)", 'in-portal:emaillog.delete;11;1;0' => "(DEFAULT, 'in-portal:emaillog.delete', 11, 1, 1, 0)", 'in-portal:visits.view;11;1;0' => "(DEFAULT, 'in-portal:visits.view', 11, 1, 1, 0)", 'in-portal:visits.delete;11;1;0' => "(DEFAULT, 'in-portal:visits.delete', 11, 1, 1, 0)", 'in-portal:configure_general.view;11;1;0' => "(DEFAULT, 'in-portal:configure_general.view', 11, 1, 1, 0)", 'in-portal:configure_general.edit;11;1;0' => "(DEFAULT, 'in-portal:configure_general.edit', 11, 1, 1, 0)", 'in-portal:modules.view;11;1;0' => "(DEFAULT, 'in-portal:modules.view', 11, 1, 1, 0)", 'in-portal:mod_status.view;11;1;0' => "(DEFAULT, 'in-portal:mod_status.view', 11, 1, 1, 0)", 'in-portal:mod_status.edit;11;1;0' => "(DEFAULT, 'in-portal:mod_status.edit', 11, 1, 1, 0)", 'in-portal:mod_status.advanced:approve;11;1;0' => "(DEFAULT, 'in-portal:mod_status.advanced:approve', 11, 1, 1, 0)", 'in-portal:mod_status.advanced:decline;11;1;0' => "(DEFAULT, 'in-portal:mod_status.advanced:decline', 11, 1, 1, 0)", 'in-portal:addmodule.view;11;1;0' => "(DEFAULT, 'in-portal:addmodule.view', 11, 1, 1, 0)", 'in-portal:addmodule.add;11;1;0' => "(DEFAULT, 'in-portal:addmodule.add', 11, 1, 1, 0)", 'in-portal:addmodule.edit;11;1;0' => "(DEFAULT, 'in-portal:addmodule.edit', 11, 1, 1, 0)", 'in-portal:tag_library.view;11;1;0' => "(DEFAULT, 'in-portal:tag_library.view', 11, 1, 1, 0)", 'in-portal:configure_themes.view;11;1;0' => "(DEFAULT, 'in-portal:configure_themes.view', 11, 1, 1, 0)", 'in-portal:configure_themes.add;11;1;0' => "(DEFAULT, 'in-portal:configure_themes.add', 11, 1, 1, 0)", 'in-portal:configure_themes.edit;11;1;0' => "(DEFAULT, 'in-portal:configure_themes.edit', 11, 1, 1, 0)", 'in-portal:configure_themes.delete;11;1;0' => "(DEFAULT, 'in-portal:configure_themes.delete', 11, 1, 1, 0)", 'in-portal:configure_styles.view;11;1;0' => "(DEFAULT, 'in-portal:configure_styles.view', 11, 1, 1, 0)", 'in-portal:configure_styles.add;11;1;0' => "(DEFAULT, 'in-portal:configure_styles.add', 11, 1, 1, 0)", 'in-portal:configure_styles.edit;11;1;0' => "(DEFAULT, 'in-portal:configure_styles.edit', 11, 1, 1, 0)", 'in-portal:configure_styles.delete;11;1;0' => "(DEFAULT, 'in-portal:configure_styles.delete', 11, 1, 1, 0)", 'in-portal:configure_lang.advanced:set_primary;11;1;0' => "(DEFAULT, 'in-portal:configure_lang.advanced:set_primary', 11, 1, 1, 0)", 'in-portal:configure_lang.advanced:import;11;1;0' => "(DEFAULT, 'in-portal:configure_lang.advanced:import', 11, 1, 1, 0)", 'in-portal:configure_lang.advanced:export;11;1;0' => "(DEFAULT, 'in-portal:configure_lang.advanced:export', 11, 1, 1, 0)", 'in-portal:tools.view;11;1;0' => "(DEFAULT, 'in-portal:tools.view', 11, 1, 1, 0)", 'in-portal:backup.view;11;1;0' => "(DEFAULT, 'in-portal:backup.view', 11, 1, 1, 0)", 'in-portal:restore.view;11;1;0' => "(DEFAULT, 'in-portal:restore.view', 11, 1, 1, 0)", 'in-portal:export.view;11;1;0' => "(DEFAULT, 'in-portal:export.view', 11, 1, 1, 0)", 'in-portal:main_import.view;11;1;0' => "(DEFAULT, 'in-portal:main_import.view', 11, 1, 1, 0)", 'in-portal:sql_query.view;11;1;0' => "(DEFAULT, 'in-portal:sql_query.view', 11, 1, 1, 0)", 'in-portal:sql_query.edit;11;1;0' => "(DEFAULT, 'in-portal:sql_query.edit', 11, 1, 1, 0)", 'in-portal:server_info.view;11;1;0' => "(DEFAULT, 'in-portal:server_info.view', 11, 1, 1, 0)", 'in-portal:help.view;11;1;0' => "(DEFAULT, 'in-portal:help.view', 11, 1, 1, 0)", ), ), 'SearchConfig' => Array ( 'UniqueField' => 'TableName;FieldName;ModuleName', 'Records' => Array ( 'Category;NewItem;In-Portal' => "('Category', 'NewItem', 0, 1, 'lu_fielddesc_category_newitem', 'lu_field_newitem', 'In-Portal', 'la_text_category', 18, DEFAULT, 0, 'boolean', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'Category;PopItem;In-Portal' => "('Category', 'PopItem', 0, 1, 'lu_fielddesc_category_popitem', 'lu_field_popitem', 'In-Portal', 'la_text_category', 19, DEFAULT, 0, 'boolean', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'Category;HotItem;In-Portal' => "('Category', 'HotItem', 0, 1, 'lu_fielddesc_category_hotitem', 'lu_field_hotitem', 'In-Portal', 'la_text_category', 17, DEFAULT, 0, 'boolean', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'Category;MetaDescription;In-Portal' => "('Category', 'MetaDescription', 0, 1, 'lu_fielddesc_category_metadescription', 'lu_field_metadescription', 'In-Portal', 'la_text_category', 16, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'Category;ParentPath;In-Portal' => "('Category', 'ParentPath', 0, 1, 'lu_fielddesc_category_parentpath', 'lu_field_parentpath', 'In-Portal', 'la_text_category', 15, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'Category;ResourceId;In-Portal' => "('Category', 'ResourceId', 0, 1, 'lu_fielddesc_category_resourceid', 'lu_field_resourceid', 'In-Portal', 'la_text_category', 14, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'Category;CreatedById;In-Portal' => "('Category', 'CreatedById', 0, 1, 'lu_fielddesc_category_createdbyid', 'lu_field_createdbyid', 'In-Portal', 'la_text_category', 13, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'Category;CachedNavbar;In-Portal' => "('Category', 'CachedNavbar', 0, 1, 'lu_fielddesc_category_cachednavbar', 'lu_field_cachednavbar', 'In-Portal', 'la_text_category', 12, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'Category;CachedDescendantCatsQty;In-Portal' => "('Category', 'CachedDescendantCatsQty', 0, 1, 'lu_fielddesc_category_cacheddescendantcatsqty', 'lu_field_cacheddescendantcatsqty', 'In-Portal', 'la_text_category', 11, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'Category;MetaKeywords;In-Portal' => "('Category', 'MetaKeywords', 0, 1, 'lu_fielddesc_category_metakeywords', 'lu_field_metakeywords', 'In-Portal', 'la_text_category', 10, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'Category;Priority;In-Portal' => "('Category', 'Priority', 0, 1, 'lu_fielddesc_category_priority', 'lu_field_priority', 'In-Portal', 'la_text_category', 9, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'Category;Status;In-Portal' => "('Category', 'Status', 0, 1, 'lu_fielddesc_category_status', 'lu_field_status', 'In-Portal', 'la_text_category', 7, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'Category;EditorsPick;In-Portal' => "('Category', 'EditorsPick', 0, 1, 'lu_fielddesc_category_editorspick', 'lu_field_editorspick', 'In-Portal', 'la_text_category', 6, DEFAULT, 0, 'boolean', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'Category;CreatedOn;In-Portal' => "('Category', 'CreatedOn', 0, 1, 'lu_fielddesc_category_createdon', 'lu_field_createdon', 'In-Portal', 'la_text_category', 5, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'Category;Description;In-Portal' => "('Category', 'Description', 1, 1, 'lu_fielddesc_category_description', 'lu_field_description', 'In-Portal', 'la_text_category', 4, DEFAULT, 2, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'Category;Name;In-Portal' => "('Category', 'Name', 1, 1, 'lu_fielddesc_category_name', 'lu_field_name', 'In-Portal', 'la_text_category', 3, DEFAULT, 2, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'Category;ParentId;In-Portal' => "('Category', 'ParentId', 0, 1, 'lu_fielddesc_category_parentid', 'lu_field_parentid', 'In-Portal', 'la_text_category', 2, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'Category;CategoryId;In-Portal' => "('Category', 'CategoryId', 0, 1, 'lu_fielddesc_category_categoryid', 'lu_field_categoryid', 'In-Portal', 'la_text_category', 0, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'Category;Modified;In-Portal' => "('Category', 'Modified', 0, 1, 'lu_fielddesc_category_modified', 'lu_field_modified', 'In-Portal', 'la_text_category', 20, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'Category;ModifiedById;In-Portal' => "('Category', 'ModifiedById', 0, 1, 'lu_fielddesc_category_modifiedbyid', 'lu_field_modifiedbyid', 'In-Portal', 'la_text_category', 21, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'PortalUser;PortalUserId;In-Portal' => "('PortalUser', 'PortalUserId', -1, 0, 'lu_fielddesc_user_portaluserid', 'lu_field_portaluserid', 'In-Portal', 'la_text_user', 0, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'PortalUser;Login;In-Portal' => "('PortalUser', 'Login', -1, 0, 'lu_fielddesc_user_login', 'lu_field_login', 'In-Portal', 'la_text_user', 1, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'PortalUser;Password;In-Portal' => "('PortalUser', 'Password', -1, 0, 'lu_fielddesc_user_password', 'lu_field_password', 'In-Portal', 'la_text_user', 2, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'PortalUser;tz;In-Portal' => "('PortalUser', 'tz', -1, 0, 'lu_fielddesc_user_tz', 'lu_field_tz', 'In-Portal', 'la_text_user', 17, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'PortalUser;dob;In-Portal' => "('PortalUser', 'dob', -1, 0, 'lu_fielddesc_user_dob', 'lu_field_dob', 'In-Portal', 'la_text_user', 16, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'PortalUser;Modified;In-Portal' => "('PortalUser', 'Modified', -1, 0, 'lu_fielddesc_user_modified', 'lu_field_modified', 'In-Portal', 'la_text_user', 15, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'PortalUser;Status;In-Portal' => "('PortalUser', 'Status', -1, 0, 'lu_fielddesc_user_status', 'lu_field_status', 'In-Portal', 'la_text_user', 14, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'PortalUser;ResourceId;In-Portal' => "('PortalUser', 'ResourceId', -1, 0, 'lu_fielddesc_user_resourceid', 'lu_field_resourceid', 'In-Portal', 'la_text_user', 13, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'PortalUser;Country;In-Portal' => "('PortalUser', 'Country', -1, 0, 'lu_fielddesc_user_country', 'lu_field_country', 'In-Portal', 'la_text_user', 12, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'PortalUser;Zip;In-Portal' => "('PortalUser', 'Zip', -1, 0, 'lu_fielddesc_user_zip', 'lu_field_zip', 'In-Portal', 'la_text_user', 11, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'PortalUser;State;In-Portal' => "('PortalUser', 'State', -1, 0, 'lu_fielddesc_user_state', 'lu_field_state', 'In-Portal', 'la_text_user', 10, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'PortalUser;City;In-Portal' => "('PortalUser', 'City', -1, 0, 'lu_fielddesc_user_city', 'lu_field_city', 'In-Portal', 'la_text_user', 9, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'PortalUser;Street;In-Portal' => "('PortalUser', 'Street', -1, 0, 'lu_fielddesc_user_street', 'lu_field_street', 'In-Portal', 'la_text_user', 8, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'PortalUser;Phone;In-Portal' => "('PortalUser', 'Phone', -1, 0, 'lu_fielddesc_user_phone', 'lu_field_phone', 'In-Portal', 'la_text_user', 7, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'PortalUser;CreatedOn;In-Portal' => "('PortalUser', 'CreatedOn', -1, 0, 'lu_fielddesc_user_createdon', 'lu_field_createdon', 'In-Portal', 'la_text_user', 6, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'PortalUser;Email;In-Portal' => "('PortalUser', 'Email', -1, 0, 'lu_fielddesc_user_email', 'lu_field_email', 'In-Portal', 'la_text_user', 5, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'PortalUser;LastName;In-Portal' => "('PortalUser', 'LastName', -1, 0, 'lu_fielddesc_user_lastname', 'lu_field_lastname', 'In-Portal', 'la_text_user', 4, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", 'PortalUser;FirstName;In-Portal' => "('PortalUser', 'FirstName', -1, 0, 'lu_fielddesc_user_firstname', 'lu_field_firstname', 'In-Portal', 'la_text_user', 3, DEFAULT, 0, 'text', NULL, NULL, NULL, NULL, NULL, NULL, NULL)", ), ), 'StatItem' => Array ( 'UniqueField' => 'Module;ListLabel', 'Records' => Array ( 'In-Portal;la_prompt_ActiveCategories' => "(DEFAULT, 'In-Portal', 'SELECT count(*) FROM <%prefix%>Category WHERE Status=1 ', NULL, 'la_prompt_ActiveCategories', '0', '1')", 'In-Portal;la_prompt_ActiveUsers' => "(DEFAULT, 'In-Portal', 'SELECT count(*) FROM <%prefix%>PortalUser WHERE Status=1 ', NULL, 'la_prompt_ActiveUsers', '0', '1')", 'In-Portal;la_prompt_CurrentSessions' => "(DEFAULT, 'In-Portal', 'SELECT count(*) FROM <%prefix%>UserSession', NULL, 'la_prompt_CurrentSessions', '0', '1')", 'In-Portal;la_prompt_TotalCategories' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) as CategoryCount FROM <%prefix%>Category', NULL, 'la_prompt_TotalCategories', 0, 2)", 'In-Portal;la_prompt_ActiveCategories' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS ActiveCategories FROM <%prefix%>Category WHERE Status = 1', NULL, 'la_prompt_ActiveCategories', 0, 2)", 'In-Portal;la_prompt_PendingCategories' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS PendingCategories FROM <%prefix%>Category WHERE Status = 2', NULL, 'la_prompt_PendingCategories', 0, 2)", 'In-Portal;la_prompt_DisabledCategories' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS DisabledCategories FROM <%prefix%>Category WHERE Status = 0', NULL, 'la_prompt_DisabledCategories', 0, 2)", 'In-Portal;la_prompt_NewCategories' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS NewCategories FROM <%prefix%>Category WHERE (NewItem = 1) OR ( (UNIX_TIMESTAMP() - CreatedOn) <= <%m:config name=\"Category_DaysNew\"%>*86400 AND (NewItem = 2) )', NULL, 'la_prompt_NewCategories', 0, 2)", 'In-Portal;la_prompt_CategoryEditorsPick' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) FROM <%prefix%>Category WHERE EditorsPick = 1', NULL, 'la_prompt_CategoryEditorsPick', 0, 2)", 'In-Portal;la_prompt_NewestCategoryDate' => "(DEFAULT, 'In-Portal', 'SELECT <%m:post_format field=\"MAX(CreatedOn)\" type=\"date\"%> FROM <%prefix%>Category', NULL, 'la_prompt_NewestCategoryDate', 0, 2)", 'In-Portal;la_prompt_LastCategoryUpdate' => "(DEFAULT, 'In-Portal', 'SELECT <%m:post_format field=\"MAX(Modified)\" type=\"date\"%> FROM <%prefix%>Category', NULL, 'la_prompt_LastCategoryUpdate', 0, 2)", 'In-Portal;la_prompt_TopicsUsers' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS TotalUsers FROM <%prefix%>PortalUser', NULL, 'la_prompt_TopicsUsers', 0, 2)", 'In-Portal;la_prompt_UsersActive' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS ActiveUsers FROM <%prefix%>PortalUser WHERE Status = 1', NULL, 'la_prompt_UsersActive', 0, 2)", 'In-Portal;la_prompt_UsersPending' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS PendingUsers FROM <%prefix%>PortalUser WHERE Status = 2', NULL, 'la_prompt_UsersPending', 0, 2)", 'In-Portal;la_prompt_UsersDisabled' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS DisabledUsers FROM <%prefix%>PortalUser WHERE Status = 0', NULL, 'la_prompt_UsersDisabled', 0, 2)", 'In-Portal;la_prompt_NewestUserDate' => "(DEFAULT, 'In-Portal', 'SELECT <%m:post_format field=\"MAX(CreatedOn)\" type=\"date\"%> FROM <%prefix%>PortalUser', NULL, 'la_prompt_NewestUserDate', 0, 2)", 'In-Portal;la_prompt_UsersUniqueCountries' => "(DEFAULT, 'In-Portal', 'SELECT COUNT( DISTINCT LOWER( Country ) ) FROM <%prefix%>PortalUser WHERE LENGTH(Country) > 0', NULL, 'la_prompt_UsersUniqueCountries', 0, 2)", 'In-Portal;la_prompt_UsersUniqueStates' => "(DEFAULT, 'In-Portal', 'SELECT COUNT( DISTINCT LOWER( State ) ) FROM <%prefix%>PortalUser WHERE LENGTH(State) > 0', NULL, 'la_prompt_UsersUniqueStates', 0, 2)", 'In-Portal;la_prompt_TotalUserGroups' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS TotalUserGroups FROM <%prefix%>PortalGroup', NULL, 'la_prompt_TotalUserGroups', 0, 2)", 'In-Portal;la_prompt_BannedUsers' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS BannedUsers FROM <%prefix%>PortalUser WHERE IsBanned = 1', NULL, 'la_prompt_BannedUsers', 0, 2)", 'In-Portal;la_prompt_NonExpiredSessions' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS NonExipedSessions FROM <%prefix%>UserSession WHERE Status = 1', NULL, 'la_prompt_NonExpiredSessions', 0, 2)", 'In-Portal;la_prompt_ThemeCount' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS ThemeCount FROM <%prefix%>Theme', NULL, 'la_prompt_ThemeCount', 0, 2)", 'In-Portal;la_prompt_RegionsCount' => "(DEFAULT, 'In-Portal', 'SELECT COUNT(*) AS RegionsCount FROM <%prefix%>Language', NULL, 'la_prompt_RegionsCount', 0, 2)", 'In-Portal;la_prompt_TablesCount' => "(DEFAULT, 'In-Portal', '<%m:sql_action sql=\"SHOW+TABLES\" action=\"COUNT\" field=\"*\"%>', NULL, 'la_prompt_TablesCount', 0, 2)", 'In-Portal;la_prompt_RecordsCount' => "(DEFAULT, 'In-Portal', '<%m:sql_action sql=\"SHOW+TABLE+STATUS\" action=\"SUM\" field=\"Rows\"%>', NULL, 'la_prompt_RecordsCount', 0, 2)", 'In-Portal;la_prompt_SystemFileSize' => "(DEFAULT, 'In-Portal', '<%m:custom_action sql=\"empty\" action=\"SysFileSize\"%>', NULL, 'la_prompt_SystemFileSize', 0, 2)", 'In-Portal;la_prompt_DataSize' => "(DEFAULT, 'In-Portal', '<%m:sql_action sql=\"SHOW+TABLE+STATUS\" action=\"SUM\" format_as=\"file\" field=\"Data_length\"%>', NULL, 'la_prompt_DataSize', 0, 2)", ), ), 'StylesheetSelectors' => Array ( 'UniqueField' => 'SelectorId', 'Records' => Array ( '169' => "(169, 8, 'Calendar''s selected days', '.calendar tbody .selected', 'a:0:{}', '', 1, 'font-weight: bold;\\\r\\nbackground-color: #9ED7ED;\\r\\nborder: 1px solid #83B2C5;', 0)", '118' => "(118, 8, 'Data grid row', 'td.block-data-row', 'a:0:{}', '', 2, 'border-bottom: 1px solid #cccccc;', 48)", '81' => "(81, 8, '\"More\" link', 'a.link-more', 'a:0:{}', '', 2, 'text-decoration: underline;', 64)", '88' => "(88, 8, 'Block data, separated rows', 'td.block-data-grid', 'a:0:{}', '', 2, 'border: 1px solid #cccccc;', 48)", '42' => "(42, 8, 'Block Header', 'td.block-header', 'a:4:{s:5:\"color\";s:16:\"rgb(0, 159, 240)\";s:9:\"font-size\";s:4:\"20px\";s:11:\"font-weight\";s:6:\"normal\";s:7:\"padding\";s:3:\"5px\";}', 'Block Header', 1, 'font-family: Verdana, Helvetica, sans-serif;', 0)", '76' => "(76, 8, 'Navigation bar menu', 'tr.head-nav td', 'a:0:{}', '', 1, 'vertical-align: middle;', 0)", '48' => "(48, 8, 'Block data', 'td.block-data', 'a:2:{s:9:\"font-size\";s:5:\"12px;\";s:7:\"padding\";s:3:\"5px\";}', '', 1, '', 0)", '78' => "(78, 8, 'Body main style', 'body', 'a:0:{}', '', 1, 'padding: 0px; \\r\\nbackground-color: #ffffff; \\r\\nfont-family: arial, verdana, helvetica; \\r\\nfont-size: small;\\r\\nwidth: auto;\\r\\nmargin: 0px;', 0)", '58' => "(58, 8, 'Main table', 'table.main-table', 'a:0:{}', '', 1, 'width: 770px;\\r\\nmargin: 0px;\\r\\n/*table-layout: fixed;*/', 0)", '79' => "(79, 8, 'Block: header of data block', 'span.block-data-grid-header', 'a:0:{}', '', 1, 'font-family: Arial, Helvetica, sans-serif;\\r\\ncolor: #009DF6;\\r\\nfont-size: 12px;\\r\\nfont-weight: bold;\\r\\nbackground-color: #E6EEFF;\\r\\npadding: 6px;\\r\\nwhite-space: nowrap;', 0)", '64' => "(64, 8, 'Link', 'a', 'a:0:{}', '', 1, '', 0)", '46' => "(46, 8, 'Product title link', 'a.link-product1', 'a:0:{}', 'Product title link', 1, 'color: #62A1DE;\\r\\nfont-size: 14px;\\r\\nfont-weight: bold;\\r\\nline-height: 20px;\\r\\npadding-bottom: 10px;', 0)", '75' => "(75, 8, 'Copy of Main path link', 'table.main-path td a:hover', 'a:0:{}', '', 1, 'color: #ffffff;', 0)", '160' => "(160, 8, 'Current item in navigation bar', '.checkout-step-current', 'a:0:{}', '', 1, 'color: #A20303;\\r\\nfont-weight: bold;', 0)", '51' => "(51, 8, 'Right block data', 'td.right-block-data', 'a:1:{s:9:\"font-size\";s:4:\"11px\";}', '', 2, 'padding: 7px;\\r\\nbackground: #e3edf6 url(\"/in-commerce4/themes/default/img/bgr_login.jpg\") repeat-y scroll left top;\\r\\nborder-bottom: 1px solid #64a1df;', 48)", '67' => "(67, 8, 'Pagination bar: text', 'table.block-pagination td', 'a:3:{s:5:\"color\";s:7:\"#8B898B\";s:9:\"font-size\";s:4:\"12px\";s:11:\"font-weight\";s:6:\"normal\";}', '', 1, '', 0)", '45' => "(45, 8, 'Category link', 'a.subcat', 'a:0:{}', 'Category link', 1, 'color: #2069A4', 0)", '68' => "(68, 8, 'Pagination bar: link', 'table.block-pagination td a', 'a:3:{s:5:\"color\";s:7:\"#8B898B\";s:9:\"font-size\";s:5:\"12px;\";s:11:\"font-weight\";s:6:\"normal\";}', '', 1, '', 0)", '69' => "(69, 8, 'Product description in product list', '.product-list-description', 'a:2:{s:5:\"color\";s:7:\"#8B898B\";s:9:\"font-size\";s:4:\"12px\";}', '', 1, '', 0)", '73' => "(73, 8, 'Main path link', 'table.main-path td a', 'a:0:{}', '', 1, 'color: #d5e231;', 0)", '83' => "(83, 8, 'Product title link in list (shopping cart)', 'a.link-product-cart', 'a:0:{}', 'Product title link', 1, 'color: #18559C;\\r\\nfont-size: 12px;\\r\\nfont-weight: bold;\\r\\ntext-decoration: none;\\r\\n\\r\\n', 0)", '72' => "(72, 8, 'Main path block text', 'table.main-path td', 'a:0:{}', '', 1, 'color: #ffffff;\\r\\nfont-size: 10px;\\r\\nfont-weight: normal;\\r\\npadding: 1px;\\r\\n', 0)", '61' => "(61, 8, 'Block: header of data table', 'td.block-data-grid-header', 'a:6:{s:4:\"font\";s:28:\"Arial, Helvetica, sans-serif\";s:5:\"color\";s:7:\"#009DF6\";s:9:\"font-size\";s:4:\"12px\";s:11:\"font-weight\";s:4:\"bold\";s:16:\"background-color\";s:7:\"#E6EEFF\";s:7:\"padding\";s:3:\"6px\";}', '', 1, 'white-space: nowrap;\\r\\npadding-left: 10px;\\r\\n/*\\r\\nbackground-image: url(/in-commerce4/themes/default/img/bullet1.gif);\\r\\nbackground-position: 10px 12px;\\r\\nbackground-repeat: no-repeat;\\r\\n*/', 0)", '65' => "(65, 8, 'Link in product list additional row', 'td.product-list-additional a', 'a:1:{s:5:\"color\";s:7:\"#8B898B\";}', '', 2, '', 64)", '55' => "(55, 8, 'Main table, left column', 'td.main-column-left', 'a:0:{}', '', 1, 'width:180px;\\r\\nborder: 1px solid #62A1DE;\\r\\nborder-top: 0px;', 0)", '70' => "(70, 8, 'Product title link in list (category)', 'a.link-product-category', 'a:0:{}', 'Product title link', 1, 'color: #18559C;\\r\\nfont-size: 12px;\\r\\nfont-weight: bold;\\r\\ntext-decoration: none;\\r\\n\\r\\n', 0)", '66' => "(66, 8, 'Pagination bar block', 'table.block-pagination', 'a:0:{}', '', 1, '', 0)", '49' => "(49, 8, 'Bulleted list inside block', 'td.block-data ul li', 'a:0:{}', '', 1, ' list-style-image: url(/in-commerce4/themes/default/img/bullet2.gif);\\r\\n margin-bottom: 10px;\\r\\n font-size: 11px;', 0)", '87' => "(87, 8, 'Cart item input form element', 'td.cart-item-atributes input', 'a:0:{}', '', 1, 'border: 1px solid #7BB2E6;', 0)", '119' => "(119, 8, 'Data grid row header', 'td.block-data-row-hdr', 'a:0:{}', 'Used in order preview', 2, 'background-color: #eeeeee;\\r\\nborder-bottom: 1px solid #dddddd;\\r\\nborder-top: 1px solid #cccccc;\\r\\nfont-weight: bold;', 48)", '82' => "(82, 8, '\"More\" link image', 'a.link-more img', 'a:0:{}', '', 2, 'text-decoration: none;\\r\\npadding-left: 5px;', 64)", '63' => "(63, 8, 'Additional info under product description in list', 'td.product-list-additional', 'a:5:{s:5:\"color\";s:7:\"#8B898B\";s:9:\"font-size\";s:4:\"11px\";s:11:\"font-weight\";s:6:\"normal\";s:10:\"border-top\";s:18:\"1px dashed #8B898B\";s:13:\"border-bottom\";s:18:\"1px dashed #8B898B\";}', '', 2, '', 48)", '43' => "(43, 8, 'Block', 'table.block', 'a:2:{s:16:\"background-color\";s:7:\"#E3EEF9\";s:6:\"border\";s:17:\"1px solid #64A1DF\";}', 'Block', 1, 'border: 0; \\r\\nmargin-bottom: 1px;\\r\\nwidth: 100%;', 0)", '84' => "(84, 8, 'Cart item cell', 'td.cart-item', 'a:0:{}', '', 1, 'background-color: #F6FAFF;\\r\\nborder-left: 1px solid #ffffff;\\r\\nborder-bottom: 1px solid #ffffff;\\r\\npadding: 4px;', 0)", '57' => "(57, 8, 'Main table, right column', 'td.main-column-right', 'a:0:{}', '', 1, 'width:220px;\\r\\nborder: 1px solid #62A1DE;\\r\\nborder-top: 0px;', 0)", '161' => "(161, 8, 'Block for sub categories', 'td.block-data-subcats', 'a:0:{}', '', 2, ' background: #FFFFFF\\r\\nurl(/in-commerce4/themes/default/in-commerce/img/bgr_categories.jpg);\\r\\n background-repeat: no-repeat;\\r\\n background-position: top right;\\r\\nborder-bottom: 5px solid #DEEAFF;\\r\\npadding-left: 10px;', 48)", '77' => "(77, 8, 'Left block header', 'td.left-block-header', 'a:0:{}', '', 2, 'font-family : verdana, helvetica, sans-serif;\\r\\ncolor : #ffffff;\\r\\nfont-size : 12px;\\r\\nfont-weight : bold;\\r\\ntext-decoration : none;\\r\\nbackground-color: #64a1df;\\r\\npadding: 5px;\\r\\npadding-left: 7px;', 42)", '80' => "(80, 8, 'Right block data - text', 'td.right-block-data td', 'a:1:{s:9:\"font-size\";s:5:\"11px;\";}', '', 2, '', 48)", '53' => "(53, 8, 'Right block header', 'td.right-block-header', 'a:0:{}', '', 2, 'font-family : verdana, helvetica, sans-serif;\\r\\ncolor : #ffffff;\\r\\nfont-size : 12px;\\r\\nfont-weight : bold;\\r\\ntext-decoration : none;\\r\\nbackground-color: #64a1df;\\r\\npadding: 5px;\\r\\npadding-left: 7px;', 42)", '85' => "(85, 8, 'Cart item cell with attributes', 'td.cart-item-attributes', 'a:0:{}', '', 1, 'background-color: #E6EEFF;\\r\\nborder-left: 1px solid #ffffff;\\r\\nborder-bottom: 1px solid #ffffff;\\r\\npadding: 4px;\\r\\ntext-align: center;\\r\\nvertical-align: middle;\\r\\nfont-size: 12px;\\r\\nfont-weight: normal;', 0)", '86' => "(86, 8, 'Cart item cell with name', 'td.cart-item-name', 'a:0:{}', '', 1, 'background-color: #F6FAFF;\\r\\nborder-left: 1px solid #ffffff;\\r\\nborder-bottom: 1px solid #ffffff;\\r\\npadding: 3px;', 0)", '47' => "(47, 8, 'Block content of featured product', 'td.featured-block-data', 'a:0:{}', '', 1, 'font-family: Arial,Helvetica,sans-serif;\\r\\nfont-size: 12px;', 0)", '56' => "(56, 8, 'Main table, middle column', 'td.main-column-center', 'a:0:{}', '', 1, '\\r\\n', 0)", '50' => "(50, 8, 'Product title link in list', 'a.link-product2', 'a:0:{}', 'Product title link', 1, 'color: #62A1DE;\\r\\nfont-size: 12px;\\r\\nfont-weight: bold;\\r\\ntext-decoration: none;\\r\\n\\r\\n', 0)", '71' => "(71, 8, 'Main path block', 'table.main-path', 'a:0:{}', '', 1, 'background: #61b0ec url(\"/in-commerce4/themes/default/img/bgr_path.jpg\") repeat-y scroll left top;\\r\\nwidth: 100%;\\r\\nmargin-bottom: 1px;\\r\\nmargin-right: 1px; \\r\\nmargin-left: 1px;', 0)", '62' => "(62, 8, 'Block: columns header for data table', 'table.block-no-border th', 'a:6:{s:4:\"font\";s:28:\"Arial, Helvetica, sans-serif\";s:5:\"color\";s:7:\"#18559C\";s:9:\"font-size\";s:4:\"11px\";s:11:\"font-weight\";s:4:\"bold\";s:16:\"background-color\";s:7:\"#B4D2EE\";s:7:\"padding\";s:3:\"6px\";}', '', 1, 'text-align: left;', 0)", '59' => "(59, 8, 'Block without border', 'table.block-no-border', 'a:0:{}', '', 1, 'border: 0px; \\r\\nmargin-bottom: 10px;\\r\\nwidth: 100%;', 0)", '74' => "(74, 8, 'Main path language selector cell', 'td.main-path-language', 'a:0:{}', '', 1, 'vertical-align: middle;\\r\\ntext-align: right;\\r\\npadding-right: 6px;', 0)", '171' => "(171, 8, 'Calendar''s highlighted day', '.calendar tbody .hilite', 'a:0:{}', '', 1, 'background-color: #f6f6f6;\\r\\nborder: 1px solid #83B2C5 !important;', 0)", '175' => "(175, 8, 'Calendar''s days', '.calendar tbody .day', 'a:0:{}', '', 1, 'text-align: right;\\r\\npadding: 2px 4px 2px 2px;\\r\\nwidth: 2em;\\r\\nborder: 1px solid #fefefe;', 0)", '170' => "(170, 8, 'Calendar''s weekends', '.calendar .weekend', 'a:0:{}', '', 1, 'color: #990000;', 0)", '173' => "(173, 8, 'Calendar''s control buttons', '.calendar .calendar_button', 'a:0:{}', '', 1, 'color: black;\\r\\nfont-size: 12px;\\r\\nbackground-color: #eeeeee;', 0)", '174' => "(174, 8, 'Calendar''s day names', '.calendar thead .name', 'a:0:{}', '', 1, 'background-color: #DEEEF6;\\r\\nborder-bottom: 1px solid #000000;', 0)", '172' => "(172, 8, 'Calendar''s top and bottom titles', '.calendar .title', 'a:0:{}', '', 1, 'color: #FFFFFF;\\r\\nbackground-color: #62A1DE;\\r\\nborder: 1px solid #107DC5;\\r\\nborder-top: 0px;\\r\\npadding: 1px;', 0)", '60' => "(60, 8, 'Block header for featured product', 'td.featured-block-header', 'a:0:{}', '', 2, '\\r\\n', 42)", '54' => "(54, 8, 'Right block', 'table.right-block', 'a:0:{}', '', 2, 'background-color: #E3EEF9;\\r\\nborder: 0px;\\r\\nwidth: 100%;', 43)", '44' => "(44, 8, 'Block content', 'td.block-data-big', 'a:0:{}', 'Block content', 1, ' background: #DEEEF6\\r\\nurl(/in-commerce4/themes/default/img/menu_bg.gif);\\r\\n background-repeat: no-repeat;\\r\\n background-position: top right;\\r\\n', 0)", ), ), 'Stylesheets' => Array ( 'UniqueField' => 'StylesheetId', 'Records' => Array ( '8' => "(8, 'Default', 'In-Portal Default Theme', '', 1124952555, 1)", ), ), 'Counters' => Array ( 'UniqueField' => 'Name', 'Records' => Array ( 'members_count' => "(DEFAULT, 'members_count', 'SELECT COUNT(*) FROM <%PREFIX%>PortalUser WHERE Status = 1', NULL , NULL , '3600', '0', '|PortalUser|')", 'members_online' => "(DEFAULT, 'members_online', 'SELECT COUNT(*) FROM <%PREFIX%>UserSession WHERE PortalUserId > 0', NULL , NULL , '3600', '0', '|UserSession|')", 'guests_online' => "(DEFAULT, 'guests_online', 'SELECT COUNT(*) FROM <%PREFIX%>UserSession WHERE PortalUserId <= 0', NULL , NULL , '3600', '0', '|UserSession|')", 'users_online' => "(DEFAULT, 'users_online', 'SELECT COUNT(*) FROM <%PREFIX%>UserSession', NULL , NULL , '3600', '0', '|UserSession|')", ), ), ); // check & insert if not found defined before data foreach ($data as $table_name => $table_info) { $unique_fields = explode(';', $table_info['UniqueField']); foreach ($table_info['Records'] as $unique_value => $insert_sql) { $unique_values = explode(';', $unique_value); $where_clause = Array (); foreach ($unique_fields as $field_index => $unique_field) { $where_clause[] = $unique_field . ' = ' . $this->Conn->qstr($unique_values[$field_index]); } $sql = 'SELECT ' . implode(', ', $unique_fields) . ' FROM ' . TABLE_PREFIX . $table_name . ' WHERE (' . implode(') AND (', $where_clause) . ')'; $found = $this->Conn->GetRow($sql); if ($found) { $found = implode(';', $found); } if ($found != $unique_value) { $this->Conn->Query('INSERT INTO ' . TABLE_PREFIX . $table_name . ' VALUES ' . $insert_sql); } } } } /** * Removes duplicate phrases per language basis (created during proj-base and in-portal shared installation) * */ function _removeDuplicatePhrases() { $config = $this->Application->getUnitConfig('phrases'); $id_field = $config->getIDField(); $table_name = $config->getTableName(); $sql = 'SELECT LanguageId, Phrase, MIN(LastChanged) AS LastChanged, COUNT(*) AS DupeCount FROM ' . $table_name . ' GROUP BY LanguageId, Phrase HAVING COUNT(*) > 1'; $duplicate_phrases = $this->Conn->Query($sql); foreach ($duplicate_phrases as $phrase_record) { // 1. keep phrase, that was added first, because it is selected in kPhraseCache::LoadPhraseByLabel. $where_clause = Array ( 'LanguageId = ' . $phrase_record['LanguageId'], 'Phrase = ' . $this->Conn->qstr($phrase_record['Phrase']), 'LastChanged' . ' = ' . $phrase_record['LastChanged'], ); $sql = 'SELECT ' . $id_field . ' FROM ' . $table_name . ' WHERE (' . implode(') AND (', $where_clause) . ')'; $phrase_id = $this->Conn->GetOne($sql); // 2. delete all other duplicates $where_clause = Array ( 'LanguageId = ' . $phrase_record['LanguageId'], 'Phrase = ' . $this->Conn->qstr($phrase_record['Phrase']), $id_field . ' <> ' . $phrase_id, ); $sql = 'DELETE FROM ' . $table_name . ' WHERE (' . implode(') AND (', $where_clause) . ')'; $this->Conn->Query($sql); } } function _moveDatabaseFolders() { // Tables: PageContent, Images if ($this->Conn->TableFound('PageContent', true)) { // 1. replaces "/kernel/user_files/" references in content blocks $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ $languages = $ml_helper->getLanguages(); $replace_sql = '%1$s = REPLACE(%1$s, "/kernel/user_files/", "/system/user_files/")'; $update_sqls = Array (); foreach ($languages as $language_id) { $update_sqls[] = sprintf($replace_sql, 'l' . $language_id . '_Content'); } if ($update_sqls) { $sql = 'UPDATE ' . TABLE_PREFIX . 'PageContent SET ' . implode(', ', $update_sqls); $this->Conn->Query($sql); } } // 2. replace path of images uploaded via "Images" tab of category items $this->_replaceImageFolder('/kernel/images/', '/system/images/'); // 3. replace path of images uploaded via "Images" tab of category items (when badly formatted) $this->_replaceImageFolder('kernel/images/', 'system/images/'); // 4. replace images uploaded via "In-Bulletin -> Emoticons" section $this->_replaceImageFolder('in-bulletin/images/emoticons/', 'system/images/emoticons/'); // 5. update backup path in config $this->_toolkit->saveConfigValues( Array ( 'Backup_Path' => FULL_PATH . '/system/backupdata' ) ); } /** * Replaces mentions of "/kernel/images" folder in Images table * * @param string $from * @param string $to */ function _replaceImageFolder($from, $to) { $replace_sql = '%1$s = REPLACE(%1$s, "' . $from . '", "' . $to . '")'; $sql = 'UPDATE ' . TABLE_PREFIX . 'Images SET ' . sprintf($replace_sql, 'ThumbPath') . ', ' . sprintf($replace_sql, 'LocalPath'); $this->Conn->Query($sql); } /** * Update colors in skin (only if they were not changed manually) * * @param string $mode when called mode {before, after) */ function Upgrade_5_0_0($mode) { if ($mode == 'before') { $this->_removeDuplicatePhrases(); // because In-Commerce & In-Link share some phrases with Proj-CMS $this->_createProjCMSTables(); $this->_addMissingConfigurationVariables(); } if ($mode == 'after') { $this->_fixSkinColors(); $this->_restructureCatalog(); $this->_sortImages(); // $this->_sortConfigurationVariables('In-Portal', 'in-portal:configure_general'); // $this->_sortConfigurationVariables('In-Portal', 'in-portal:configure_advanced'); } } function _sortConfigurationVariables($module, $section) { $sql = 'SELECT ca.heading, cv.VariableName FROM ' . TABLE_PREFIX . 'ConfigurationAdmin ca LEFT JOIN ' . TABLE_PREFIX . 'ConfigurationValues cv USING(VariableName) WHERE (cv.ModuleOwner = ' . $this->Conn->qstr($module) . ') AND (cv.Section = ' . $this->Conn->qstr($section) . ') ORDER BY ca.DisplayOrder asc, ca.GroupDisplayOrder asc'; $variables = $this->Conn->GetCol($sql, 'VariableName'); if (!$variables) { return ; } $variables = $this->_groupRecords($variables); $group_number = 0; $variable_order = 1; $prev_heading = ''; foreach ($variables as $variable_name => $variable_heading) { if ($prev_heading != $variable_heading) { $group_number++; $variable_order = 1; } $sql = 'UPDATE ' . TABLE_PREFIX . 'ConfigurationAdmin SET DisplayOrder = ' . $this->Conn->qstr($group_number * 10 + $variable_order / 100) . ' WHERE VariableName = ' . $this->Conn->qstr($variable_name); $this->Conn->Query($sql); $variable_order++; $prev_heading = $variable_heading; } } /** * Group list records by header, saves internal order in group * * @param Array $variables * @return Array */ function _groupRecords($variables) { $sorted = Array(); foreach ($variables as $variable_name => $variable_heading) { $sorted[$variable_heading][] = $variable_name; } $variables = Array(); foreach ($sorted as $heading => $heading_records) { foreach ($heading_records as $variable_name) { $variables[$variable_name] = $heading; } } return $variables; } /** * Returns module root category * * @param string $module_name * @param string $module_prefix * @return int */ function _getRootCategory($module_name, $module_prefix) { // don't cache anything here (like in static variables), because database value is changed on the fly !!! $sql = 'SELECT RootCat FROM ' . TABLE_PREFIX . 'Modules WHERE LOWER(Name) = ' . $this->Conn->qstr( strtolower($module_name) ); $root_category = $this->Conn->GetOne($sql); // put to cache too, because CategoriesEventHandler::_prepareAutoPage uses kApplication::findModule $this->Application->ModuleInfo[$module_name]['Name'] = $module_name; $this->Application->ModuleInfo[$module_name]['RootCat'] = $root_category; $this->Application->ModuleInfo[$module_name]['Var'] = $module_prefix; return $root_category; } /** * Move all categories (except "Content") from "Home" to "Content" category and hide them from menu * */ function _restructureCatalog() { $root_category = $this->_getRootCategory('Core', 'adm'); $sql = 'SELECT CategoryId FROM ' . TABLE_PREFIX . 'Category WHERE ParentId = 0 AND CategoryId <> ' . $root_category; $top_categories = $this->Conn->GetCol($sql); if ($top_categories) { // hide all categories located outside "Content" category from menu $sql = 'UPDATE ' . TABLE_PREFIX . 'Category SET IsMenu = 0 WHERE (ParentPath LIKE "|' . implode('|%") OR (ParentPath LIKE "|', $top_categories) . '|%")'; $this->Conn->Query($sql); // move all top level categories under "Content" category and make them visible in menu $sql = 'UPDATE ' . TABLE_PREFIX . 'Category SET IsMenu = 1, ParentId = ' . $root_category . ' WHERE ParentId = 0 AND CategoryId <> ' . $root_category; $this->Conn->Query($sql); } // make sure, that all categories have valid value for Priority field $priority_helper = $this->Application->recallObject('PriorityHelper'); /* @var $priority_helper kPriorityHelper */ $event = new kEvent('c:OnListBuild'); // update all categories, because they are all under "Content" category now $sql = 'SELECT CategoryId FROM ' . TABLE_PREFIX . 'Category'; $categories = $this->Conn->GetCol($sql); foreach ($categories as $category_id) { $priority_helper->recalculatePriorities($event, 'ParentId = ' . $category_id); } // create initial theme structure in Category table $this->_toolkit->rebuildThemes(); // make sure, that all system templates have ThemeId set (only possible during platform project upgrade) $sql = 'SELECT ThemeId FROM ' . TABLE_PREFIX . 'Theme WHERE PrimaryTheme = 1'; $primary_theme_id = $this->Conn->GetOne($sql); if ($primary_theme_id) { $sql = 'UPDATE ' . TABLE_PREFIX . 'Category SET ThemeId = ' . $primary_theme_id . ' WHERE IsSystem = 1 AND ThemeId = 0'; $this->Conn->Query($sql); } } /** * Changes skin colors to match new ones (only in case, when they match default values) * */ function _fixSkinColors() { $skin = $this->Application->recallObject('skin', null, Array ('skip_autoload' => 1)); /* @var $skin kDBItem */ $skin->Load(1, 'IsPrimary'); if ($skin->isLoaded()) { $skin_options = unserialize( $skin->GetDBField('Options') ); $changes = Array ( // option: from -> to 'HeadBgColor' => Array ('#1961B8', '#007BF4'), 'HeadBarColor' => Array ('#FFFFFF', '#000000'), 'HeadColor' => Array ('#CCFF00', '#FFFFFF'), 'TreeColor' => Array ('#006F99', '#000000'), 'TreeHoverColor' => Array ('', '#009FF0'), 'TreeHighHoverColor' => Array ('', '#FFFFFF'), 'TreeHighBgColor' => Array ('#4A92CE', '#4A92CE'), 'TreeBgColor' => Array ('#FFFFFF', '#DCECF6'), ); $can_change = true; foreach ($changes as $option_name => $change) { list ($change_from, $change_to) = $change; $can_change = $can_change && ($change_from == $skin_options[$option_name]['Value']); if ($can_change) { $skin_options[$option_name]['Value'] = $change_to; } } if ($can_change) { $skin->SetDBField('Options', serialize($skin_options)); $skin->Update(); $skin_helper = $this->Application->recallObject('SkinHelper'); /* @var $skin_helper SkinHelper */ $skin_file = $skin_helper->getSkinPath(); if (file_exists($skin_file)) { unlink($skin_file); } } } } /** * 1. Set root category not to generate filename automatically and hide it from catalog * 2. Hide root category of In-Edit and set it's fields * * @param int $category_id */ function _resetRootCategory($category_id) { $fields_hash = Array ( 'l1_Name' => 'Content', 'Filename' => 'Content', 'AutomaticFilename' => 0, 'l1_Description' => 'Content', 'Status' => 4, ); $this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'Category', 'CategoryId = ' . $category_id); } function _createProjCMSTables() { // 0. make sure, that Content category exists $root_category = $this->_getRootCategory('Proj-CMS', 'st'); if ($root_category) { // proj-cms module found -> remove it $sql = 'DELETE FROM ' . TABLE_PREFIX . 'Modules WHERE Name = "Proj-CMS"'; $this->Conn->Query($sql); unset($this->Application->ModuleInfo['Proj-CMS']); $this->_resetRootCategory($root_category); // unhide all structure categories $sql = 'UPDATE ' . TABLE_PREFIX . 'Category SET Status = 1 WHERE (Status = 4) AND (CategoryId <> ' . $root_category . ')'; $this->Conn->Query($sql); } else { $root_category = $this->_getRootCategory('In-Edit', 'cms'); if ($root_category) { // in-edit module found -> remove it $sql = 'DELETE FROM ' . TABLE_PREFIX . 'Modules WHERE Name = "In-Edit"'; $this->Conn->Query($sql); unset($this->Application->ModuleInfo['In-Edit']); $this->_resetRootCategory($root_category); } } if (!$root_category) { // create "Content" category when Proj-CMS/In-Edit module was not installed before // use direct sql here, because category table structure doesn't yet match table structure in object $fields_hash = Array ( 'l1_Name' => 'Content', 'Filename' => 'Content', 'AutomaticFilename' => 0, 'l1_Description' => 'Content', 'Status' => 4, ); $this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'Category'); $root_category = $this->Conn->getInsertID(); } $this->_toolkit->deleteCache(); $this->_toolkit->SetModuleRootCategory('Core', $root_category); // 1. process "Category" table $structure = $this->Conn->Query('DESCRIBE ' . TABLE_PREFIX . 'Category', 'Field'); if (!array_key_exists('Template', $structure)) { // fields from "Pages" table were not added to "Category" table (like before "Proj-CMS" module install) $sql = "ALTER TABLE " . TABLE_PREFIX . "Category ADD COLUMN Template varchar(255) default NULL, ADD COLUMN l1_Title varchar(255) default '', ADD COLUMN l2_Title varchar(255) default '', ADD COLUMN l3_Title varchar(255) default '', ADD COLUMN l4_Title varchar(255) default '', ADD COLUMN l5_Title varchar(255) default '', ADD COLUMN l1_MenuTitle varchar(255) NOT NULL default '', ADD COLUMN l2_MenuTitle varchar(255) NOT NULL default '', ADD COLUMN l3_MenuTitle varchar(255) NOT NULL default '', ADD COLUMN l4_MenuTitle varchar(255) NOT NULL default '', ADD COLUMN l5_MenuTitle varchar(255) NOT NULL default '', ADD COLUMN MetaTitle text, ADD COLUMN IndexTools text, ADD COLUMN IsIndex tinyint(1) NOT NULL default '0', ADD COLUMN IsMenu TINYINT(4) NOT NULL DEFAULT '1', ADD COLUMN IsSystem tinyint(4) NOT NULL default '0', ADD COLUMN FormId int(11) default NULL, ADD COLUMN FormSubmittedTemplate varchar(255) default NULL, ADD COLUMN l1_Translated tinyint(4) NOT NULL default '0', ADD COLUMN l2_Translated tinyint(4) NOT NULL default '0', ADD COLUMN l3_Translated tinyint(4) NOT NULL default '0', ADD COLUMN l4_Translated tinyint(4) NOT NULL default '0', ADD COLUMN l5_Translated tinyint(4) NOT NULL default '0', ADD COLUMN FriendlyURL varchar(255) NOT NULL default '', ADD INDEX IsIndex (IsIndex), ADD INDEX l1_Translated (l1_Translated), ADD INDEX l2_Translated (l2_Translated), ADD INDEX l3_Translated (l3_Translated), ADD INDEX l4_Translated (l4_Translated), ADD INDEX l5_Translated (l5_Translated)"; $this->Conn->Query($sql); } if (array_key_exists('Path', $structure)) { $sql = 'ALTER TABLE ' . TABLE_PREFIX . 'Category DROP Path'; $this->Conn->Query($sql); } // 2. process "PageContent" table if ($this->Conn->TableFound(TABLE_PREFIX . 'PageContent', true)) { $structure = $this->Conn->Query('DESCRIBE ' . TABLE_PREFIX . 'PageContent', 'Field'); if (!array_key_exists('l1_Translated', $structure)) { $sql = "ALTER TABLE " . TABLE_PREFIX . "PageContent ADD COLUMN l1_Translated tinyint(4) NOT NULL default '0', ADD COLUMN l2_Translated tinyint(4) NOT NULL default '0', ADD COLUMN l3_Translated tinyint(4) NOT NULL default '0', ADD COLUMN l4_Translated tinyint(4) NOT NULL default '0', ADD COLUMN l5_Translated tinyint(4) NOT NULL default '0'"; $this->Conn->Query($sql); } } // 3. process "FormFields" table if ($this->Conn->TableFound(TABLE_PREFIX . 'FormFields', true)) { $structure = $this->Conn->Query('DESCRIBE ' . TABLE_PREFIX . 'FormFields', 'Field'); if (!$structure['FormId']['Key']) { $sql = "ALTER TABLE " . TABLE_PREFIX . "FormFields CHANGE Validation Validation TINYINT NOT NULL DEFAULT '0', ADD INDEX FormId (FormId), ADD INDEX Priority (Priority), ADD INDEX IsSystem (IsSystem), ADD INDEX DisplayInGrid (DisplayInGrid)"; $this->Conn->Query($sql); } } else { $this->Conn->Query("INSERT INTO " . TABLE_PREFIX . "Permissions VALUES (DEFAULT, 'in-portal:forms.view', 11, 1, 1, 0)"); $this->Conn->Query("INSERT INTO " . TABLE_PREFIX . "Permissions VALUES (DEFAULT, 'in-portal:forms.add', 11, 1, 1, 0)"); $this->Conn->Query("INSERT INTO " . TABLE_PREFIX . "Permissions VALUES (DEFAULT, 'in-portal:forms.edit', 11, 1, 1, 0)"); $this->Conn->Query("INSERT INTO " . TABLE_PREFIX . "Permissions VALUES (DEFAULT, 'in-portal:forms.delete', 11, 1, 1, 0)"); } // 4. process "FormSubmissions" table if ($this->Conn->TableFound(TABLE_PREFIX . 'FormSubmissions', true)) { $structure = $this->Conn->Query('DESCRIBE ' . TABLE_PREFIX . 'FormSubmissions', 'Field'); if (!$structure['SubmissionTime']['Key']) { $sql = "ALTER TABLE " . TABLE_PREFIX . "FormSubmissions ADD INDEX SubmissionTime (SubmissionTime)"; $this->Conn->Query($sql); } } else { $this->Conn->Query("INSERT INTO " . TABLE_PREFIX . "Permissions VALUES (DEFAULT, 'in-portal:submissions.view', 11, 1, 1, 0)"); } // 5. add missing event $sql = 'SELECT EventId FROM ' . TABLE_PREFIX . 'Events WHERE (Event = "FORM.SUBMITTED") AND (Type = 1)'; $event_id = $this->Conn->GetOne($sql); if (!$event_id) { $sql = "INSERT INTO " . TABLE_PREFIX . "Events VALUES (DEFAULT, 'FORM.SUBMITTED', NULL, 1, 0, 'Core:Category', 'la_event_FormSubmitted', 1)"; $this->Conn->Query($sql); } $sql = 'SELECT EventId FROM ' . TABLE_PREFIX . 'Events WHERE (Event = "FORM.SUBMITTED") AND (Type = 0)'; $event_id = $this->Conn->GetOne($sql); if (!$event_id) { $sql = "INSERT INTO " . TABLE_PREFIX . "Events VALUES (DEFAULT, 'FORM.SUBMITTED', NULL, 1, 0, 'Core:Category', 'la_event_FormSubmitted', 0)"; $this->Conn->Query($sql); } } function _addMissingConfigurationVariables() { $variables = Array ( 'cms_DefaultDesign' => Array ( "INSERT INTO " . TABLE_PREFIX . "ConfigurationAdmin VALUES ('cms_DefaultDesign', 'la_Text_General', 'la_prompt_DefaultDesignTemplate', 'text', NULL, NULL, 10.15, 0, 0)", "INSERT INTO " . TABLE_PREFIX . "ConfigurationValues VALUES (DEFAULT, 'cms_DefaultDesign', '/platform/designs/general', 'In-Portal', 'in-portal:configure_categories')", ), 'Require_AdminSSL' => Array ( "INSERT INTO " . TABLE_PREFIX . "ConfigurationAdmin VALUES ('Require_AdminSSL', 'la_Text_Website', 'la_config_RequireSSLAdmin', 'checkbox', '', '', 10.105, 0, 1)", "INSERT INTO " . TABLE_PREFIX . "ConfigurationValues VALUES (DEFAULT, 'Require_AdminSSL', '', 'In-Portal', 'in-portal:configure_advanced')", ), 'UsePopups' => Array ( "INSERT INTO " . TABLE_PREFIX . "ConfigurationAdmin VALUES ('UsePopups', 'la_Text_Website', 'la_config_UsePopups', 'radio', '', '1=la_Yes,0=la_No', 10.221, 0, 0)", "INSERT INTO " . TABLE_PREFIX . "ConfigurationValues VALUES (DEFAULT, 'UsePopups', '1', 'In-Portal', 'in-portal:configure_advanced')", ), 'UseDoubleSorting' => Array ( "INSERT INTO " . TABLE_PREFIX . "ConfigurationAdmin VALUES ('UseDoubleSorting', 'la_Text_Website', 'la_config_UseDoubleSorting', 'radio', '', '1=la_Yes,0=la_No', 10.222, 0, 0)", "INSERT INTO " . TABLE_PREFIX . "ConfigurationValues VALUES (DEFAULT, 'UseDoubleSorting', '0', 'In-Portal', 'in-portal:configure_advanced')", ), 'MenuFrameWidth' => Array ( "INSERT INTO " . TABLE_PREFIX . "ConfigurationAdmin VALUES ('MenuFrameWidth', 'la_title_General', 'la_prompt_MenuFrameWidth', 'text', NULL, NULL, 10.31, 0, 0)", "INSERT INTO " . TABLE_PREFIX . "ConfigurationValues VALUES (DEFAULT, 'MenuFrameWidth', 200, 'In-Portal', 'in-portal:configure_advanced')", ), 'DefaultSettingsUserId' => Array ( "INSERT INTO " . TABLE_PREFIX . "ConfigurationAdmin VALUES ('DefaultSettingsUserId', 'la_title_General', 'la_prompt_DefaultUserId', 'text', NULL, NULL, '10.06', '0', '0')", "INSERT INTO " . TABLE_PREFIX . "ConfigurationValues VALUES (DEFAULT, 'DefaultSettingsUserId', -1, 'In-Portal:Users', 'in-portal:configure_users')", ), ); foreach ($variables as $variable_name => $variable_sqls) { $sql = 'SELECT VariableId FROM ' . TABLE_PREFIX . 'ConfigurationValues WHERE VariableName = ' . $this->Conn->qstr($variable_name); $variable_id = $this->Conn->GetOne($sql); if ($variable_id) { continue; } foreach ($variable_sqls as $variable_sql) { $this->Conn->Query($variable_sql); } } } /** * Sort images in database (update Priority field) * */ function _sortImages() { $sql = 'SELECT * FROM ' . TABLE_PREFIX . 'Images ORDER BY ResourceId ASC , DefaultImg DESC , ImageId ASC'; $images = $this->Conn->Query($sql); $priority = 0; $last_resource_id = false; foreach ($images as $image) { if ($image['ResourceId'] != $last_resource_id) { // each item have own priorities among it's images $priority = 0; $last_resource_id = $image['ResourceId']; } if (!$image['DefaultImg']) { $priority--; } $sql = 'UPDATE ' . TABLE_PREFIX . 'Images SET Priority = ' . $priority . ' WHERE ImageId = ' . $image['ImageId']; $this->Conn->Query($sql); } } /** * Update to 5.0.1 * * @param string $mode when called mode {before, after) */ function Upgrade_5_0_1($mode) { if ($mode == 'after') { // delete old events $events_to_delete = Array ('CATEGORY.MODIFY', 'CATEGORY.DELETE'); $sql = 'SELECT EventId FROM ' . TABLE_PREFIX . 'Events WHERE Event IN ("' . implode('","', $events_to_delete) . '")'; $event_ids = $this->Conn->GetCol($sql); if ($event_ids) { $this->_deleteEvents($event_ids); $sql = 'DELETE FROM ' . TABLE_PREFIX . 'Phrase WHERE Phrase IN ("la_event_category.modify", "la_event_category.delete")'; $this->Conn->Query($sql); } // partially delete events $sql = 'SELECT EventId FROM ' . TABLE_PREFIX . 'Events WHERE (Event IN ("CATEGORY.APPROVE", "CATEGORY.DENY")) AND (Type = ' . EmailTemplate::TEMPLATE_TYPE_ADMIN . ')'; $event_ids = $this->Conn->GetCol($sql); if ($event_ids) { $this->_deleteEvents($event_ids); } } } function _deleteEvents($ids) { $sql = 'DELETE FROM ' . TABLE_PREFIX . 'EmailMessage WHERE EventId IN (' . implode(',', $ids) . ')'; $this->Conn->Query($sql); $sql = 'DELETE FROM ' . TABLE_PREFIX . 'Events WHERE EventId IN (' . implode(',', $ids) . ')'; $this->Conn->Query($sql); } /** * Update to 5.0.2-B2; Transforms IsIndex field values to SymLinkCategoryId field * * @param string $mode when called mode {before, after) */ function Upgrade_5_0_2_B2($mode) { // 0 - Regular, 1 - Category Index, 2 - Container if ($mode == 'before') { // fix "Content" category $fields_hash = Array ( 'CreatedById' => USER_ROOT, 'CreatedOn' => time(), 'ResourceId' => $this->Application->NextResourceId(), ); $category_id = $this->Application->findModule('Name', 'Core', 'RootCat'); $this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'Category', 'CategoryId = ' . $category_id); // get all categories, marked as category index $sql = 'SELECT ParentPath, CategoryId FROM ' . TABLE_PREFIX . 'Category WHERE IsIndex = 1'; $category_indexes = $this->Conn->GetCol($sql, 'CategoryId'); foreach ($category_indexes as $category_id => $parent_path) { $parent_path = explode('|', substr($parent_path, 1, -1)); // set symlink to $category_id for each category, marked as container in given category path $sql = 'SELECT CategoryId FROM ' . TABLE_PREFIX . 'Category WHERE CategoryId IN (' . implode(',', $parent_path) . ') AND (IsIndex = 2)'; $category_containers = $this->Conn->GetCol($sql); if ($category_containers) { $sql = 'UPDATE ' . TABLE_PREFIX . 'Category SET SymLinkCategoryId = ' . $category_id . ' WHERE CategoryId IN (' . implode(',', $category_containers) . ')'; $this->Conn->Query($sql); } } } if ($mode == 'after') { // scan theme to fill Theme.TemplateAliases and ThemeFiles.TemplateAlias fields $this->_toolkit->rebuildThemes(); $sql = 'SELECT TemplateAliases, ThemeId FROM ' . TABLE_PREFIX . 'Theme WHERE (Enabled = 1) AND (TemplateAliases <> "")'; $template_aliases = $this->Conn->GetCol($sql, 'ThemeId'); $all_template_aliases = Array (); // reversed alias (from real template to alias) foreach ($template_aliases as $theme_id => $theme_template_aliases) { $theme_template_aliases = unserialize($theme_template_aliases); if (!$theme_template_aliases) { continue; } $all_template_aliases = array_merge($all_template_aliases, array_flip($theme_template_aliases)); } $default_design_replaced = false; $default_design = trim($this->Application->ConfigValue('cms_DefaultDesign'), '/'); foreach ($all_template_aliases as $from_template => $to_alias) { // replace default design in configuration variable (when matches alias) if ($from_template == $default_design) { // specific alias matched $sql = 'UPDATE ' . TABLE_PREFIX . 'ConfigurationValues SET VariableValue = ' . $this->Conn->qstr($to_alias) . ' WHERE VariableName = "cms_DefaultDesign"'; $this->Conn->Query($sql); $default_design_replaced = true; } // replace Category.Template and Category.CachedTemplate fields (when matches alias) $sql = 'UPDATE ' . TABLE_PREFIX . 'Category SET Template = ' . $this->Conn->qstr($to_alias) . ' WHERE Template IN (' . $this->Conn->qstr('/' . $from_template) . ',' . $this->Conn->qstr($from_template) . ')'; $this->Conn->Query($sql); $sql = 'UPDATE ' . TABLE_PREFIX . 'Category SET CachedTemplate = ' . $this->Conn->qstr($to_alias) . ' WHERE CachedTemplate IN (' . $this->Conn->qstr('/' . $from_template) . ',' . $this->Conn->qstr($from_template) . ')'; $this->Conn->Query($sql); } if (!$default_design_replaced) { // in case if current default design template doesn't // match any of aliases, then set it to #default_design# $sql = 'UPDATE ' . TABLE_PREFIX . 'ConfigurationValues SET VariableValue = "#default_design#" WHERE VariableName = "cms_DefaultDesign"'; $this->Conn->Query($sql); } // replace data in category custom fields used for category item template storage $rewrite_processor = $this->Application->recallObject('kRewriteUrlProcessor'); /* @var $rewrite_processor kRewriteUrlProcessor */ foreach ($this->Application->ModuleInfo as $module_name => $module_info) { $custom_field_id = $rewrite_processor->getItemTemplateCustomField($module_info['Var']); if (!$custom_field_id) { continue; } foreach ($all_template_aliases as $from_template => $to_alias) { $sql = 'UPDATE ' . TABLE_PREFIX . 'CategoryCustomData SET l1_cust_' . $custom_field_id . ' = ' . $this->Conn->qstr($to_alias) . ' WHERE l1_cust_' . $custom_field_id . ' = ' . $this->Conn->qstr($from_template); $this->Conn->Query($sql); } } } } /** * Update to 5.0.3-B2; Moves CATEGORY.* permission from module root categories to Content category * * @param string $mode when called mode {before, after) */ function Upgrade_5_0_3_B2($mode) { if ($mode == 'before') { // get permissions $sql = 'SELECT PermissionName FROM ' . TABLE_PREFIX . 'PermissionConfig WHERE PermissionName LIKE "CATEGORY.%"'; $permission_names = $this->Conn->GetCol($sql); // get groups $sql = 'SELECT GroupId FROM ' . TABLE_PREFIX . 'PortalGroup'; $user_groups = $this->Conn->GetCol($sql); $user_group_count = count($user_groups); // get module root categories $sql = 'SELECT RootCat FROM ' . TABLE_PREFIX . 'Modules'; $module_categories = $this->Conn->GetCol($sql); $module_categories[] = 0; $module_categories = implode(',', array_unique($module_categories)); $permissions = $delete_permission_ids = Array (); foreach ($permission_names as $permission_name) { foreach ($user_groups as $group_id) { $sql = 'SELECT PermissionId FROM ' . TABLE_PREFIX . 'Permissions WHERE (Permission = ' . $this->Conn->qstr($permission_name) . ') AND (PermissionValue = 1) AND (GroupId = ' . $group_id . ') AND (`Type` = 0) AND (CatId IN (' . $module_categories . '))'; $permission_ids = $this->Conn->GetCol($sql); if ($permission_ids) { if (!array_key_exists($permission_name, $permissions)) { $permissions[$permission_name] = Array (); } $permissions[$permission_name][] = $group_id; $delete_permission_ids = array_merge($delete_permission_ids, $permission_ids); } } } if ($delete_permission_ids) { // here we can delete some of permissions that will be added later $sql = 'DELETE FROM ' . TABLE_PREFIX . 'Permissions WHERE PermissionId IN (' . implode(',', $delete_permission_ids) . ')'; $this->Conn->Query($sql); } $home_category = $this->Application->findModule('Name', 'Core', 'RootCat'); foreach ($permissions as $permission_name => $permission_groups) { // optimize a bit $has_everyone = in_array(15, $permission_groups); if ($has_everyone || (!$has_everyone && count($permission_groups) == $user_group_count - 1)) { // has permission for "Everyone" group OR allowed in all groups except "Everyone" group // so remove all other explicitly allowed permissions $permission_groups = Array (15); } foreach ($permission_groups as $group_id) { $fields_hash = Array ( 'Permission' => $permission_name, 'GroupId' => $group_id, 'PermissionValue' => 1, 'Type' => 0, // category-based permission, 'CatId' => $home_category, ); $this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'Permissions'); } } $updater = $this->Application->makeClass('kPermCacheUpdater'); /* @var $updater kPermCacheUpdater */ $updater->OneStepRun(); } } /** * Update to 5.1.0-B1; Makes email message fields multilingual * * @param string $mode when called mode {before, after) */ function Upgrade_5_1_0_B1($mode) { if ( $mode == 'before' ) { $this->_renameTables('from'); // migrate email events $table_structure = $this->Conn->Query('DESCRIBE ' . TABLE_PREFIX . 'Events', 'Field'); if (!array_key_exists('Headers', $table_structure)) { $sql = 'ALTER TABLE ' . TABLE_PREFIX . 'Events ADD `Headers` TEXT NULL AFTER `ReplacementTags`, ADD `MessageType` VARCHAR(4) NOT NULL default "text" AFTER `Headers`'; $this->Conn->Query($sql); } // alter here, because kMultiLanguageHelper::createFields // method, called after will expect that to be in database $sql = 'ALTER TABLE ' . TABLE_PREFIX . 'Events ADD AllowChangingSender TINYINT NOT NULL DEFAULT "0" AFTER MessageType , ADD CustomSender TINYINT NOT NULL DEFAULT "0" AFTER AllowChangingSender , ADD SenderName VARCHAR(255) NOT NULL DEFAULT "" AFTER CustomSender , ADD SenderAddressType TINYINT NOT NULL DEFAULT "0" AFTER SenderName , ADD SenderAddress VARCHAR(255) NOT NULL DEFAULT "" AFTER SenderAddressType , ADD AllowChangingRecipient TINYINT NOT NULL DEFAULT "0" AFTER SenderAddress , ADD CustomRecipient TINYINT NOT NULL DEFAULT "0" AFTER AllowChangingRecipient , ADD Recipients TEXT AFTER CustomRecipient, ADD INDEX (AllowChangingSender), ADD INDEX (CustomSender), ADD INDEX (SenderAddressType), ADD INDEX (AllowChangingRecipient), ADD INDEX (CustomRecipient)'; $this->Conn->Query($sql); // create multilingual fields for phrases and email events $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ $ml_helper->createFields('phrases'); $ml_helper->createFields('email-template'); $languages = $ml_helper->getLanguages(); if ($this->Conn->TableFound(TABLE_PREFIX . 'EmailMessage', true)) { $email_template_helper = $this->Application->recallObject('kEmailTemplateHelper'); /* @var $email_template_helper kEmailTemplateHelper */ foreach ($languages as $language_id) { $sql = 'SELECT EmailMessageId, Template, EventId FROM ' . TABLE_PREFIX . 'EmailMessage WHERE LanguageId = ' . $language_id; $translations = $this->Conn->Query($sql, 'EventId'); foreach ($translations as $event_id => $translation_data) { $parsed = $email_template_helper->parseTemplate($translation_data['Template'], 'html'); $fields_hash = Array ( 'l' . $language_id . '_Subject' => $parsed['Subject'], 'l' . $language_id . '_Body' => $parsed['HtmlBody'], ); if ( $parsed['Headers'] ) { $fields_hash['Headers'] = $parsed['Headers']; } $this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'Events', 'EventId = ' . $event_id); $sql = 'DELETE FROM ' . TABLE_PREFIX . 'EmailMessage WHERE EmailMessageId = ' . $translation_data['EmailMessageId']; $this->Conn->Query($sql); } } } // migrate phrases $temp_table = $this->Application->GetTempName(TABLE_PREFIX . 'Phrase'); $sqls = Array ( 'DROP TABLE IF EXISTS ' . $temp_table, 'CREATE TABLE ' . $temp_table . ' LIKE ' . TABLE_PREFIX . 'Phrase', 'ALTER TABLE ' . $temp_table . ' DROP LanguageId, DROP Translation', 'ALTER IGNORE TABLE ' . $temp_table . ' DROP INDEX LanguageId_2', 'ALTER TABLE ' . $temp_table . ' DROP PhraseId', 'ALTER TABLE ' . $temp_table . ' ADD PhraseId INT NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST', ); foreach ($sqls as $sql) { $this->Conn->Query($sql); } $already_added = Array (); $primary_language_id = $this->Application->GetDefaultLanguageId(); foreach ($languages as $language_id) { $sql = 'SELECT Phrase, PhraseKey, Translation AS l' . $language_id . '_Translation, PhraseType, LastChanged, LastChangeIP, Module FROM ' . TABLE_PREFIX . 'Phrase WHERE LanguageId = ' . $language_id; $phrases = $this->Conn->Query($sql, 'Phrase'); foreach ($phrases as $phrase => $fields_hash) { if (array_key_exists($phrase, $already_added)) { $this->Conn->doUpdate($fields_hash, $temp_table, 'PhraseId = ' . $already_added[$phrase]); } else { $this->Conn->doInsert($fields_hash, $temp_table); $already_added[$phrase] = $this->Conn->getInsertID(); } } // in case some phrases were found in this language, but not in primary language -> copy them if ($language_id != $primary_language_id) { $sql = 'UPDATE ' . $temp_table . ' SET l' . $primary_language_id . '_Translation = l' . $language_id . '_Translation WHERE l' . $primary_language_id . '_Translation IS NULL'; $this->Conn->Query($sql); } } $this->Conn->Query('DROP TABLE IF EXISTS ' . TABLE_PREFIX . 'Phrase'); $this->Conn->Query('RENAME TABLE ' . $temp_table . ' TO ' . TABLE_PREFIX . 'Phrase'); $this->_updateCountryStatesTable(); $this->_replaceConfigurationValueSeparator(); // save "config.php" in php format, not ini format as before $this->_toolkit->SaveConfig(); } if ($mode == 'after') { $this->_transformEmailRecipients(); $this->_fixSkinColors(); } } /** * Makes sure we rename tables to legacy names before doing other upgrades before 5.2.0-B1 upgrade * * @param string $name * @param Array $arguments */ public function __call($name, $arguments) { if ( substr($name, 0, 12) == 'Upgrade_5_1_' && $arguments[0] == 'before' ) { $this->_renameTables('from'); } if ( substr($name, 0, 13) == 'Upgrade_5_2_0' && $arguments[0] == 'before' ) { $this->_renameTables('to'); } } /** * Move country/state translations from Phrase to CountryStates table * */ function _updateCountryStatesTable() { // refactor StdDestinations table $sql = 'RENAME TABLE ' . TABLE_PREFIX . 'StdDestinations TO ' . TABLE_PREFIX . 'CountryStates'; $this->Conn->Query($sql); $sql = 'ALTER TABLE ' . TABLE_PREFIX . 'CountryStates CHANGE DestId CountryStateId INT(11) NOT NULL AUTO_INCREMENT, CHANGE DestType Type INT(11) NOT NULL DEFAULT \'1\', CHANGE DestParentId StateCountryId INT(11) NULL DEFAULT NULL, CHANGE DestAbbr IsoCode CHAR(3) NOT NULL DEFAULT \'\', CHANGE DestAbbr2 ShortIsoCode CHAR(2) NULL DEFAULT NULL, DROP INDEX DestType, DROP INDEX DestParentId, ADD INDEX (`Type`), ADD INDEX (StateCountryId)'; $this->Conn->Query($sql); $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ $ml_helper->createFields('country-state'); $languages = $ml_helper->getLanguages(); foreach ($languages as $language_id) { $sub_select = ' SELECT l' . $language_id . '_Translation FROM ' . TABLE_PREFIX . 'Phrase WHERE Phrase = DestName'; $sql = 'UPDATE ' . TABLE_PREFIX . 'CountryStates SET l' . $language_id . '_Name = (' . $sub_select . ')'; $this->Conn->Query($sql); } $sql = 'ALTER TABLE ' . TABLE_PREFIX . 'CountryStates DROP DestName'; $this->Conn->Query($sql); $sql = 'DELETE FROM ' . TABLE_PREFIX . 'Phrase WHERE Phrase LIKE ' . $this->Conn->qstr('la_country_%') . ' OR Phrase LIKE ' . $this->Conn->qstr('la_state_%'); $this->Conn->Query($sql); } /** * Makes configuration values dropdowns use "||" as separator * */ function _replaceConfigurationValueSeparator() { $custom_field_helper = $this->Application->recallObject('InpCustomFieldsHelper'); /* @var $custom_field_helper InpCustomFieldsHelper */ $sql = 'SELECT ValueList, VariableName FROM ' . TABLE_PREFIX . 'ConfigurationAdmin WHERE ValueList LIKE "%,%"'; $variables = $this->Conn->GetCol($sql, 'VariableName'); foreach ($variables as $variable_name => $value_list) { $ret = Array (); $options = $custom_field_helper->GetValuesHash($value_list, ',', false); foreach ($options as $option_key => $option_title) { if (substr($option_key, 0, 3) == 'SQL') { $ret[] = $option_title; } else { $ret[] = $option_key . '=' . $option_title; } } $fields_hash = Array ( 'ValueList' => implode(VALUE_LIST_SEPARATOR, $ret), ); $this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'ConfigurationAdmin', 'VariableName = ' . $this->Conn->qstr($variable_name)); } } /** * Transforms "FromUserId" into Sender* and Recipients columns * */ function _transformEmailRecipients() { $sql = 'SELECT FromUserId, Type, EventId FROM ' . TABLE_PREFIX . 'Events WHERE FromUserId IS NOT NULL AND (FromUserId <> ' . USER_ROOT . ')'; $events = $this->Conn->Query($sql, 'EventId'); $minput_helper = $this->Application->recallObject('MInputHelper'); /* @var $minput_helper MInputHelper */ foreach ($events as $event_id => $event_data) { $sql = 'SELECT Login FROM ' . TABLE_PREFIX . 'PortalUser WHERE PortalUserId = ' . $event_data['FromUserId']; $username = $this->Conn->GetOne($sql); if (!$username) { continue; } if ($event_data['Type'] == EmailTemplate::TEMPLATE_TYPE_FRONTEND) { // from user $fields_hash = Array ( 'CustomSender' => 1, 'SenderAddressType' => EmailTemplate::ADDRESS_TYPE_USER, 'SenderAddress' => $username ); } if ($event_data['Type'] == EmailTemplate::TEMPLATE_TYPE_ADMIN) { // to user $records = Array ( Array ( 'RecipientType' => EmailTemplate::RECIPIENT_TYPE_TO, 'RecipientName' => '', 'RecipientAddressType' => EmailTemplate::ADDRESS_TYPE_USER, 'RecipientAddress' => $username ) ); $fields_hash = Array ( 'CustomRecipient' => 1, 'Recipients' => $minput_helper->prepareMInputXML($records, array_keys( reset($records) )) ); } $this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'Events', 'EventId = ' . $event_id); } $this->Conn->Query('ALTER TABLE ' . TABLE_PREFIX . 'Events DROP FromUserId'); } /** * Update to 5.1.0; Fixes refferer of form submissions * * @param string $mode when called mode {before, after) */ function Upgrade_5_1_0($mode) { if ( $mode == 'before' ) { $this->_renameTables('from'); } if ( $mode == 'after' ) { $base_url = $this->Application->BaseURL(); $sql = 'UPDATE ' . TABLE_PREFIX . 'FormSubmissions SET ReferrerURL = REPLACE(ReferrerURL, ' . $this->Conn->qstr($base_url) . ', "/")'; $this->Conn->Query($sql); } } /** * Update to 5.1.1-B1; Transforms DisplayToPublic logic * * @param string $mode when called mode {before, after) */ function Upgrade_5_1_1_B1($mode) { if ( $mode == 'before' ) { $this->_renameTables('from'); } if ($mode == 'after') { $this->processDisplayToPublic(); } } function processDisplayToPublic() { $profile_mapping = Array ( 'pp_firstname' => 'FirstName', 'pp_lastname' => 'LastName', 'pp_dob' => 'dob', 'pp_email' => 'Email', 'pp_phone' => 'Phone', 'pp_street' => 'Street', 'pp_city' => 'City', 'pp_state' => 'State', 'pp_zip' => 'Zip', 'pp_country' => 'Country', ); $fields = array_keys($profile_mapping); $fields = $this->Conn->qstrArray($fields); $where_clause = 'VariableName IN (' . implode(',', $fields) . ')'; // 1. get user, that have saved their profile at least once $sql = 'SELECT DISTINCT PortalUserId FROM ' . TABLE_PREFIX . 'PersistantSessionData WHERE ' . $where_clause; $users = $this->Conn->GetCol($sql); foreach ($users as $user_id) { // 2. convert to new format $sql = 'SELECT VariableValue, VariableName FROM ' . TABLE_PREFIX . 'PersistantSessionData WHERE (PortalUserId = ' . $user_id . ') AND ' . $where_clause; $user_variables = $this->Conn->GetCol($sql, 'VariableName'); // go through mapping to preserve variable order $value = Array (); foreach ($profile_mapping as $from_name => $to_name) { if (array_key_exists($from_name, $user_variables) && $user_variables[$from_name]) { $value[] = $to_name; } } if ($value) { $fields_hash = Array ( 'DisplayToPublic' => '|' . implode('|', $value) . '|', ); $this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'PortalUser', 'PortalUserId = ' . $user_id); } // 3. delete old style variables $sql = 'DELETE FROM ' . TABLE_PREFIX . 'PersistantSessionData WHERE (PortalUserId = ' . $user_id . ') AND ' . $where_clause; $this->Conn->Query($sql); } } /** * Update to 5.1.3; Merges column and field phrases * * @param string $mode when called mode {before, after) */ function Upgrade_5_1_3($mode) { if ( $mode == 'before' ) { $this->_renameTables('from'); } if ( $mode == 'after' ) { $this->moveTranslation('LA_COL_', 'LA_FLD_', 'ColumnTranslation'); } } /** * Makes sure table names match upgrade script * * @param string $key * @return void * @access private */ private function _renameTables($key) { foreach ($this->renamedTables as $prefix => $table_info) { $this->Application->getUnitConfig($prefix)->setTableName(TABLE_PREFIX . $table_info[$key]); } } /** * Update to 5.2.0-B1; Transform list sortings storage * * @param string $mode when called mode {before, after) */ public function Upgrade_5_2_0_B1($mode) { if ( $mode == 'before' ) { $this->_renameTables('to'); } if ( $mode == 'after' ) { $this->transformSortings(); $this->moveTranslation('LA_COL_', 'LA_FLD_', 'ColumnTranslation'); // because of "la_col_ItemPrefix" phrase $this->moveTranslation('LA_HINT_', 'LA_FLD_', 'HintTranslation'); $this->moveTranslation('LA_HINT_', 'LA_CONFIG_', 'HintTranslation'); $this->moveTranslation('LA_HINT_', 'LA_TITLE_', 'HintTranslation'); $this->createPageRevisions(); } } /** * Transforms a way, how list sortings are stored * * @return void */ function transformSortings() { $sql = 'SELECT VariableName, PortalUserId FROM ' . TABLE_PREFIX . 'UserPersistentSessionData WHERE VariableName LIKE "%_Sort1.%"'; $sortings = $this->Conn->Query($sql); foreach ($sortings AS $sorting) { if ( !preg_match('/^(.*)_Sort1.(.*)$/', $sorting['VariableName'], $regs) ) { continue; } $user_id = $sorting['PortalUserId']; $prefix_special = $regs[1] . '_'; $view_name = '.' . $regs[2]; $old_variable_names = Array ( $prefix_special . 'Sort1' . $view_name, $prefix_special . 'Sort1_Dir' . $view_name, $prefix_special . 'Sort2' . $view_name, $prefix_special . 'Sort2_Dir' . $view_name, ); $old_variable_names = $this->Conn->qstrArray($old_variable_names); $sql = 'SELECT VariableValue, VariableName FROM ' . TABLE_PREFIX . 'UserPersistentSessionData WHERE PortalUserId = ' . $user_id . ' AND VariableName IN (' . implode(',', $old_variable_names) . ')'; $sorting_data = $this->Conn->GetCol($sql, 'VariableName'); // prepare & save new sortings $new_sorting = Array ( 'Sort1' => $sorting_data[$prefix_special . 'Sort1' . $view_name], 'Sort1_Dir' => $sorting_data[$prefix_special . 'Sort1_Dir' . $view_name], ); if ( isset($sorting_data[$prefix_special . 'Sort2' . $view_name]) ) { $new_sorting['Sort2'] = $sorting_data[$prefix_special . 'Sort2' . $view_name]; $new_sorting['Sort2_Dir'] = $sorting_data[$prefix_special . 'Sort2_Dir' . $view_name]; } $fields_hash = Array ( 'PortalUserId' => $user_id, 'VariableName' => $prefix_special . 'Sortings' . $view_name, 'VariableValue' => serialize($new_sorting), ); $this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'UserPersistentSessionData'); // delete sortings, that were already processed $sql = 'DELETE FROM ' . TABLE_PREFIX . 'UserPersistentSessionData WHERE PortalUserId = ' . $user_id . ' AND VariableName IN (' . implode(',', $old_variable_names) . ')'; $this->Conn->Query($sql); } } /** * Merges several phrases into one (e.g. la_col_ + la_hint_ into designated columns of la_fld_ phrases) * * @param string $source_prefix * @param string $target_prefix * @param string $db_column * @return void * @access protected */ public function moveTranslation($source_prefix, $target_prefix, $db_column) { $source_phrases = $this->getPhrasesByMask($source_prefix . '%'); $target_phrases = $this->getPhrasesByMask($target_prefix . '%'); $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ $delete_ids = Array (); $ml_helper->createFields('phrases'); $languages = $ml_helper->getLanguages(); $phrase_table = $this->Application->getUnitConfig('phrases')->getTableName(); foreach ($source_phrases as $phrase_key => $phrase_info) { $target_phrase_key = $target_prefix . substr($phrase_key, strlen($source_prefix)); if ( !isset($target_phrases[$target_phrase_key]) ) { continue; } $fields_hash = Array (); // copy column phrase main translation into field phrase column translation foreach ($languages as $language_id) { $fields_hash['l' . $language_id . '_' . $db_column] = $phrase_info['l' . $language_id . '_Translation']; } $delete_ids[] = $phrase_info['PhraseId']; $this->Conn->doUpdate($fields_hash, $phrase_table, 'PhraseId = ' . $target_phrases[$target_phrase_key]['PhraseId']); } // delete all column phrases, that were absorbed by field phrases if ( $delete_ids ) { $sql = 'DELETE FROM ' . $phrase_table . ' WHERE PhraseId IN (' . implode(',', $delete_ids) . ')'; $this->Conn->Query($sql); $sql = 'DELETE FROM ' . TABLE_PREFIX . 'PhraseCache'; $this->Conn->Query($sql); } } /** * Returns phrases by mask * * @param string $mask * @return Array * @access protected */ protected function getPhrasesByMask($mask) { $sql = 'SELECT * FROM ' . $this->Application->getUnitConfig('phrases')->getTableName() . ' WHERE PhraseKey LIKE ' . $this->Conn->qstr($mask); return $this->Conn->Query($sql, 'PhraseKey'); } protected function createPageRevisions() { $sql = 'SELECT DISTINCT PageId FROM ' . TABLE_PREFIX . 'PageContent'; $page_ids = $this->Conn->GetCol($sql); foreach ($page_ids as $page_id) { $fields_hash = Array ( 'PageId' => $page_id, 'RevisionNumber' => 1, 'IsDraft' => 0, 'FromRevisionId' => 0, 'CreatedById' => USER_ROOT, 'CreatedOn' => time(), 'Status' => STATUS_ACTIVE, ); $this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'PageRevisions'); $fields_hash = Array ( 'RevisionId' => $this->Conn->getInsertID(), ); $this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'PageContent', 'PageId = ' . $page_id); } } /** * Update to 5.2.0-B3; Introduces separate field for plain-text e-mail event translations * * @param string $mode when called mode {before, after) */ public function Upgrade_5_2_0_B3($mode) { if ( $mode == 'before' ) { $this->_renameTables('to'); } if ( $mode == 'after' ) { $this->_splitEmailBody(); $this->_migrateCommonFooter(); } } /** * Splits e-mail body into HTML and Text fields * * @return void * @access private */ private function _splitEmailBody() { $config = $this->Application->getUnitConfig('email-template'); $id_field = $config->getIDField(); $table_name = $config->getTableName(); $fields = $this->Conn->Query('DESCRIBE ' . $table_name, 'Field'); + // The "_renameTables" method doesn't rename IDField, so find real one in DESCRIBE result. + if ( !isset($fields[$id_field]) ) { + $id_field = 'EventId'; + } + if ( !isset($fields['l1_Body']) ) { // column dropped - nothing to convert anymore return; } $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ $languages = $ml_helper->getLanguages(); $ml_helper->createFields('email-template'); $sql = 'SELECT * FROM ' . $table_name; $email_events = $this->Conn->Query($sql); // 1. move data to new columns foreach ($email_events as $email_event) { $fields_hash = Array (); $translation_field = $email_event['MessageType'] == 'html' ? 'HtmlBody' : 'PlainTextBody'; foreach ($languages as $language_id) { $fields_hash['l' . $language_id . '_' . $translation_field] = $email_event['l' . $language_id . '_Body']; } if ( $fields_hash ) { $this->Conn->doUpdate($fields_hash, $table_name, $id_field . ' = ' . $email_event[$id_field]); } } // 2. drop old columns $drops = Array ('DROP COLUMN MessageType'); foreach ($languages as $language_id) { $lang_field = 'l' . $language_id . '_Body'; if ( isset($fields[$lang_field]) ) { $drops[] = 'DROP COLUMN ' . $lang_field; } } $this->Conn->Query('ALTER TABLE ' . $table_name . ' ' . implode(', ', $drops)); } /** * Transforms COMMON.FOOTER e-mail event into new field in Languages table */ private function _migrateCommonFooter() { $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ $languages = $ml_helper->getLanguages(); $event_table = $this->Application->getUnitConfig('email-template')->getTableName(); $sql = 'SELECT * FROM ' . $event_table . ' WHERE Event = "COMMON.FOOTER"'; $footer_data = $this->Conn->GetRow($sql); if ( !$footer_data ) { return; } $primary_language_id = $this->Application->GetDefaultLanguageId(); $table_name = $this->Application->getUnitConfig('lang')->getTableName(); foreach ($languages as $language_id) { $is_primary = $language_id == $primary_language_id; $fields_hash = Array ( 'HtmlEmailTemplate' => $this->_appendEmailDesignBody($footer_data['l' . $language_id . '_HtmlBody'], $is_primary), 'TextEmailTemplate' => $this->_appendEmailDesignBody($footer_data['l' . $language_id . '_PlainTextBody'], $is_primary), ); $this->Conn->doUpdate($fields_hash, $table_name, 'LanguageId = ' . $language_id); } $sql = 'DELETE FROM ' . $event_table . ' WHERE EventId = ' . $footer_data['EventId']; $this->Conn->Query($sql); } /** * Adds "$body" to given string * * @param string $string * @param bool $is_primary for primary language * @return string * @access private */ private function _appendEmailDesignBody($string, $is_primary) { if ( !$string ) { return $is_primary ? '$body' : $string; } return '$body' . "\n" . str_replace(Array ("\r\n", "\r"), "\n", $string); } /** * Update to 5.2.0-RC1 * * @param string $mode when called mode {before, after) */ public function Upgrade_5_2_0_RC1($mode) { if ( $mode != 'before' ) { return; } $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ // make some promo block fields translatable $ml_helper->createFields('promo-block'); $table_name = $this->Application->getUnitConfig('promo-block')->getTableName(); $table_structure = $this->Conn->Query('DESCRIBE ' . $table_name, 'Field'); if ( isset($table_structure['Title']) ) { $sql = 'UPDATE ' . $table_name . ' SET l' . $this->Application->GetDefaultLanguageId() . '_Title = Title'; $this->Conn->Query($sql); $sql = 'ALTER TABLE ' . $table_name . ' DROP Title'; $this->Conn->Query($sql); } // fix e-mail event translations $languages = $ml_helper->getLanguages(); $table_name = $this->Application->getUnitConfig('email-template')->getTableName(); $change_fields = Array ('Subject', 'HtmlBody', 'PlainTextBody'); foreach ($languages as $language_id) { foreach ($change_fields as $change_field) { $change_field = 'l' . $language_id . '_' . $change_field; $sql = "UPDATE " . $table_name . " SET {$change_field} = REPLACE({$change_field}, '<inp2:m_BaseUrl/>', '<inp2:m_Link template=\"index\"/>') WHERE {$change_field} LIKE '%m_BaseURL%'"; $this->Conn->Query($sql); } } // add new ml columns to phrases/e-mail events $ml_helper->createFields('phrases'); $ml_helper->createFields('email-template'); } /** * Update to 5.2.0 * * @param string $mode when called mode {before, after) */ public function Upgrade_5_2_0($mode) { if ( $mode != 'after' ) { return; } $table_name = $this->Application->getUnitConfig('c')->getTableName(); $sql = 'SELECT NamedParentPath, CachedTemplate, CategoryId FROM ' . $table_name; $categories = $this->Conn->GetIterator($sql); foreach ($categories as $category_data) { $fields_hash = Array ( 'NamedParentPathHash' => kUtil::crc32(mb_strtolower(preg_replace('/^Content\//i', '', $category_data['NamedParentPath']))), 'CachedTemplateHash' => kUtil::crc32(mb_strtolower($category_data['CachedTemplate'])), ); $this->Conn->doUpdate($fields_hash, $table_name, 'CategoryId = ' . $category_data['CategoryId']); } $rebuild_mode = $this->Application->ConfigValue('QuickCategoryPermissionRebuild') ? CategoryPermissionRebuild::SILENT : CategoryPermissionRebuild::AUTOMATIC; $this->Application->SetConfigValue('CategoryPermissionRebuildMode', $rebuild_mode); $sql = 'DELETE FROM ' . TABLE_PREFIX . 'SystemSettings WHERE VariableName = "QuickCategoryPermissionRebuild"'; $this->Conn->Query($sql); $this->_updateScheduledTaskRunSchedule(); } /** * Transforms RunInterval into RunSchedule column for Scheduled Tasks * * @return void * @access protected */ protected function _updateScheduledTaskRunSchedule() { // minute hour day_of_month month day_of_week $config = $this->Application->getUnitConfig('scheduled-task'); $id_field = $config->getIDField(); $table_name = $config->getTableName(); $sql = 'SELECT RunInterval, ' . $id_field . ' FROM ' . $table_name; $run_intervals = $this->Conn->GetCol($sql, $id_field); $ranges = Array (0 => 'min', 1 => 'hour', 2 => 'day', 3 => 'month'); $range_values = Array ('min' => 60, 'hour' => 60, 'day' => 24, 'month' => 30); $range_masks = Array ('min' => '*/%s * * * *', 'hour' => '0 */%s * * *', 'day' => '0 0 */%s * *', 'month' => '0 0 1 */%s *'); foreach ($run_intervals as $scheduled_task_id => $interval) { $mask_index = 'month'; foreach ($ranges as $range_index => $range_name) { $range_value = $range_values[$range_name]; if ( $interval >= $range_value ) { $interval = ceil($interval / $range_value); } else { $mask_index = $ranges[$range_index - 1]; break; } } $run_schedule = sprintf($range_masks[$mask_index], $interval); if ( $run_schedule == '0 0 */7 * *' ) { // once in 7 days = once in a week $run_schedule = '0 0 * * 0'; } $run_schedule = preg_replace('/(\*\/1( |$))/', '*\\2', $run_schedule); $fields_hash = Array ('RunSchedule' => $run_schedule); $this->Conn->doUpdate($fields_hash, $table_name, $id_field . ' = ' . $scheduled_task_id); } // drop RunInterval column $this->Conn->Query('ALTER TABLE ' . $table_name . ' DROP RunInterval'); } /** * Update to 5.2.1-B1 * * @param string $mode when called mode {before, after) */ public function Upgrade_5_2_1_B1($mode) { if ( $mode != 'after' ) { return; } $this->_updateUserPasswords(); } protected function _updateUserPasswords() { $user_table = $this->Application->getUnitConfig('u')->getTableName(); $sql = 'SELECT Password, PortalUserId FROM ' . $user_table . ' WHERE PasswordHashingMethod = ' . PasswordHashingMethod::MD5; $user_passwords = $this->Conn->GetColIterator($sql, 'PortalUserId'); if ( !count($user_passwords) ) { // no users at all or existing users have converted passwords already return; } kUtil::setResourceLimit(); $password_formatter = $this->Application->recallObject('kPasswordFormatter'); /* @var $password_formatter kPasswordFormatter */ foreach ($user_passwords as $user_id => $user_password) { $fields_hash = Array ( 'Password' => $password_formatter->hashPassword($user_password, '', PasswordHashingMethod::MD5_AND_PHPPASS), 'PasswordHashingMethod' => PasswordHashingMethod::MD5_AND_PHPPASS, ); $this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'Users', 'PortalUserId = ' . $user_id); } } /** + * Update to 5.2.2-B1 + * + * @param string $mode when called mode {before, after) + */ + public function Upgrade_5_2_2_B1($mode) + { + if ( $mode != 'after' ) { + return; + } + + $this->deleteThumbnails(); + } + + /** + * Deletes folders, containing thumbnails recursively. + * + * @param string $folder Folder. + * + * @return void + */ + protected function deleteThumbnails($folder = WRITEABLE) + { + foreach ( glob($folder . '/*', GLOB_ONLYDIR) as $sub_folder ) { + if ( $sub_folder === WRITEABLE . '/cache' ) { + continue; + } + + if ( basename($sub_folder) === 'resized' ) { + $files = glob($sub_folder . '/*'); + array_map('unlink', $files); + rmdir($sub_folder); + } + else { + $this->deleteThumbnails($sub_folder); + } + } + } + + /** * Update to 5.3.0-B1 * * @param string $mode when called mode {before, after) */ public function Upgrade_5_3_0_B1($mode) { if ( $mode == 'before' ) { $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ // add new ml columns to phrases/e-mail events $ml_helper->createFields('phrases'); $ml_helper->createFields('email-template'); } elseif ( $mode == 'after' ) { $sql = 'SELECT Event, ScheduledTaskId FROM ' . TABLE_PREFIX . 'ScheduledTasks'; $scheduled_tasks = $this->Conn->GetCol($sql, 'ScheduledTaskId'); foreach ( $scheduled_tasks as $id => $event_string ) { $event = new kEvent($event_string); $module = $event->getUnitConfig()->getModule(); $this->Conn->doUpdate( array('Module' => $module), TABLE_PREFIX . 'ScheduledTasks', 'ScheduledTaskId = ' . $id ); } $this->updateDeploymentLog(); $this->migrateSSLSettings(); $this->migrateSSLSiteDomains(); } } /** * Upgrades project deployment log. * * @return void */ protected function updateDeploymentLog() { $log = $this->Application->recallObject('module-deployment-log', null, array('skip_autoload' => true)); /* @var $log kDBItem */ $table_name = $this->Application->getUnitConfig('mod')->getTableName(); $structure = $this->Conn->Query('DESCRIBE ' . $table_name, 'Field'); if ( !isset($structure['AppliedDBRevisions']) ) { return; } $sql = 'SELECT Name, Path, AppliedDBRevisions FROM ' . $table_name . ' WHERE COALESCE(AppliedDBRevisions, "") <> ""'; $modules = $this->Conn->Query($sql, 'Name'); foreach ( $modules as $module_name => $module_data ) { $filename = FULL_PATH . DIRECTORY_SEPARATOR . $module_data['Path'] . 'install/project_upgrades.sql'; if ( !file_exists($filename) ) { continue; } $sqls = file_get_contents($filename); preg_match_all("/# r([\d]+)([^\:]*):(.*?)(\n|$)/s", $sqls, $matches, PREG_SET_ORDER + PREG_OFFSET_CAPTURE); if ( !$matches ) { continue; } $revisions = array_flip(explode(',', $module_data['AppliedDBRevisions'])); foreach ( $matches as $match ) { $revision_number = $match[1][0]; if ( !isset($revisions[$revision_number]) ) { continue; } $log->Clear(); $log->SetDBField('Module', $module_name); $log->SetDBField('RevisionNumber', $revision_number); $log->SetDBField('RevisionTitle', trim($match[3][0])); $log->SetDBField('IPAddress', '0.0.0.0'); $log->SetDBField('Output', 'IMPORTED'); $log->Create(); } } $sql = 'ALTER TABLE ' . $table_name . ' DROP AppliedDBRevisions'; $this->Conn->Query($sql); } /** * Migrates SSL settings to new format. * * @return void */ protected function migrateSSLSettings() { $mapping = array( 'SSL_URL' => 'SSLDomain', 'AdminSSL_URL' => 'AdminSSLDomain', ); foreach ( $mapping as $old_setting => $new_setting ) { $old_value = $this->Application->ConfigValue($old_setting); if ( $old_value ) { $this->Application->SetConfigValue($new_setting, parse_url($old_value, PHP_URL_HOST)); } $sql = 'DELETE FROM ' . $this->Application->getUnitConfig('conf')->getTableName() . ' WHERE VariableName = ' . $this->Conn->qstr($old_setting); $this->Conn->Query($sql); } } /** * Migrates SSL site domains. * * @return void */ protected function migrateSSLSiteDomains() { /** @var kDBItem $object */ $object = $this->Application->recallObject('site-domain.migrate', null, array('skip_autoload' => true)); $sql = 'SELECT * FROM ' . $this->Application->getUnitConfig('site-domain')->getTableName() . ' WHERE SSLDomainName <> ""'; $site_domains = $this->Conn->Query($sql); foreach ( $site_domains as $site_domain ) { if ( strpos($site_domain['SSLDomainName'], '//') === false ) { continue; } // use object to reset domain cache as well $object->LoadFromHash($site_domain); $object->SetDBField('SSLDomainName', parse_url($site_domain['SSLDomainName'], PHP_URL_HOST)); $object->Update(); } } } Index: branches/5.3.x/core/install/upgrades.sql =================================================================== --- branches/5.3.x/core/install/upgrades.sql (revision 16394) +++ branches/5.3.x/core/install/upgrades.sql (revision 16395) @@ -1,3041 +1,3050 @@ # ===== 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.2.2-B1 ===== UPDATE LanguageLabels SET l1_Translation = 'Incorrect data format, please use {type}' WHERE PhraseKey = 'LA_ERR_BAD_TYPE'; UPDATE LanguageLabels SET l1_Translation = 'Field value is out of range, possible values from {min_value} to {max_value}' WHERE PhraseKey = 'LA_ERR_VALUE_OUT_OF_RANGE'; UPDATE LanguageLabels SET l1_Translation = 'Field value length is out of range, possible value length from {min_length} to {max_length}' WHERE PhraseKey = 'LA_ERR_LENGTH_OUT_OF_RANGE'; ALTER TABLE Themes ADD StylesheetFile VARCHAR( 255 ) NOT NULL DEFAULT ''; UPDATE Themes SET StylesheetFile = 'platform/inc/styles.css' WHERE `Name` = 'advanced'; +UPDATE EmailTemplates +SET l1_Subject = REPLACE(l1_Subject, "Field name='Username'", 'UserTitle'), l1_HtmlBody = REPLACE(l1_HtmlBody, "Field name='Username'", 'UserTitle') +WHERE TemplateName LIKE 'USER%'; +UPDATE EmailTemplates +SET l1_Subject = REPLACE(l1_Subject, 'Field name="Username"', 'UserTitle'), l1_HtmlBody = REPLACE(l1_HtmlBody, 'Field name="Username"', 'UserTitle') +WHERE TemplateName LIKE 'USER%'; + +UPDATE SystemSettings SET VariableValue = 1 WHERE VariableName = 'CSVExportEncoding'; + # ===== 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 SystemCache (VarName, Data) SELECT 'tmp_translation' AS VarName, l<%PRIMARY_LANGUAGE%>_Translation AS Data FROM <%TABLE_PREFIX%>LanguageLabels WHERE PhraseKey = 'LC_IMPORTLANG_PHRASEWARNING'; UPDATE 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_.'; UPDATE Modules SET ClassNamespace = 'InPortal\\Core' WHERE `Name` IN ('Core', 'In-Portal'); UPDATE EmailTemplates SET l1_Subject = REPLACE(l1_Subject, '<inp2:u_', '<inp2:'), l1_HtmlBody = REPLACE(l1_HtmlBody, '<inp2:u_', '<inp2:'), l1_PlainTextBody = REPLACE(l1_PlainTextBody, '<inp2:u_', '<inp2:') WHERE TemplateName LIKE 'USER.MEMBERSHIP%'; ALTER TABLE SiteDomains ADD `Status` TINYINT NOT NULL DEFAULT '1' AFTER Priority, ADD INDEX (`Status`); INSERT INTO SystemSettings VALUES(DEFAULT, 'SessionLogRotationInterval', '2629800', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsLogs', 'la_config_SessionLogRotationInterval', 'select', '', '86400=la_opt_OneDay||604800=la_opt_OneWeek||1209600=la_opt_TwoWeeks||2629800=la_opt_OneMonth||7889400=la_opt_ThreeMonths||31557600=la_opt_OneYear||63115200=la_opt_TwoYears||94672800=la_opt_ThreeYears||157788000=la_opt_FiveYears||-1=la_opt_EmailLogKeepForever', 65.05, 0, 1, NULL); Index: branches/5.3.x/core/install/english.lang =================================================================== --- branches/5.3.x/core/install/english.lang (revision 16394) +++ branches/5.3.x/core/install/english.lang (revision 16395) @@ -1,2251 +1,2251 @@ <?xml version="1.0" encoding="utf-8"?> <LANGUAGES Version="6"> <LANGUAGE Encoding="base64" PackName="English" LocalName="English" DateFormat="m/d/Y" ShortDateFormat="m/d" TimeFormat="g:i A" ShortTimeFormat="g:i A" InputDateFormat="m/d/Y" InputTimeFormat="g:i:s A" DecimalPoint="." ThousandSep="," UnitSystem="2" Locale="en-US" UserDocsUrl="http://docs.in-portal.org/eng/index.php"> <EMAILDESIGNS> <HTML>JGJvZHkNCjxici8+PGJyLz4NCg0KU2luY2VyZWx5LDxici8+PGJyLz4NCg0KV2Vic2l0ZSBhZG1pbmlzdHJhdGlvbi4NCg0KPCEtLSMjIDxpbnAyOmVtYWlsLWxvZ19JdGVtTGluayB0ZW1wbGF0ZT0icGxhdGZvcm0vbXlfYWNjb3VudC9lbWFpbCIvPiAjIy0tPg==</HTML> </EMAILDESIGNS> <PHRASES> <PHRASE Label="la_Active" Module="Core" Type="1">QWN0aXZl</PHRASE> <PHRASE Label="la_Add" Module="Core" Type="1">QWRk</PHRASE> <PHRASE Label="la_AddTo" Module="Core" Type="1">QWRkIFRv</PHRASE> <PHRASE Label="la_AdministrativeConsole" Module="Core" Type="1">QWRtaW5pc3RyYXRpdmUgQ29uc29sZQ==</PHRASE> <PHRASE Label="la_AllowChangingAdminConsoleInterface" Module="Core" Type="1">YWxsb3cgY2hhbmdpbmc=</PHRASE> <PHRASE Label="la_AllowDeleteRootCats" Module="Core" Type="1">QWxsb3cgZGVsZXRpbmcgTW9kdWxlIFJvb3QgU2VjdGlvbg==</PHRASE> <PHRASE Label="la_alt_Browse" Module="Core" Type="1">VmlldyBpbiBCcm93c2UgTW9kZQ==</PHRASE> <PHRASE Label="la_alt_GoInside" Module="Core" Type="1">R28gSW5zaWRl</PHRASE> <PHRASE Label="la_Always" Module="Core" Type="1">QWx3YXlz</PHRASE> <PHRASE Label="la_and" Module="Core" Type="1">YW5k</PHRASE> <PHRASE Label="la_Auto" Module="Core" Type="1">QXV0bw==</PHRASE> <PHRASE Label="la_Automatic" Module="Core" Type="1">QXV0b21hdGlj</PHRASE> <PHRASE Label="la_AvailableColumns" Module="Core" Type="1">QXZhaWxhYmxlIENvbHVtbnM=</PHRASE> <PHRASE Label="la_AvailableItems" Module="Core" Type="1">QXZhaWxhYmxlIEl0ZW1z</PHRASE> <PHRASE Label="la_Background" Module="Core" Type="1">QmFja2dyb3VuZA==</PHRASE> <PHRASE Label="la_Borders" Module="Core" Type="1">Qm9yZGVycw==</PHRASE> <PHRASE Label="la_btn_Add" Module="Core" Type="1">QWRk</PHRASE> <PHRASE Label="la_btn_AdminEditItem" Module="Core" Type="1">RWRpdCBJdGVt</PHRASE> <PHRASE Label="la_btn_BrowseMode" Module="Core" Type="1">QnJvd3NlIE1vZGU=</PHRASE> <PHRASE Label="la_btn_Cancel" Module="Core" Type="1">Q2FuY2Vs</PHRASE> <PHRASE Label="la_btn_Change" Module="Core" Type="1">Q2hhbmdl</PHRASE> <PHRASE Label="la_btn_Clear" Module="Core" Type="1">Q2xlYXI=</PHRASE> <PHRASE Label="la_btn_ContentMode" Module="Core" Type="1">Q29udGVudCBNb2Rl</PHRASE> <PHRASE Label="la_btn_Delete" Module="Core" Type="1">RGVsZXRl</PHRASE> <PHRASE Label="la_btn_DeleteDraft" Module="Core" Type="1">RGVsZXRl</PHRASE> <PHRASE Label="la_btn_DeleteReview" Module="Core" Type="1">ZGVsZXRlIHJldmlldw==</PHRASE> <PHRASE Label="la_btn_Deploy" Module="Core" Type="1">RGVwbG95</PHRASE> <PHRASE Label="la_btn_DesignMode" Module="Core" Type="1">RGVzaWduIE1vZGU=</PHRASE> <PHRASE Label="la_btn_Down" Module="Core" Type="1">RG93bg==</PHRASE> <PHRASE Label="la_btn_Edit" Module="Core" Type="1">RWRpdA==</PHRASE> <PHRASE Label="la_btn_EditBlock" Module="Core" Type="1">RWRpdCBCbG9jaw==</PHRASE> <PHRASE Label="la_btn_EditContent" Module="Core" Type="1">RWRpdCBDb250ZW50</PHRASE> <PHRASE Label="la_btn_EditDesign" Module="Core" Type="1">RWRpdCBEZXNpZ24=</PHRASE> <PHRASE Label="la_btn_Generate" Module="Core" Type="1">R2VuZXJhdGU=</PHRASE> <PHRASE Label="la_btn_GeneratePage" Module="Core" Type="1">R2VuZXJhdGUgUGFnZQ==</PHRASE> <PHRASE Label="la_btn_GetValue" Module="Core" Type="1">R2V0IFZhbHVl</PHRASE> <PHRASE Label="la_btn_Locate" Module="Core" Type="1">TG9jYXRl</PHRASE> <PHRASE Label="la_btn_MoveDown" Module="Core" Type="1">TW92ZSBEb3du</PHRASE> <PHRASE Label="la_btn_MoveUp" Module="Core" Type="1">TW92ZSBVcA==</PHRASE> <PHRASE Label="la_btn_PublishingTools" Module="Core" Type="1">UHVibGlzaGluZyBUb29scw==</PHRASE> <PHRASE Label="la_btn_Rebuild" Module="Core" Type="1">UmVidWlsZA==</PHRASE> <PHRASE Label="la_btn_Recompile" Module="Core" Type="1">UmVjb21waWxl</PHRASE> <PHRASE Label="la_btn_Refresh" Module="Core" Type="1">UmVmcmVzaA==</PHRASE> <PHRASE Label="la_btn_Reset" Module="Core" Type="1">UmVzZXQ=</PHRASE> <PHRASE Label="la_btn_ResetAndValidateConfigFiles" Module="Core" Type="1">UmVzZXQgJmFtcDsgVmFsaWRhdGUgQ29uZmlnIEZpbGVz</PHRASE> <PHRASE Label="la_btn_ResetRootPassword" Module="Core" Type="1">UmVzZXQgInJvb3QiIHBhc3N3b3Jk</PHRASE> <PHRASE Label="la_btn_Save" Module="Core" Type="1">U2F2ZQ==</PHRASE> <PHRASE Label="la_btn_SaveChanges" Module="Core" Type="1">U2F2ZSBDaGFuZ2Vz</PHRASE> <PHRASE Label="la_btn_SectionProperties" Module="Core" Type="1">U2VjdGlvbiBQcm9wZXJ0aWVz</PHRASE> <PHRASE Label="la_btn_SectionTemplate" Module="Core" Type="1">U2VjdGlvbiBUZW1wbGF0ZQ==</PHRASE> <PHRASE Label="la_btn_SelectAll" Module="Core" Type="1">U2VsZWN0IEFsbA==</PHRASE> <PHRASE Label="la_btn_SetValue" Module="Core" Type="1">U2V0IFZhbHVl</PHRASE> <PHRASE Label="la_btn_ShowStructure" Module="Core" Type="1">U2hvdyBTdHJ1Y3R1cmU=</PHRASE> <PHRASE Label="la_btn_Synchronize" Module="Core" Type="1">U3luY2hyb25pemU=</PHRASE> <PHRASE Label="la_btn_Unselect" Module="Core" Type="1">VW5zZWxlY3Q=</PHRASE> <PHRASE Label="la_btn_Up" Module="Core" Type="1">VXA=</PHRASE> <PHRASE Label="la_btn_UseDraft" Module="Core" Type="1">VXNl</PHRASE> <PHRASE Label="la_By" Module="Core" Type="1">Ynk=</PHRASE> <PHRASE Label="la_Cancel" Module="Core" Type="1">Q2FuY2Vs</PHRASE> <PHRASE Label="la_category" Module="Core" Type="1">U2VjdGlvbg==</PHRASE> <PHRASE Label="la_category_daysnew_prompt" Module="Core" Type="1">TnVtYmVyIG9mIGRheXMgZm9yIGEgY2F0LiB0byBiZSBORVc=</PHRASE> <PHRASE Label="la_category_metadesc" Module="Core" Type="1">RGVmYXVsdCBNRVRBIGRlc2NyaXB0aW9u</PHRASE> <PHRASE Label="la_category_metakey" Module="Core" Type="1">RGVmYXVsdCBNRVRBIEtleXdvcmRz</PHRASE> <PHRASE Label="la_category_perpage_prompt" Module="Core" Type="1">TnVtYmVyIG9mIHNlY3Rpb25zIHBlciBwYWdl</PHRASE> <PHRASE Label="la_category_perpage__short_prompt" Module="Core" Type="1">U2VjdGlvbnMgUGVyIFBhZ2UgKFNob3J0bGlzdCk=</PHRASE> <PHRASE Label="la_category_showpick_prompt" Module="Core" Type="1">RGlzcGxheSBlZGl0b3IgUElDS3MgYWJvdmUgcmVndWxhciBzZWN0aW9ucw==</PHRASE> <PHRASE Label="la_category_sortfield2_prompt" Module="Core" Type="1">QW5kIHRoZW4gYnk=</PHRASE> <PHRASE Label="la_category_sortfield_prompt" Module="Core" Type="1">T3JkZXIgc2VjdGlvbnMgYnk=</PHRASE> <PHRASE Label="la_Close" Module="Core" Type="1">Q2xvc2U=</PHRASE> <PHRASE Label="la_col_Access" Module="Core" Type="1">QWNjZXNz</PHRASE> <PHRASE Label="la_col_AdditionalPermissions" Module="Core" Type="1">QWRkaXRpb25hbA==</PHRASE> <PHRASE Label="la_col_AffectedItems" Module="Core" Type="1">QWZmZWN0ZWQgSXRlbXM=</PHRASE> <PHRASE Label="la_col_AltName" Module="Core" Type="1">QWx0IFZhbHVl</PHRASE> <PHRASE Label="la_col_BuildDate" Module="Core" Type="1">QnVpbGQgRGF0ZQ==</PHRASE> <PHRASE Label="la_col_CategoryName" Module="Core" Type="1">U2VjdGlvbiBOYW1l</PHRASE> <PHRASE Label="la_col_ColumnPhrase" Module="Core" Type="1">Q29sdW1uIFBocmFzZQ==</PHRASE> <PHRASE Label="la_col_Effective" Module="Core" Type="1">RWZmZWN0aXZl</PHRASE> <PHRASE Label="la_col_EmailTemplates" Module="Core" Type="1">RS1tYWlsIFRlbXBsYXRlcw==</PHRASE> <PHRASE Label="la_col_EnableEmailCommunication" Module="Core" Type="1">RW5hYmxlIEUtbWFpbCBDb21tdW5pY2F0aW9u</PHRASE> <PHRASE Label="la_col_Error" Module="Core" Type="1">Jm5ic3A7</PHRASE> <PHRASE Label="la_col_EventDescription" Module="Core" Type="1">RXZlbnQgRGVzY3JpcHRpb24=</PHRASE> <PHRASE Label="la_col_EventParams" Module="Core" Type="1">RXZlbnQgUGFyYW1z</PHRASE> <PHRASE Label="la_col_HintPhrase" Module="Core" Type="1">SGludCBQaHJhc2U=</PHRASE> <PHRASE Label="la_col_ImageEnabled" Module="Core" Type="1">U3RhdHVz</PHRASE> <PHRASE Label="la_col_ImageName" Module="Core" Type="1">SW1hZ2U=</PHRASE> <PHRASE Label="la_col_ImageUrl" Module="Core" Type="1">VVJM</PHRASE> <PHRASE Label="la_col_Inherited" Module="Core" Type="1">SW5oZXJpdGVk</PHRASE> <PHRASE Label="la_col_InheritedFrom" Module="Core" Type="1">SW5oZXJpdGVkIEZyb20=</PHRASE> <PHRASE Label="la_col_InMenu" Module="Core" Type="1">SW4gTWVudQ==</PHRASE> <PHRASE Label="la_col_IP" Module="Core" Type="1">SVAgQWRkcmVzcw==</PHRASE> <PHRASE Label="la_col_IsPopular" Module="Core" Type="1">UG9wdWxhcg==</PHRASE> <PHRASE Label="la_col_IsPrimaryLanguage" Module="Core" Type="1">VXNlciBQcmltYXJ5</PHRASE> <PHRASE Label="la_col_Keyword" Module="Core" Type="1">S2V5d29yZA==</PHRASE> <PHRASE Label="la_col_Label" Module="Core" Type="1">TGFiZWw=</PHRASE> <PHRASE Label="la_col_LanguagePackInstalled" Module="Core" Type="1">TGFuZ3VhZ2UgUGFjayBJbnN0YWxsZWQ=</PHRASE> <PHRASE Label="la_col_LastChanged" Module="Core" Type="1">TGFzdCBDaGFuZ2Vk</PHRASE> <PHRASE Label="la_col_LastCompiled" Module="Core" Type="1">TGFzdCBDb21waWxlZA==</PHRASE> <PHRASE Label="la_col_LastSendRetry" Module="Core" Type="1">TGFzdCBBdHRlbXB0</PHRASE> <PHRASE Label="la_col_LinkUrl" Module="Core" Type="1">TGluayBVUkw=</PHRASE> <PHRASE Label="la_col_MailingList" Module="Core" Type="1">TWFpbGluZyBMaXN0</PHRASE> <PHRASE Label="la_col_MembershipExpires" Module="Core" Type="1">TWVtYmVyc2hpcCBFeHBpcmVz</PHRASE> <PHRASE Label="la_col_MessageHeaders" Module="Core" Type="1">TWVzc2FnZSBIZWFkZXJz</PHRASE> <PHRASE Label="la_col_MessageHtml" Module="Core" Type="1">SFRNTA==</PHRASE> <PHRASE Label="la_col_OriginalValue" Module="Core" Type="1">T3JpZ2luYWwgVmFsdWU=</PHRASE> <PHRASE Label="la_col_Path" Module="Core" Type="1">UGF0aA==</PHRASE> <PHRASE Label="la_col_PermAdd" Module="Core" Type="1">QWRk</PHRASE> <PHRASE Label="la_col_PermDelete" Module="Core" Type="1">RGVsZXRl</PHRASE> <PHRASE Label="la_col_PermEdit" Module="Core" Type="1">RWRpdA==</PHRASE> <PHRASE Label="la_col_PermissionName" Module="Core" Type="1">UGVybWlzc2lvbiBOYW1l</PHRASE> <PHRASE Label="la_col_PermissionValue" Module="Core" Type="1">QWNjZXNz</PHRASE> <PHRASE Label="la_col_PermView" Module="Core" Type="1">Vmlldw==</PHRASE> <PHRASE Label="la_col_Phrases" Module="Core" Type="1">UGhyYXNlcw==</PHRASE> <PHRASE Label="la_col_PortalUserId" Module="Core" Type="1">VXNlciBJRA==</PHRASE> <PHRASE Label="la_col_Preview" Module="Core" Type="1">UHJldmlldw==</PHRASE> <PHRASE Label="la_col_PrimaryGroup" Module="Core" Type="1">UHJpbWFyeSBHcm91cA==</PHRASE> <PHRASE Label="la_col_PrimaryValue" Module="Core" Type="1">UHJpbWFyeSBWYWx1ZQ==</PHRASE> <PHRASE Label="la_col_Prompt" Module="Core" Type="1">RmllbGQgUHJvbXB0</PHRASE> <PHRASE Label="la_col_Queued" Module="Core" Type="1">UXVldWVk</PHRASE> <PHRASE Label="la_col_Referer" Module="Core" Type="1">UmVmZXJlcg==</PHRASE> <PHRASE Label="la_col_ResetToDefaultSorting" Module="Core" Type="1">UmVzZXQgdG8gZGVmYXVsdA==</PHRASE> <PHRASE Label="la_col_ReviewCount" Module="Core" Type="1">Q29tbWVudHM=</PHRASE> <PHRASE Label="la_col_ReviewedBy" Module="Core" Type="1">Q3JlYXRlZCBieQ==</PHRASE> <PHRASE Label="la_col_ScheduleFromDate" Module="Core" Type="1">U2NoZWR1bGUgRnJvbSBEYXRl</PHRASE> <PHRASE Label="la_col_ScheduleToDate" Module="Core" Type="1">U2NoZWR1bGUgVG8gRGF0ZQ==</PHRASE> <PHRASE Label="la_col_SendRetries" Module="Core" Type="1">QXR0ZW1wdHMg</PHRASE> <PHRASE Label="la_col_SessionEnd" Module="Core" Type="1">U2Vzc2lvbiBFbmQ=</PHRASE> <PHRASE Label="la_col_SessionStart" Module="Core" Type="1">U2Vzc2lvbiBTdGFydA==</PHRASE> <PHRASE Label="la_col_SortBy" Module="Core" Type="1">U29ydCBieQ==</PHRASE> <PHRASE Label="la_col_System" Module="Core" Type="1">VHlwZQ==</PHRASE> <PHRASE Label="la_col_SystemPath" Module="Core" Type="1">U3lzdGVtIFBhdGg=</PHRASE> <PHRASE Label="la_col_TargetType" Module="Core" Type="1">SXRlbSBUeXBl</PHRASE> <PHRASE Label="la_col_UserCount" Module="Core" Type="1">VXNlcnM=</PHRASE> <PHRASE Label="la_col_UserFirstLastName" Module="Core" Type="1">TGFzdG5hbWUgRmlyc3RuYW1l</PHRASE> <PHRASE Label="la_col_Value" Module="Core" Type="1">RmllbGQgVmFsdWU=</PHRASE> <PHRASE Label="la_col_Visible" Module="Core" Type="1">VmlzaWJsZQ==</PHRASE> <PHRASE Label="la_col_VisitDate" Module="Core" Type="1">VmlzaXQgRGF0ZQ==</PHRASE> <PHRASE Label="la_common_ascending" Module="Core" Type="1">QXNjZW5kaW5n</PHRASE> <PHRASE Label="la_common_descending" Module="Core" Type="1">RGVzY2VuZGluZw==</PHRASE> <PHRASE Label="la_config_AdminConsoleInterface" Module="Core" Type="1">QWRtaW4gQ29uc29sZSBJbnRlcmZhY2U=</PHRASE> <PHRASE Label="la_config_AdminSSLDomain" Module="Core" Type="1">U1NMIERvbWFpbiBmb3IgQWRtaW5pc3RyYXRpdmUgQ29uc29sZSAod3d3LmRvbWFpbi5jb20p</PHRASE> <PHRASE Label="la_config_AllowSelectGroupOnFront" Module="Core" Type="1">QWxsb3cgdG8gc2VsZWN0IG1lbWJlcnNoaXAgZ3JvdXAgb24gRnJvbnQtZW5k</PHRASE> <PHRASE Label="la_config_AutoRefreshIntervals" Module="Core" Type="1">TGlzdCBhdXRvbWF0aWMgcmVmcmVzaCBpbnRlcnZhbHMgKGluIG1pbnV0ZXMp</PHRASE> <PHRASE Label="la_config_backup_path" Module="Core" Type="1">QmFja3VwIFBhdGg=</PHRASE> <PHRASE Label="la_config_CatalogPreselectModuleTab" Module="Core" Type="1">U3dpdGNoIENhdGFsb2cgdGFicyBiYXNlZCBvbiBNb2R1bGU=</PHRASE> <PHRASE Label="la_config_CategoryPermissionRebuildMode" Module="Core" Type="1" Hint="TWFudWFsIC0gbmV2ZXIgcmVidWlsZCBhdXRvbWF0aWNhbGx5DQpTaWxlbnQgLSBhbHdheXMgcmVidWlsZCB3aXRob3V0IHByb2dyZXNzIGJhcg0KQXV0b21hdGljIC0gYWx3YXlzIHJlYnVpbGQsIGJ1dCB1c2UgcHJvZ3Jlc3MgYmFyIG9uIGxhcmdlIHNlY3Rpb24gY291bnQ=">U2VjdGlvbiBQZXJtaXNzaW9uIFJlYnVpbGQgTW9kZQ==</PHRASE> <PHRASE Label="la_config_CheckStopWords" Module="Core" Type="1">Q2hlY2sgU3RvcCBXb3Jkcw==</PHRASE> <PHRASE Label="la_config_CheckViewPermissionsInCatalog" Module="Core" Type="1" Hint="RW5hYmxpbmcgdGhpcyBzZXR0aW5ncyBhdXRvbWF0aWNhbGx5IHR1cm5zIG9uIGFkdmFuY2VkIFZpZXcgUGVybWlzc2lvbiBjaGVjayBvbiBhbGwgQ2F0YWxvZyBTZWN0aW9ucyBhbmQgSXRlbXMgYmFzZWQgb24gY3VycmVudCBVc2VyIEdyb3VwLiBBZGRpdGlvbmFsbHksIGl0IHdvdWxkIHJlcXVpcmUgQWRtaW4gdG8gcnVuIFNlY3Rpb24gQ2FjaGUgcmVidWlsZGluZyBpbiBDYXRhbG9nIGluIHZhcmlvdXMgb2NjYXNpb25zIGluIG9yZGVyIHRvIHNlZSB1cGRhdGUgdG8gZGF0ZSBDYXRhbG9nIFNlY3Rpb25zLiBJdCdzIHJlY29tbWVuZGVkIHRvIGVuYWJsZSB0aGlzIHNldHRpbmcgT05MWSBpZiB5b3VyIHByb2plY3QgcmVxdWlyZXMgaXQu">RW5hYmxlICJWaWV3IFBlcm1pc3Npb25zIiBDaGVjayBpbiBDYXRhbG9n</PHRASE> <PHRASE Label="la_config_CKFinderLicenseKey" Module="Core" Type="1">Q0tGaW5kZXIgTGljZW5zZSBLZXk=</PHRASE> <PHRASE Label="la_config_CKFinderLicenseName" Module="Core" Type="1">Q0tGaW5kZXIgTGljZW5zZSBOYW1l</PHRASE> <PHRASE Label="la_config_CSVExportDelimiter" Module="Core" Type="1">RGVmYXVsdCBDU1YgRXhwb3J0IERlbGltaXRlcg==</PHRASE> <PHRASE Label="la_config_CSVExportEnclosure" Module="Core" Type="1">RGVmYXVsdCBDU1YgRXhwb3J0IEVuY2xvc3VyZSBDaGFyYWN0ZXI=</PHRASE> <PHRASE Label="la_config_CSVExportEncoding" Module="Core" Type="1">RGVmYXVsdCBDU1YgRXhwb3J0IEVuY29kaW5n</PHRASE> <PHRASE Label="la_config_CSVExportSeparator" Module="Core" Type="1">RGVmYXVsdCBDU1YgRXhwb3J0IE5ldyBMaW5lIFNlcGFyYXRvcg==</PHRASE> <PHRASE Label="la_config_DebugOnlyFormConfigurator" Module="Core" Type="1">U2hvdyAiRm9ybXMgRWRpdG9yIiBpbiBERUJVRyBtb2RlIG9ubHk=</PHRASE> <PHRASE Label="la_config_DebugOnlyPromoBlockGroupConfigurator" Module="Core" Type="1">U2hvdyAiUHJvbW8gQmxvY2sgR3JvdXBzIEVkaXRvciIgaW4gREVCVUcgbW9kZSBvbmx5</PHRASE> <PHRASE Label="la_config_DefaultDesignTemplate" Module="Core" Type="1">RGVmYXVsdCBEZXNpZ24gVGVtcGxhdGU=</PHRASE> <PHRASE Label="la_config_DefaultEmailRecipients" Module="Core" Type="1">RGVmYXVsdCBFLW1haWwgUmVjaXBpZW50cw==</PHRASE> <PHRASE Label="la_config_DefaultGridPerPage" Module="Core" Type="1">RGVmYXVsdCAiUGVyIFBhZ2UiIHNldHRpbmcgaW4gR3JpZHM=</PHRASE> <PHRASE Label="la_config_DefaultRegistrationCountry" Module="Core" Type="1">RGVmYXVsdCBSZWdpc3RyYXRpb24gQ291bnRyeQ==</PHRASE> <PHRASE Label="la_config_DefaultTrackingCode" Module="Core" Type="1">RGVmYXVsdCBBbmFseXRpY3MgVHJhY2tpbmcgQ29kZQ==</PHRASE> <PHRASE Label="la_config_EmailDelivery" Module="Core" Type="1">RW1haWwgRGVsaXZlcnk=</PHRASE> <PHRASE Label="la_config_EmailLogRotationInterval" Module="Core" Type="1" Hint="VGhpcyBzZXR0aW5nIGFsbG93cyB5b3UgdG8gY29udHJvbCBmb3IgaG93IGxvbmcgIkUtbWFpbCBMb2ciIG1lc3NhZ2VzIHdpbGwgYmUgc3RvcmVkIGluIHRoZSBsb2cgYW5kIHRoZW4gYXV0b21hdGljYWxseSBkZWxldGVkLiBVc2Ugb3B0aW9uICJGb3JldmVyIiB3aXRoIGNhdXRpb24gc2luY2UgaXQgd2lsbCBjb21wbGV0ZWx5IGRpc2FibGUgYXV0b21hdGljIGxvZyBjbGVhbnVwIGFuZCBjYW4gbGVhZCB0byBsYXJnZSBzaXplIG9mIGRhdGFiYXNlIHRhYmxlIHRoYXQgc3RvcmVzIGUtbWFpbCBtZXNzYWdlcy4=">S2VlcCAiRS1tYWlsIExvZyIgZm9y</PHRASE> <PHRASE Label="la_config_EnableEmailLog" Module="Core" Type="1" Hint="IkUtbWFpbCBMb2ciIHN0b3JlcyB0aGUgZXhhY3QgY29weSBvZiBhbGwgZW1haWxzIHRoYXQgYmVlbiBzZW50IG91dCBieSB5b3VyIHdlYnNpdGUuIFRoZSBmb2xsb3dpbmcgaW5mb3JtYXRpb24gaXMgc3RvcmVkOiBUaW1lLCBTZW5kZXIsIFJlY2lwaWVudHMsIFN1YmplY3QsIEUtbWFpbCBFdmVudCBuYW1lLCBhbmQgRW1haWwgQm9keS4=">RW5hYmxlICJFLW1haWwgTG9nIg==</PHRASE> <PHRASE Label="la_config_EnablePageContentRevisionControl" Module="Core" Type="1">RW5hYmxlIFJldmlzaW9uIENvbnRyb2wgZm9yIFNlY3Rpb24gQ29udGVudA==</PHRASE> <PHRASE Label="la_config_error_template" Module="Core" Type="1">VGVtcGxhdGUgZm9yICJGaWxlIG5vdCBmb3VuZCAoNDA0KSIgRXJyb3I=</PHRASE> <PHRASE Label="la_config_ExcludeTemplateSectionsFromSearch" Module="Core" Type="1">RXhjbHVkZSB0ZW1wbGF0ZSBiYXNlZCBTZWN0aW9ucyBmcm9tIFNlYXJjaCBSZXN1bHRzIChpZS4gVXNlciBSZWdpc3RyYXRpb24p</PHRASE> <PHRASE Label="la_config_FilenameSpecialCharReplacement" Module="Core" Type="1">RmlsZW5hbWUgU3BlY2lhbCBDaGFyIFJlcGxhY2VtZW50</PHRASE> <PHRASE Label="la_config_first_day_of_week" Module="Core" Type="1">Rmlyc3QgRGF5IE9mIFdlZWs=</PHRASE> <PHRASE Label="la_config_ForceCanonicalUrls" Module="Core" Type="1">Rm9yY2UgQ2Fub25pY2FsIFVSTHM=</PHRASE> <PHRASE Label="la_config_ForceImageMagickResize" Module="Core" Type="1">QWx3YXlzIHVzZSBJbWFnZU1hZ2ljayB0byByZXNpemUgaW1hZ2Vz</PHRASE> <PHRASE Label="la_config_ForceModRewriteUrlEnding" Module="Core" Type="1" Hint="VXNlciB3aWxsIGJlIGF1dG9tYXRpY2FsbHkgcmVkaXJlY3RlZCB0byB0aGUgc2VsZWN0ZWQgVXJsIEVuZGluZyBpbiBjYXNlIHdoZW4gY3VycmVudCBwYWdlIHVybCBoYXMgYSBkaWZmZXJlbnQgZW5kaW5n">Rm9yY2UgUmVkaXJlY3QgdG8gU2VsZWN0ZWQgVVJMIEVuZGluZw==</PHRASE> <PHRASE Label="la_config_force_http" Module="Core" Type="1">UmVkaXJlY3QgdG8gSFRUUCB3aGVuIFNTTCBpcyBub3QgcmVxdWlyZWQ=</PHRASE> <PHRASE Label="la_config_FullImageHeight" Module="Core" Type="1">RnVsbCBpbWFnZSBIZWlnaHQ=</PHRASE> <PHRASE Label="la_config_FullImageWidth" Module="Core" Type="1">RnVsbCBpbWFnZSBXaWR0aA==</PHRASE> <PHRASE Label="la_config_HardMaintenanceTemplate" Module="Core" Type="1" Hint="VGhpcyB0ZW1wbGF0ZSB3aWxsIGJlIHVzZWQgdG8gc3RhdGljIEhUTUwgZmlsZSB1bmRlciAvc3lzdGVtIGZvbGRlciB0byBiZSBzaG93biBvbiBGcm9udC1lbmQgb3IgQWRtaW4gd2hlbiBIYXJkIE1haW50ZW5hbmNlIG1vZGUgaXMgYWN0aXZlLiBTdGF0aWMgcGFnZSBzaG91bGQgYmUgZ2VuZXJhdGVkIGZyb20gc3BlY2lmaWVkIGhlcmUgdGVtcGxhdGUgYnkgY2xpY2tpbmcgIkdlbmVyYXRlIFBhZ2UiIGJ1dHRvbi4=">VGVtcGxhdGUgZm9yIEhhcmQgTWFpbnRlbmFuY2U=</PHRASE> <PHRASE Label="la_config_HTTPAuthBypassIPs" Module="Core" Type="1">QnlwYXNzIEhUVFAgQXV0aGVudGljYXRpb24gZnJvbSBJUHMgKHNlcGFyYXRlZCBieSBzZW1pY29sb25zKQ==</PHRASE> <PHRASE Label="la_config_HTTPAuthPassword" Module="Core" Type="1">UGFzc3dvcmQgZm9yIEhUVFAgQXV0aGVudGljYXRpb24=</PHRASE> <PHRASE Label="la_config_HTTPAuthUsername" Module="Core" Type="1">VXNlcm5hbWUgZm9yIEhUVFAgQXV0aGVudGljYXRpb24=</PHRASE> <PHRASE Label="la_config_KeepSessionOnBrowserClose" Module="Core" Type="1">S2VlcCBTZXNzaW9uIGFsaXZlIG9uIEJyb3dzZXIgY2xvc2U=</PHRASE> <PHRASE Label="la_config_MailFunctionHeaderSeparator" Module="Core" Type="1">TWFpbCBGdW5jdGlvbiBIZWFkZXIgU2VwYXJhdG9y</PHRASE> <PHRASE Label="la_config_MailingListQueuePerStep" Module="Core" Type="1">TWFpbGluZyBMaXN0IFF1ZXVlIFBlciBTdGVw</PHRASE> <PHRASE Label="la_config_MailingListSendPerStep" Module="Core" Type="1">TWFpbGluZyBMaXN0IFNlbmQgUGVyIFN0ZXA=</PHRASE> <PHRASE Label="la_config_MaintenanceMessageAdmin" Module="Core" Type="1" Hint="VGhpcyBtZXNzYWdlIHdpbGwgYmUgc2hvd24gb24gQWRtaW4gaW5zdGVhZCBvZiBMb2dpbiBmb3JtIGVpdGhlciB3aGVuIFNvZnQgb3IgSGFyZCBNYWludGVuYW5jZSBtb2RlcyBhcmUgZW5hYmxlZCB2aWEgZGVidWcucGhwIGZpbGUgb3IgdGhlcmUgbm8gRGF0YWJhc2UgY29ubmVjdGlvbi4=">TWFpbnRlbmFuY2UgTWVzc2FnZSBmb3IgQWRtaW4=</PHRASE> <PHRASE Label="la_config_MaintenanceMessageFront" Module="Core" Type="1" Hint="VGhpcyBtZXNzYWdlIHdpbGwgYmUgc2hvd24gb24gRnJvbnQgRW5kIHdoZW4gZWl0aGVyIFNvZnQgb3IgSGFyZCBNYWludGVuYW5jZSBtb2RlcyBhcmUgZW5hYmxlZCB2aWEgZGVidWcucGhwIGZpbGUgb3IgdGhlcmUgbm8gRGF0YWJhc2UgY29ubmVjdGlvbi4=">TWFpbnRlbmFuY2UgTWVzc2FnZSBmb3IgRnJvbnQgRW5k</PHRASE> <PHRASE Label="la_config_MaxImageCount" Module="Core" Type="1">TWF4aW11bSBudW1iZXIgb2YgaW1hZ2Vz</PHRASE> <PHRASE Label="la_config_ModRewriteUrlEnding" Module="Core" Type="1">RGVmYXVsdCBVUkwgRW5kaW5nIGluIFNFTy1mcmllbmRseSBtb2Rl</PHRASE> <PHRASE Label="la_config_nopermission_template" Module="Core" Type="1">VGVtcGxhdGUgZm9yICJJbnN1ZmZpY2llbnQgUGVybWlzc2lvbnMiIEVycm9y</PHRASE> <PHRASE Label="la_config_OutputCompressionLevel" Module="Core" Type="1">R1pJUCBjb21wcmVzc2lvbiBsZXZlbCAwLTk=</PHRASE> <PHRASE Label="la_config_PathToWebsite" Module="Core" Type="1">UGF0aCB0byBXZWJzaXRl</PHRASE> <PHRASE Label="la_config_PerformExactSearch" Module="Core" Type="1" Hint="U2VhcmNoIGZvciBhbGwgZW50ZXJlZCBrZXl3b3JkcywgaW5zdGVhZCBvZiBhbnkgb25lIG9mIHRoZW0=">UGVyZm9ybSBFeGFjdCBTZWFyY2g=</PHRASE> <PHRASE Label="la_config_PerpageReviews" Module="Core" Type="1">Q29tbWVudHMgcGVyIHBhZ2U=</PHRASE> <PHRASE Label="la_config_PlainTextCookies" Module="Core" Type="1">UGxhaW4gVGV4dCBDb29raWVz</PHRASE> <PHRASE Label="la_config_RandomString" Module="Core" Type="1">UmFuZG9tIFN0cmluZw==</PHRASE> <PHRASE Label="la_config_RecycleBinFolder" Module="Core" Type="1">IlJlY3ljbGUgQmluIiBTZWN0aW9uSWQ=</PHRASE> <PHRASE Label="la_config_RegistrationUsernameRequired" Module="Core" Type="1">VXNlcm5hbWUgUmVxdWlyZWQgRHVyaW5nIFJlZ2lzdHJhdGlvbg==</PHRASE> <PHRASE Label="la_config_RememberLastAdminTemplate" Module="Core" Type="1">UmVzdG9yZSBsYXN0IHZpc2l0ZWQgQWRtaW4gU2VjdGlvbiBhZnRlciBMb2dpbg==</PHRASE> <PHRASE Label="la_config_RequireSSLAdmin" Module="Core" Type="1">UmVxdWlyZSBTU0wgZm9yIEFkbWluaXN0cmF0aXZlIENvbnNvbGU=</PHRASE> <PHRASE Label="la_config_require_ssl" Module="Core" Type="1">UmVxdWlyZSBTU0wgZm9yIGxvZ2luICYgY2hlY2tvdXQ=</PHRASE> <PHRASE Label="la_config_ResizableFrames" Module="Core" Type="1">RnJhbWVzIGluIGFkbWluaXN0cmF0aXZlIGNvbnNvbGUgYXJlIHJlc2l6YWJsZQ==</PHRASE> <PHRASE Label="la_config_Search_MinKeyword_Length" Module="Core" Type="1">TWluaW1hbCBTZWFyY2ggS2V5d29yZCBMZW5ndGg=</PHRASE> <PHRASE Label="la_config_SessionBrowserSignatureCheck" Module="Core" Type="1">U2Vzc2lvbiBTZWN1cml0eSBDaGVjayBiYXNlZCBvbiBCcm93c2VyIFNpZ25hdHVyZQ==</PHRASE> <PHRASE Label="la_config_SessionCookieDomains" Module="Core" Type="1">U2Vzc2lvbiBDb29raWUgRG9tYWlucyAoc2luZ2xlIGRvbWFpbiBwZXIgbGluZSk=</PHRASE> <PHRASE Label="la_config_SessionIPAddressCheck" Module="Core" Type="1">U2Vzc2lvbiBTZWN1cml0eSBDaGVjayBiYXNlZCBvbiBJUA==</PHRASE> <PHRASE Label="la_config_SessionLogRotationInterval" Module="Core" Type="1">S2VlcCAiU2Vzc2lvbiBMb2ciIGZvcg==</PHRASE> <PHRASE Label="la_config_SiteNameSubTitle" Module="Core" Type="1">V2Vic2l0ZSBTdWJ0aXRsZQ==</PHRASE> <PHRASE Label="la_config_site_zone" Module="Core" Type="1">VGltZSB6b25lIG9mIHRoZSBzaXRl</PHRASE> <PHRASE Label="la_config_SoftMaintenanceTemplate" Module="Core" Type="1" Hint="VGhpcyB0ZW1wbGF0ZSB3aWxsIGJlIHNob3duIHRvIHRoZSBGcm9udCBFbmQgdXNlcnMgd2hlbiBTb2Z0IE1haW50ZW5hbmNlIG1vZGUgaXMgYWN0aXZlLg==">VGVtcGxhdGUgZm9yIFNvZnQgTWFpbnRlbmFuY2U=</PHRASE> <PHRASE Label="la_config_SSLDomain" Module="Core" Type="1">U1NMIERvbWFpbiAod3d3LmRvbWFpbi5jb20p</PHRASE> <PHRASE Label="la_config_StickyGridSelection" Module="Core" Type="1">VXNlIFN0aWNreSBHcmlkIFNlbGVjdGlvbg==</PHRASE> <PHRASE Label="la_config_SystemLogNotificationEmail" Module="Core" Type="1" Hint="RW1haWwgYWRkcmVzcyB3aGVyZSBVc2VyLWRlZmluZWQgbWVzc2FnZXMgKG1hbnVhbGx5IHNldHVwIGluIHRoZSBjb2RlKSB3aWxsIGJlIGVtYWlsZWQgdG8uIE5vdGUgdGhhdCBhbGwgZGVmYXVsdCAiU3lzdGVtIExvZyIgbWVzc2FnZXMgKG5vbmUgdXNlci1kZWZpbmVkKSBhcmUgTk9UIGVtYWlsZWQgYnkgZGVmYXVsdC4=">U2VuZCBVc2VyLWRlZmluZWQgIlN5c3RlbSBMb2ciIG1lc3NhZ2VzIHRv</PHRASE> <PHRASE Label="la_config_SystemLogRotationInterval" Module="Core" Type="1" Hint="VGhpcyBzZXR0aW5nIGFsbG93cyB5b3UgdG8gY29udHJvbCBmb3IgaG93IGxvbmcgIlN5c3RlbSBMb2ciIG1lc3NhZ2VzIHdpbGwgYmUgc3RvcmVkIGluIHRoZSBsb2cgYW5kIHRoZW4gYXV0b21hdGljYWxseSBkZWxldGVkLiBVc2Ugb3B0aW9uICJGb3JldmVyIiB3aXRoIGNhdXRpb24gc2luY2UgaXQgd2lsbCBjb21wbGV0ZWx5IGRpc2FibGUgYXV0b21hdGljIGxvZyBjbGVhbnVwIGFuZCBjYW4gbGVhZCB0byBsYXJnZSBzaXplIG9mIGRhdGFiYXNlIHRhYmxlIHRoYXQgc3RvcmVzIGxvZyBtZXNzYWdlcy4=">S2VlcCAiU3lzdGVtIExvZyIgZm9y</PHRASE> <PHRASE Label="la_config_ThumbnailImageHeight" Module="Core" Type="1">VGh1bWJuYWlsIEhlaWdodA==</PHRASE> <PHRASE Label="la_config_ThumbnailImageWidth" Module="Core" Type="1">VGh1bWJuYWlsIFdpZHRo</PHRASE> <PHRASE Label="la_config_TrimRequiredFields" Module="Core" Type="1">VHJpbSBSZXF1aXJlZCBGaWVsZHM=</PHRASE> <PHRASE Label="la_config_TypeKitId" Module="Core" Type="1">VHlwZUtpdCBJRA==</PHRASE> <PHRASE Label="la_config_UpdateCountersOnFilterChange" Module="Core" Type="1">VXBkYXRlIGNvdW50ZXJzIChpbiBvdGhlciBmaWx0ZXJzKSBvbiBmaWx0ZXIgY2hhbmdl</PHRASE> <PHRASE Label="la_config_UseChangeLog" Module="Core" Type="1">VHJhY2sgZGF0YWJhc2UgY2hhbmdlcyB0byBjaGFuZ2UgbG9n</PHRASE> <PHRASE Label="la_config_UseColumnFreezer" Module="Core" Type="1">VXNlIENvbHVtbiBGcmVlemVy</PHRASE> <PHRASE Label="la_config_UseContentLanguageNegotiation" Module="Core" Type="1">QXV0by1kZXRlY3QgVXNlcidzIGxhbmd1YWdlIGJhc2VkIG9uIGl0J3MgQnJvd3NlciBzZXR0aW5ncw==</PHRASE> <PHRASE Label="la_config_UseDoubleSorting" Module="Core" Type="1">VXNlIERvdWJsZSBTb3J0aW5n</PHRASE> <PHRASE Label="la_config_UseHTTPAuth" Module="Core" Type="1">RW5hYmxlIEhUVFAgQXV0aGVudGljYXRpb24=</PHRASE> <PHRASE Label="la_config_UseOutputCompression" Module="Core" Type="1">RW5hYmxlIEhUTUwgR1pJUCBjb21wcmVzc2lvbg==</PHRASE> <PHRASE Label="la_config_UsePageHitCounter" Module="Core" Type="1">VXNlIFBhZ2VIaXQgY291bnRlcg==</PHRASE> <PHRASE Label="la_config_UsePopups" Module="Core" Type="1">RWRpdGluZyBXaW5kb3cgU3R5bGU=</PHRASE> <PHRASE Label="la_config_UserEmailActivationTimeout" Module="Core" Type="1">RW1haWwgYWN0aXZhdGlvbiBleHBpcmF0aW9uIHRpbWVvdXQgKGluIG1pbnV0ZXMp</PHRASE> <PHRASE Label="la_config_UseSmallHeader" Module="Core" Type="1">VXNlIFNtYWxsIFNlY3Rpb24gSGVhZGVycw==</PHRASE> <PHRASE Label="la_config_UseToolbarLabels" Module="Core" Type="1">VXNlIFRvb2xiYXIgTGFiZWxz</PHRASE> <PHRASE Label="la_config_UseVisitorTracking" Module="Core" Type="1">VXNlIFZpc2l0b3IgVHJhY2tpbmc=</PHRASE> <PHRASE Label="la_config_use_js_redirect" Module="Core" Type="1">VXNlIEphdmFTY3JpcHQgcmVkaXJlY3Rpb24gYWZ0ZXIgbG9naW4vbG9nb3V0IChmb3IgSUlTKQ==</PHRASE> <PHRASE Label="la_config_use_modrewrite" Module="Core" Type="1">RW5hYmxlIFNFTy1mcmllbmRseSBVUkxzIG1vZGUgKE1PRC1SRVdSSVRFKQ==</PHRASE> <PHRASE Label="la_config_use_modrewrite_with_ssl" Module="Core" Type="1">RW5hYmxlIE1PRF9SRVdSSVRFIGZvciBTU0w=</PHRASE> <PHRASE Label="la_config_website_name" Module="Core" Type="1">V2Vic2l0ZSBuYW1l</PHRASE> <PHRASE Label="la_config_YahooApplicationId" Module="Core" Type="1">WWFob28gQXBwbGljYXRpb25JZA==</PHRASE> <PHRASE Label="la_ConfirmDeleteExportPreset" Module="Core" Type="1">QXJlIHlvdSBzdXJlIHlvdSB3YW50IHRvIGRlbGV0ZSBzZWxlY3RlZCBFeHBvcnQgUHJlc2V0Pw==</PHRASE> <PHRASE Label="la_confirm_maintenance" Module="Core" Type="1">VGhlIHNlY3Rpb24gdHJlZSBtdXN0IGJlIHVwZGF0ZWQgdG8gcmVmbGVjdCB0aGUgbGF0ZXN0IGNoYW5nZXM=</PHRASE> <PHRASE Label="la_CurrentTheme" Module="Core" Type="1">Q3VycmVudCBUaGVtZQ==</PHRASE> <PHRASE Label="la_DataGrid1" Module="Core" Type="1">RGF0YSBHcmlkcw==</PHRASE> <PHRASE Label="la_DataGrid2" Module="Core" Type="1">RGF0YSBHcmlkcyAy</PHRASE> <PHRASE Label="la_days" Module="Core" Type="1">ZGF5cw==</PHRASE> <PHRASE Label="la_Delete_Confirm" Module="Core" Type="1">QXJlIHlvdSBzdXJlIHlvdSB3YW50IHRvIGRlbGV0ZSB0aGUgaXRlbShzKT8gVGhpcyBhY3Rpb24gY2Fubm90IGJlIHVuZG9uZS4=</PHRASE> <PHRASE Label="la_Description_in-portal:advanced_view" Module="Core" Type="1">VGhpcyBzZWN0aW9uIGFsbG93cyB5b3UgdG8gbWFuYWdlIHNlY3Rpb25zIGFuZCBpdGVtcyBhY3Jvc3MgYWxsIHNlY3Rpb25z</PHRASE> <PHRASE Label="la_Description_in-portal:browse" Module="Core" Type="1">VGhpcyBzZWN0aW9uIGFsbG93cyB5b3UgdG8gYnJvd3NlIHRoZSBjYXRhbG9nIGFuZCBtYW5hZ2Ugc2VjdGlvbnMgYW5kIGl0ZW1z</PHRASE> <PHRASE Label="la_Description_in-portal:site" Module="Core" Type="1">TWFuYWdlIHRoZSBzdHJ1Y3R1cmUgb2YgeW91ciBzaXRlLCBpbmNsdWRpbmcgc2VjdGlvbnMsIGl0ZW1zIGFuZCBzZWN0aW9uIHNldHRpbmdzLg==</PHRASE> <PHRASE Label="la_Disabled" Module="Core" Type="1">RGlzYWJsZWQ=</PHRASE> <PHRASE Label="la_Doublequotes" Module="Core" Type="1">RG91YmxlLVF1b3Rlcw==</PHRASE> <PHRASE Label="la_DownloadCSV" Module="Core" Type="1">RG93bmxvYWQgQ1NW</PHRASE> <PHRASE Label="la_DownloadExportFile" Module="Core" Type="1">RG93bmxvYWQgRXhwb3J0IEZpbGU=</PHRASE> <PHRASE Label="la_DownloadLanguageExport" Module="Core" Type="1">RG93bmxvYWQgTGFuZ3VhZ2UgRXhwb3J0</PHRASE> <PHRASE Label="la_Draft" Module="Core" Type="1">RHJhZnQ=</PHRASE> <PHRASE Label="la_DraftAvailableFrom" Module="Core" Type="1">RHJhZnQgQXZhaWxhYmxl</PHRASE> <PHRASE Label="la_DraftSavedAt" Module="Core" Type="1">ZHJhZnQgc2F2ZWQgYXQgJXM=</PHRASE> <PHRASE Label="la_EditingContent" Module="Core" Type="1">Q29udGVudCBFZGl0b3I=</PHRASE> <PHRASE Label="la_EditingInProgress" Module="Core" Type="1">WW91IGhhdmUgbm90IHNhdmVkIGNoYW5nZXMgdG8gdGhlIGl0ZW0geW91IGFyZSBlZGl0aW5nITxiciAvPkNsaWNrIE9LIHRvIGxvb3NlIGNoYW5nZXMgYW5kIGdvIHRvIHRoZSBzZWxlY3RlZCBzZWN0aW9uPGJyIC8+b3IgQ2FuY2VsIHRvIHN0YXkgaW4gdGhlIGN1cnJlbnQgc2VjdGlvbi4=</PHRASE> <PHRASE Label="la_editor_default_style" Module="Core" Type="1">RGVmYXVsdCB0ZXh0</PHRASE> <PHRASE Label="la_EmptyFile" Module="Core" Type="1">RmlsZSBpcyBlbXB0eQ==</PHRASE> <PHRASE Label="la_empty_file" Module="Core" Type="1">RmlsZSBpcyBlbXB0eQ==</PHRASE> <PHRASE Label="la_Enabled" Module="Core" Type="1">RW5hYmxlZA==</PHRASE> <PHRASE Label="la_error_CantDeleteSystemPermission" Module="Core" Type="1">Q2FuJ3QgZGVsZXRlIHN5c3RlbSBwZXJtaXNzaW9u</PHRASE> <PHRASE Label="la_error_CantOpenFile" Module="Core" Type="1">Q2FuJ3Qgb3BlbiB0aGUgZmlsZQ==</PHRASE> <PHRASE Label="la_error_cant_save_file" Module="Core" Type="1">Q2FuJ3Qgc2F2ZSBhIGZpbGU=</PHRASE> <PHRASE Label="la_error_ConnectionFailed" Module="Core" Type="1">Q29ubmVjdGlvbiBGYWlsZWQ=</PHRASE> <PHRASE Label="la_error_copy_subcategory" Module="Core" Type="1">RXJyb3IgY29weWluZyBzdWJzZWN0aW9ucw==</PHRASE> <PHRASE Label="la_error_CustomExists" Module="Core" Type="1">Q3VzdG9tIGZpZWxkIHdpdGggaWRlbnRpY2FsIG5hbWUgYWxyZWFkeSBleGlzdHM=</PHRASE> <PHRASE Label="la_error_EmailTemplateBodyMissing" Module="Core" Type="1">RW1haWwgRGVzaWduIFRlbXBsYXRlIHNob3VsZCBjb250YWluIGF0IGxlYXN0ICIkYm9keSIgdGFnIGluIGl0Lg==</PHRASE> <PHRASE Label="la_error_FileNotFound" Module="Core" Type="1">RmlsZSBub3QgZm91bmQ=</PHRASE> <PHRASE Label="la_error_FileTooLarge" Module="Core" Type="1">RmlsZSBpcyB0b28gbGFyZ2U=</PHRASE> <PHRASE Label="la_error_GroupNotFound" Module="Core" Type="1">Z3JvdXAgbm90IGZvdW5k</PHRASE> <PHRASE Label="la_error_InvalidFieldName" Module="Core" Type="1">RmllbGQgZG9lc24ndCBleGlzdCBpbiAiJXMiIHVuaXQgY29uZmln</PHRASE> <PHRASE Label="la_error_InvalidFileFormat" Module="Core" Type="1">SW52YWxpZCBGaWxlIEZvcm1hdA==</PHRASE> <PHRASE Label="la_error_InvalidItemPrefix" Module="Core" Type="1">VW5pdCBjb25maWcgcHJlZml4IG5vdCBmb3VuZA==</PHRASE> <PHRASE Label="la_error_invalidoption" Module="Core" Type="1">aW52YWxpZCBvcHRpb24=</PHRASE> <PHRASE Label="la_error_LoginFailed" Module="Core" Type="1">TG9naW4gRmFpbGVk</PHRASE> <PHRASE Label="la_error_MessagesListReceivingFailed" Module="Core" Type="1">UmVjZWl2aW5nIGxpc3Qgb2YgbWVzc2FnZXMgZnJvbSB0aGUgU2VydmVyIGhhcyBmYWlsZWQ=</PHRASE> <PHRASE Label="la_error_move_subcategory" Module="Core" Type="1">RXJyb3IgbW92aW5nIHN1YnNlY3Rpb24=</PHRASE> <PHRASE Label="la_error_NoInheritancePossible" Module="Core" Type="1">Q2FuJ3QgaW5oZXJpdCB0ZW1wbGF0ZSBmcm9tIHRvcCBjYXRlZ29yeQ==</PHRASE> <PHRASE Label="la_error_NoMatchingColumns" Module="Core" Type="1">Tm8gbWF0Y2hpbmcgY29sdW1ucyBhcmUgZm91bmQ=</PHRASE> <PHRASE Label="la_error_OperationNotAllowed" Module="Core" Type="1">VGhpcyBvcGVyYXRpb24gaXMgbm90IGFsbG93ZWQh</PHRASE> <PHRASE Label="la_error_ParsingError" Module="Core" Type="1">VmFsaWRhdGlvbiBlcnJvciwgcGxlYXNlIGRvdWJsZS1jaGVjayBJbi1Qb3J0YWwgdGFncw==</PHRASE> <PHRASE Label="la_error_PasswordMatch" Module="Core" Type="1">UGFzc3dvcmRzIGRvIG5vdCBtYXRjaCE=</PHRASE> <PHRASE Label="la_error_PromoGroupNotEmpty" Module="Core" Type="1">Q2FuJ3QgRGVsZXRlIE5vbi1FbXB0eSBQcm9tbyBCbG9jayBHcm91cA==</PHRASE> <PHRASE Label="la_error_required" Module="Core" Type="1">UmVxdWlyZWQgZmllbGQoLXMpIG5vdCBmaWxsZWQ=</PHRASE> <PHRASE Label="la_error_RequiredColumnsMissing" Module="Core" Type="1">cmVxdWlyZWQgY29sdW1ucyBtaXNzaW5n</PHRASE> <PHRASE Label="la_error_RootCategoriesDelete" Module="Core" Type="1">Um9vdCBzZWN0aW9uIG9mIHRoZSBtb2R1bGUocykgY2FuIG5vdCBiZSBkZWxldGVkIQ==</PHRASE> <PHRASE Label="la_error_SelectItemToMove" Module="Core" Type="1">U2VsZWN0IGF0IGxlYXN0IG9uZSBpdGVtIHRvIG1vdmU=</PHRASE> <PHRASE Label="la_error_TemplateFileMissing" Module="Core" Type="1">VGVtcGxhdGUgZmlsZSBpcyBtaXNzaW5n</PHRASE> <PHRASE Label="la_error_TemporaryTableCopyingFailed" Module="Core" Type="1">Q29weWluZyBvcGVyYXRpb24gaW4gVGVtcG9yYXJ5IHRhYmxlcyBoYXMgZmFpbGVkLiBQbGVhc2UgY29udGFjdCB3ZWJzaXRlIGFkbWluaXN0cmF0b3Iu</PHRASE> <PHRASE Label="la_error_unique" Module="Core" Type="1">UmVjb3JkIGlzIG5vdCB1bmlxdWU=</PHRASE> <PHRASE Label="la_error_unique_category_field" Module="Core" Type="1">U2VjdGlvbiBmaWVsZCBub3QgdW5pcXVl</PHRASE> <PHRASE Label="la_error_UnknownSection" Module="Core" Type="1">VW5rbm93biBzZWN0aW9u</PHRASE> <PHRASE Label="la_error_unknown_category" Module="Core" Type="1">VW5rbm93biBzZWN0aW9u</PHRASE> <PHRASE Label="la_error_UserBanned" Module="Core" Type="1">VXNlciBCYW5uZWQ=</PHRASE> <PHRASE Label="LA_ERROR_USERNOTFOUND" Module="Core" Type="1">dXNlciBub3QgZm91bmQ=</PHRASE> <PHRASE Label="la_error_YouMustSelectOnlyOneUser" Module="Core" Type="1">WW91IG11c3Qgc2VsZWN0IG9ubHkgb25lIHVzZXI=</PHRASE> <PHRASE Label="la_err_bad_date_format" Module="Core" Type="1">SW5jb3JyZWN0IGRhdGUgZm9ybWF0LCBwbGVhc2UgdXNlICglcykgZXguICglcyk=</PHRASE> <PHRASE Label="la_err_bad_type" Module="Core" Type="1">SW5jb3JyZWN0IGRhdGEgZm9ybWF0LCBwbGVhc2UgdXNlIHt0eXBlfQ==</PHRASE> <PHRASE Label="la_err_invalid_format" Module="Core" Type="1">SW52YWxpZCBGb3JtYXQ=</PHRASE> <PHRASE Label="la_err_length_out_of_range" Module="Core" Type="1">RmllbGQgdmFsdWUgbGVuZ3RoIGlzIG91dCBvZiByYW5nZSwgcG9zc2libGUgdmFsdWUgbGVuZ3RoIGZyb20ge21pbl9sZW5ndGh9IHRvIHttYXhfbGVuZ3RofQ==</PHRASE> <PHRASE Label="la_err_Primary_Lang_Required" Module="Core" Type="1">UHJpbWFyeSBMYW5nLiB2YWx1ZSBSZXF1aXJlZA==</PHRASE> <PHRASE Label="la_err_required" Module="Core" Type="1">RmllbGQgaXMgcmVxdWlyZWQ=</PHRASE> <PHRASE Label="la_err_unique" Module="Core" Type="1">RmllbGQgdmFsdWUgbXVzdCBiZSB1bmlxdWU=</PHRASE> <PHRASE Label="la_err_value_out_of_range" Module="Core" Type="1">RmllbGQgdmFsdWUgaXMgb3V0IG9mIHJhbmdlLCBwb3NzaWJsZSB2YWx1ZXMgZnJvbSB7bWluX3ZhbHVlfSB0byB7bWF4X3ZhbHVlfQ==</PHRASE> <PHRASE Label="la_exportfoldernotwritable" Module="Core" Type="1">RXhwb3J0IGZvbGRlciBpcyBub3Qgd3JpdGFibGU=</PHRASE> <PHRASE Label="la_fck_ErrorCreatingFolder" Module="Core" Type="1">RXJyb3IgY3JlYXRpbmcgZm9sZGVyLiBFcnJvciBudW1iZXI6</PHRASE> <PHRASE Label="la_fck_ErrorFileName" Module="Core" Type="1">UGxlYXNlIG5hbWUgeW91ciBmaWxlcyB0byBiZSB3ZWItZnJpZW5kbHkuIFdlIHJlY29tbWVuZCB1c2luZyBvbmx5IHRoZXNlIGNoYXJhY3RlcnMgaW4gZmlsZSBuYW1lczogDQpMZXR0ZXJzIGEteiwgQS1aLCBOdW1iZXJzIDAtOSwgIl8iICh1bmRlcnNjb3JlKSwgIi0iIChkYXNoKSwgIiAiIChzcGFjZSksICIuIiAocGVyaW9kKQ0KUGxlYXNlIGF2b2lkIHVzaW5nIGFueSBvdGhlciBjaGFyYWN0ZXJzIGxpa2UgcXVvdGVzLCBicmFja2V0cywgcXVvdGF0aW9uIG1hcmtzLCAiPyIsICIhIiwgIj0iLCBmb3JlaWduIHN5bWJvbHMsIGV0Yy4=</PHRASE> <PHRASE Label="la_fck_ErrorFileUpload" Module="Core" Type="1">RXJyb3Igb24gZmlsZSB1cGxvYWQuIEVycm9yIG51bWJlcjo=</PHRASE> <PHRASE Label="la_fck_FileAvailable" Module="Core" Type="1">QSBmaWxlIHdpdGggdGhlIHNhbWUgbmFtZSBpcyBhbHJlYWR5IGF2YWlsYWJsZQ==</PHRASE> <PHRASE Label="la_fck_FileDate" Module="Core" Type="1">RGF0ZQ==</PHRASE> <PHRASE Label="la_fck_FileName" Module="Core" Type="1">RmlsZSBOYW1l</PHRASE> <PHRASE Label="la_fck_FileSize" Module="Core" Type="1">U2l6ZQ==</PHRASE> <PHRASE Label="la_fck_FolderAlreadyExists" Module="Core" Type="1">Rm9sZGVyIGFscmVhZHkgZXhpc3Rz</PHRASE> <PHRASE Label="la_fck_InvalidFileType" Module="Core" Type="1">SW52YWxpZCBmaWxlIHR5cGUgZm9yIHRoaXMgZm9kZXI=</PHRASE> <PHRASE Label="la_fck_InvalidFolderName" Module="Core" Type="1">SW52YWxpZCBmb2xkZXIgbmFtZQ==</PHRASE> <PHRASE Label="la_fck_NoPermissionsCreateFolder" Module="Core" Type="1">WW91IGhhdmUgbm8gcGVybWlzc2lvbnMgdG8gY3JlYXRlIHRoZSBmb2xkZXI=</PHRASE> <PHRASE Label="la_fck_PleaseTypeTheFolderName" Module="Core" Type="1">UGxlYXNlIHR5cGUgdGhlIGZvbGRlciBuYW1l</PHRASE> <PHRASE Label="la_fck_TypeTheFolderName" Module="Core" Type="1">VHlwZSB0aGUgbmFtZSBvZiB0aGUgbmV3IGZvbGRlcjo=</PHRASE> <PHRASE Label="la_fck_UnknownErrorCreatingFolder" Module="Core" Type="1">VW5rbm93biBlcnJvciBjcmVhdGluZyBmb2xkZXI=</PHRASE> <PHRASE Label="la_Field" Module="Core" Type="1">RmllbGQ=</PHRASE> <PHRASE Label="la_field_displayorder" Module="Core" Type="1">RGlzcGxheSBPcmRlcg==</PHRASE> <PHRASE Label="la_field_Priority" Module="Core" Type="1">T3JkZXI=</PHRASE> <PHRASE Label="la_fld_Action" Module="Core" Type="1" Column="QWN0aW9u">QWN0aW9u</PHRASE> <PHRASE Label="la_fld_AddressLine1" Module="Core" Type="1" Column="QWRkcmVzcyBMaW5lIDE=">QWRkcmVzcyBMaW5lIDE=</PHRASE> <PHRASE Label="la_fld_AddressLine2" Module="Core" Type="1" Column="QWRkcmVzcyBMaW5lIDI=">QWRkcmVzcyBMaW5lIDI=</PHRASE> <PHRASE Label="la_fld_AdminEmail" Module="Core" Type="1">TWVzc2FnZXMgZnJvbSBTaXRlIEFkbWluIGFyZSBmcm9t</PHRASE> <PHRASE Label="la_fld_AdminInterfaceLang" Module="Core" Type="1" Column="QWRtaW4gUHJpbWFyeQ==">QWRtaW4gUHJpbWFyeQ==</PHRASE> <PHRASE Label="la_fld_AdminLanguage" Module="Core" Type="1" Column="TGFuZ3VhZ2U=">TGFuZ3VhZ2U=</PHRASE> <PHRASE Label="la_fld_AdvancedCSS" Module="Core" Type="1">QWR2YW5jZWQgQ1NT</PHRASE> <PHRASE Label="la_fld_AdvancedSearch" Module="Core" Type="1">QWR2YW5jZWQgU2VhcmNo</PHRASE> <PHRASE Label="la_fld_AllowChangingRecipient" Module="Core" Type="1">QWxsb3cgQ2hhbmdpbmcgIlRvIiBSZWNpcGllbnQ=</PHRASE> <PHRASE Label="la_fld_AllowChangingSender" Module="Core" Type="1">QWxsb3cgQ2hhbmdpbmcgU2VuZGVy</PHRASE> <PHRASE Label="la_fld_AltValue" Module="Core" Type="1">QWx0IFZhbHVl</PHRASE> <PHRASE Label="la_fld_Answer" Module="Core" Type="1">QW5zd2Vy</PHRASE> <PHRASE Label="la_fld_AssignedToSections" Module="Core" Type="1">QXNzaWduZWQgdG8gU2VjdGlvbnM=</PHRASE> <PHRASE Label="LA_FLD_ATTACHMENT" Module="Core" Type="1">QXR0YWNobWVudA==</PHRASE> <PHRASE Label="la_fld_AutoCreateFileName" Module="Core" Type="1">QXV0byBDcmVhdGUgRmlsZSBOYW1l</PHRASE> <PHRASE Label="la_fld_AutomaticFilename" Module="Core" Type="1">QXV0b21hdGljIEZpbGVuYW1l</PHRASE> <PHRASE Label="la_fld_AvailableColumns" Module="Core" Type="1">QXZhaWxhYmxlIENvbHVtbnM=</PHRASE> <PHRASE Label="la_fld_Background" Module="Core" Type="1">QmFja2dyb3VuZA==</PHRASE> <PHRASE Label="la_fld_BackgroundAttachment" Module="Core" Type="1">QmFja2dyb3VuZCBBdHRhY2htZW50</PHRASE> <PHRASE Label="la_fld_BackgroundColor" Module="Core" Type="1">QmFja2dyb3VuZCBDb2xvcg==</PHRASE> <PHRASE Label="la_fld_BackgroundImage" Module="Core" Type="1">QmFja2dyb3VuZCBJbWFnZQ==</PHRASE> <PHRASE Label="la_fld_BackgroundPosition" Module="Core" Type="1">QmFja2dyb3VuZCBQb3NpdGlvbg==</PHRASE> <PHRASE Label="la_fld_BackgroundRepeat" Module="Core" Type="1">QmFja2dyb3VuZCBSZXBlYXQ=</PHRASE> <PHRASE Label="la_fld_Bcc" Module="Core" Type="1" Column="QmNj">QmNj</PHRASE> <PHRASE Label="la_fld_BindToSystemEvent" Module="Core" Type="1" Hint="U3lzdGVtIEV2ZW50IGluICJwcmVmaXhbLnNwZWNpYWxdOk9uRXZlbnROYW1lIiBmb3JtYXQ=" Column="QmluZCB0byBTeXN0ZW0gRXZlbnQ=">QmluZCB0byBTeXN0ZW0gRXZlbnQ=</PHRASE> <PHRASE Label="la_fld_BlockPosition" Module="Core" Type="1">RWxlbWVudCBQb3NpdGlvbg==</PHRASE> <PHRASE Label="la_fld_BorderBottom" Module="Core" Type="1">Qm9yZGVyIEJvdHRvbQ==</PHRASE> <PHRASE Label="la_fld_BorderLeft" Module="Core" Type="1">Qm9yZGVyIExlZnQ=</PHRASE> <PHRASE Label="la_fld_BorderRight" Module="Core" Type="1">Qm9yZGVyIFJpZ2h0</PHRASE> <PHRASE Label="la_fld_Borders" Module="Core" Type="1">Qm9yZGVycw==</PHRASE> <PHRASE Label="la_fld_BorderTop" Module="Core" Type="1">Qm9yZGVyIFRvcA==</PHRASE> <PHRASE Label="la_fld_BounceDate" Module="Core" Type="1" Column="Qm91bmNlZCBPbg==">Qm91bmNlIERhdGU=</PHRASE> <PHRASE Label="la_fld_BounceEmail" Module="Core" Type="1">Qm91bmNlIEVtYWls</PHRASE> <PHRASE Label="la_fld_BounceInfo" Module="Core" Type="1" Column="Qm91bmNlIEluZm8=">Qm91bmNlIEluZm8=</PHRASE> <PHRASE Label="la_fld_ButtonText" Module="Core" Type="1" Column="QnV0dG9uIFRleHQ=">QnV0dG9uIFRleHQ=</PHRASE> <PHRASE Label="la_fld_Category" Module="Core" Type="1" Column="U2VjdGlvbg==">U2VjdGlvbg==</PHRASE> <PHRASE Label="la_fld_CategoryFormat" Module="Core" Type="1">U2VjdGlvbiBGb3JtYXQ=</PHRASE> <PHRASE Label="la_fld_CategoryId" Module="Core" Type="1" Column="U2VjdGlvbiBJRA==">U2VjdGlvbiBJRA==</PHRASE> <PHRASE Label="la_fld_CategorySeparator" Module="Core" Type="1">U2VjdGlvbiBzZXBhcmF0b3I=</PHRASE> <PHRASE Label="la_fld_CategoryTemplate" Module="Core" Type="1">U2VjdGlvbiBUZW1wbGF0ZQ==</PHRASE> <PHRASE Label="la_fld_Cc" Module="Core" Type="1" Column="Q2M=">Q2M=</PHRASE> <PHRASE Label="la_fld_Changes" Module="Core" Type="1" Column="Q2hhbmdlcw==">Q2hhbmdlcw==</PHRASE> <PHRASE Label="la_fld_Charset" Module="Core" Type="1" Column="Q2hhcnNldA==">Q2hhcnNldA==</PHRASE> <PHRASE Label="la_fld_CheckDuplicatesMethod" Module="Core" Type="1">Q2hlY2sgRHVwbGljYXRlcyBieQ==</PHRASE> <PHRASE Label="la_fld_City" Module="Core" Type="1" Column="Q2l0eQ==">Q2l0eQ==</PHRASE> <PHRASE Label="la_fld_ColumnTranslation" Module="Core" Type="1" Hint="QXNzb2NpYXRlZCBjb2x1bW4gaGVhZGluZyB0cmFuc2xhdGlvbg==">Q29sdW1uIFBocmFzZQ==</PHRASE> <PHRASE Label="la_fld_Comments" Module="Core" Type="1">Q29tbWVudHM=</PHRASE> <PHRASE Label="la_fld_Company" Module="Core" Type="1" Column="Q29tcGFueQ==">Q29tcGFueQ==</PHRASE> <PHRASE Label="la_fld_ConfigHeader" Module="Core" Type="1">Q29uZmlndXJhdGlvbiBIZWFkZXIgTGFiZWw=</PHRASE> <PHRASE Label="la_fld_ContentBlock" Module="Core" Type="1">Q29udGVudCBCbG9jaw==</PHRASE> <PHRASE Label="la_fld_ConversionPercent" Module="Core" Type="1" Column="Q1RSLCAl">Q1RSLCAl</PHRASE> <PHRASE Label="la_fld_CopyLabels" Module="Core" Type="1">Q29weSBMYWJlbHMgZnJvbSB0aGlzIExhbmd1YWdl</PHRASE> <PHRASE Label="la_fld_Country" Module="Core" Type="1" Column="Q291bnRyeQ==">Q291bnRyeQ==</PHRASE> <PHRASE Label="la_fld_CreatedById" Module="Core" Type="1">Q3JlYXRlZCBCeQ==</PHRASE> <PHRASE Label="la_fld_CreatedOn" Module="Core" Type="1" Column="Q3JlYXRlZCBPbg==">Q3JlYXRlZCBPbg==</PHRASE> <PHRASE Label="la_fld_CronCommonHints" Module="Core" Type="1">Q29tbW9uIFNldHRpbmdz</PHRASE> <PHRASE Label="la_fld_CronDay" Module="Core" Type="1" Column="RGF5">RGF5</PHRASE> <PHRASE Label="la_fld_CronHour" Module="Core" Type="1" Column="SG91cg==">SG91cg==</PHRASE> <PHRASE Label="la_fld_CronMinute" Module="Core" Type="1" Column="TWludXRl">TWludXRl</PHRASE> <PHRASE Label="la_fld_CronMonth" Module="Core" Type="1" Column="TW9udGg=">TW9udGg=</PHRASE> <PHRASE Label="la_fld_CronWeekday" Module="Core" Type="1" Column="V2Vla2RheQ==">V2Vla2RheQ==</PHRASE> <PHRASE Label="la_fld_CSS" Module="Core" Type="1">Q1NTIFRlbXBsYXRl</PHRASE> <PHRASE Label="la_fld_CSSClassName" Module="Core" Type="1" Column="Q1NTIENsYXNzIE5hbWU=">Q1NTIENsYXNzIE5hbWU=</PHRASE> <PHRASE Label="la_fld_Cursor" Module="Core" Type="1">Q3Vyc29y</PHRASE> <PHRASE Label="la_fld_CustomDetailTemplate" Module="Core" Type="1">Q3VzdG9tIERldGFpbHMgVGVtcGxhdGU=</PHRASE> <PHRASE Label="la_fld_CustomRecipient" Module="Core" Type="1">U2VuZCBFbWFpbCBUbw==</PHRASE> <PHRASE Label="la_fld_CustomSender" Module="Core" Type="1">U2VuZCBFbWFpbCBGcm9t</PHRASE> <PHRASE Label="la_fld_CustomTemplate" Module="Core" Type="1">DQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KRGV0YWlscyBUZW1wbGF0ZQ==</PHRASE> <PHRASE Label="la_fld_DateFormat" Module="Core" Type="1">RGF0ZSBGb3JtYXQ=</PHRASE> <PHRASE Label="la_fld_DecimalPoint" Module="Core" Type="1">RGVjaW1hbCBQb2ludA==</PHRASE> <PHRASE Label="la_fld_Description" Module="Core" Type="1" Column="RGVzY3JpcHRpb24=">RGVzY3JpcHRpb24=</PHRASE> <PHRASE Label="la_fld_DirectLinkEnabled" Module="Core" Type="1">QWNjZXNzIHdpdGggTGluaw==</PHRASE> <PHRASE Label="la_fld_Display" Module="Core" Type="1">RGlzcGxheQ==</PHRASE> <PHRASE Label="la_fld_DisplayInGrid" Module="Core" Type="1">RGlzcGxheSBpbiBHcmlk</PHRASE> <PHRASE Label="la_fld_DisplayName" Module="Core" Type="1">RmllbGQgTGFiZWw=</PHRASE> <PHRASE Label="la_fld_DisplaySiteNameInHeader" Module="Core" Type="1">RGlzcGxheSBzaXRlIG5hbWUgaW4gSGVhZGVy</PHRASE> <PHRASE Label="la_fld_DisplayToPublic" Module="Core" Type="1">RGlzcGxheSBUbyBQdWJsaWM=</PHRASE> <PHRASE Label="la_fld_DomainIPRange" Module="Core" Type="1" Hint="U2luZ2xlIElQLCBJUCByYW5nZSwgU3VibmV0IG9yIGhvc3RuYW1lIHJlY29yZCBwZXIgbGluZSAoZm9ybWF0czogMS4yLjMuNCBvciAxLjIuMy4zMi0xLjIuMy41NCBvciAxLjIuMy4zMi8yNyBvciAxLjIuMy4zMi8yNTUuMjU1LjI1NS4yMjQgb3Igd3d3LmluLXBvcnRhbC5jb20p">UmFuZ2Ugb2YgSVBz</PHRASE> <PHRASE Label="la_fld_DomainName" Module="Core" Type="1" Column="RG9tYWluIE5hbWU=">RG9tYWluIE5hbWU=</PHRASE> <PHRASE Label="la_fld_DoNotEncode" Module="Core" Type="1">QXMgUGxhaW4gVGV4dA==</PHRASE> <PHRASE Label="la_fld_Duration" Module="Core" Type="1" Column="RHVyYXRpb24=">RHVyYXRpb24=</PHRASE> <PHRASE Label="la_fld_EditorsPick" Module="Core" Type="1">RWRpdG9ycyBQaWNr</PHRASE> <PHRASE Label="la_fld_ElapsedTime" Module="Core" Type="1">RWxhcHNlZCBUaW1l</PHRASE> <PHRASE Label="la_fld_Email" Module="Core" Type="1" Column="RW1haWw=">RS1tYWls</PHRASE> <PHRASE Label="la_fld_EmailCommunicationRole" Module="Core" Type="1" Column="RS1tYWlsIENvbW11bmljYXRpb24gUm9sZQ==">RS1tYWlsIENvbW11bmljYXRpb24gUm9sZQ==</PHRASE> <PHRASE Label="la_fld_EmailOrUsername" Module="Core" Type="1">RS1tYWlsIG9yIFVzZXJuYW1l</PHRASE> <PHRASE Label="la_fld_EmailPassword" Module="Core" Type="1">RS1tYWlsICI8c3Ryb25nPntwYXNzd29yZH08L3N0cm9uZz4iIHBhc3N3b3JkIHRvIHVzZXI=</PHRASE> <PHRASE Label="la_fld_EmailsQueued" Module="Core" Type="1" Column="UXVldWU=">RW1haWxzIGluIFF1ZXVl</PHRASE> <PHRASE Label="la_fld_EmailsSent" Module="Core" Type="1" Column="U2VudA==">RW1haWxzIFNlbnQ=</PHRASE> <PHRASE Label="la_fld_EmailsTotal" Module="Core" Type="1" Column="VG90YWw=">RW1haWxzIFRvdGFs</PHRASE> <PHRASE Label="la_fld_EmailTemplateName" Module="Core" Type="1" Column="RS1tYWlsIFRlbXBsYXRlIE5hbWU=">RS1tYWlsIFRlbXBsYXRlIE5hbWU=</PHRASE> <PHRASE Label="la_fld_EmailVerified" Module="Core" Type="1" Column="RW1haWwgVmVyaWZpZWQ=">RW1haWwgVmVyaWZpZWQ=</PHRASE> <PHRASE Label="la_fld_Enable" Module="Core" Type="1">RW5hYmxl</PHRASE> <PHRASE Label="la_fld_Enabled" Module="Core" Type="1" Column="RW5hYmxlZA==">RW5hYmxlZA==</PHRASE> <PHRASE Label="la_fld_EnablePageCache" Module="Core" Type="1">RW5hYmxlIENhY2hpbmcgZm9yIHRoaXMgU2VjdGlvbg==</PHRASE> <PHRASE Label="la_fld_ErrorMessage" Module="Core" Type="1" Column="RXJyb3IgTWVzc2FnZQ==">RXJyb3IgTWVzc2FnZQ==</PHRASE> <PHRASE Label="la_fld_ErrorTag" Module="Core" Type="1">RXJyb3IgVGFn</PHRASE> <PHRASE Label="la_fld_EstimatedTime" Module="Core" Type="1">RXN0aW1hdGVkIFRpbWU=</PHRASE> <PHRASE Label="la_fld_Event" Module="Core" Type="1" Column="RXZlbnQ=">RXZlbnQ=</PHRASE> <PHRASE Label="la_fld_Expire" Module="Core" Type="1">RXhwaXJl</PHRASE> <PHRASE Label="la_fld_ExportColumns" Module="Core" Type="1">RXhwb3J0IGNvbHVtbnM=</PHRASE> <PHRASE Label="la_fld_ExportCountries" Module="Core" Type="1" Hint="U2luZ2xlIENvdW50cnkgSVNPIGNvZGUgcGVyIGxpbmUgKGZvcm1hdHM6IEFUQSwgQkVMKQ==">RXhwb3J0IFNwZWNpZmllZCBDb3VudHJpZXM=</PHRASE> <PHRASE Label="la_fld_ExportDataTypes" Module="Core" Type="1">RGF0YSBUeXBlcyB0byBFeHBvcnQ=</PHRASE> <PHRASE Label="la_fld_ExportEmailTemplates" Module="Core" Type="1" Hint="U2luZ2xlIEUtbWFpbCBUZW1wbGF0ZSBwZXIgbGluZSAoZm9ybWF0czogVVNFUi5BREQsIE9SREVSLlNVQk1JVCk=">RXhwb3J0IFNwZWNpZmllZCBFLW1haWwgVGVtcGxhdGVz</PHRASE> <PHRASE Label="la_fld_ExportFileName" Module="Core" Type="1">RXhwb3J0IEZpbGVuYW1l</PHRASE> <PHRASE Label="la_fld_ExportFormat" Module="Core" Type="1">RXhwb3J0IGZvcm1hdA==</PHRASE> <PHRASE Label="la_fld_ExportModules" Module="Core" Type="1">RXhwb3J0IE1vZHVsZXM=</PHRASE> <PHRASE Label="la_fld_ExportPhrases" Module="Core" Type="1" Hint="U2luZ2xlIFBocmFzZSBMYWJlbCBwZXIgbGluZSAoZm9ybWF0czogbGFfU2FtcGxlTGFiZWwsIGx1X0Zyb250RW5kTGFiZWwp">RXhwb3J0IFNwZWNpZmllZCBQaHJhc2Vz</PHRASE> <PHRASE Label="la_fld_ExportPhraseTypes" Module="Core" Type="1">RXhwb3J0IFBocmFzZSBUeXBlcw==</PHRASE> <PHRASE Label="la_fld_ExportPresetName" Module="Core" Type="1">RXhwb3J0IFByZXNldCBUaXRsZQ==</PHRASE> <PHRASE Label="la_fld_ExportPresets" Module="Core" Type="1">RXhwb3J0IFByZXNldA==</PHRASE> <PHRASE Label="la_fld_ExportSavePreset" Module="Core" Type="1">U2F2ZS9VcGRhdGUgRXhwb3J0IFByZXNldA==</PHRASE> <PHRASE Label="la_fld_ExternalLink" Module="Core" Type="1" Column="RXh0ZXJuYWwgTGluaw==">RXh0ZXJuYWwgTGluaw==</PHRASE> <PHRASE Label="la_fld_ExternalUrl" Module="Core" Type="1">RXh0ZXJuYWwgVVJM</PHRASE> <PHRASE Label="la_fld_ExtraHeaders" Module="Core" Type="1">RXh0cmEgSGVhZGVycw==</PHRASE> <PHRASE Label="la_fld_Fax" Module="Core" Type="1" Column="RmF4">RmF4</PHRASE> <PHRASE Label="la_fld_FieldComparision" Module="Core" Type="1" Column="TWF0Y2ggVHlwZQ==">TWF0Y2ggVHlwZQ==</PHRASE> <PHRASE Label="la_fld_FieldName" Module="Core" Type="1" Column="RmllbGQgTmFtZQ==">RmllbGQgTmFtZQ==</PHRASE> <PHRASE Label="la_fld_FieldsEnclosedBy" Module="Core" Type="1">RmllbGRzIGVuY2xvc2VkIGJ5</PHRASE> <PHRASE Label="la_fld_FieldsSeparatedBy" Module="Core" Type="1">RmllbGRzIHNlcGFyYXRlZCBieQ==</PHRASE> <PHRASE Label="la_fld_FieldTitles" Module="Core" Type="1">RmllbGQgVGl0bGVz</PHRASE> <PHRASE Label="la_fld_FieldType" Module="Core" Type="1">RmllbGQgVHlwZQ==</PHRASE> <PHRASE Label="la_fld_FieldValue" Module="Core" Type="1" Column="TWF0Y2ggVmFsdWU=">TWF0Y2ggVmFsdWU=</PHRASE> <PHRASE Label="la_fld_FileContents" Module="Core" Type="1">RmlsZSBDb250ZW50cw==</PHRASE> <PHRASE Label="la_fld_Filename" Module="Core" Type="1" Column="RmlsZW5hbWU=">RmlsZW5hbWU=</PHRASE> <PHRASE Label="la_fld_FilenameReplacements" Module="Core" Type="1">RmlsZW5hbWUgUmVwbGFjZW1lbnRz</PHRASE> <PHRASE Label="la_fld_FilePath" Module="Core" Type="1" Column="UGF0aA==">UGF0aA==</PHRASE> <PHRASE Label="la_fld_FilterField" Module="Core" Type="1" Hint="RmllbGQgbmFtZSwgdG8gYmUgdXNlZCBpbiBmaWx0ZXI=" Column="RmlsdGVyIEZpZWxk">RmlsdGVyIEZpZWxk</PHRASE> <PHRASE Label="la_fld_FilterType" Module="Core" Type="1" Hint="VGhlIHdheSwgaG93IHRoaXMgZmlsdGVyIHdpbGwgYmUgZGlzcGxheWVkIG9uIEZyb250LUVuZA==" Column="RmlsdGVyIFR5cGU=">RmlsdGVyIFR5cGU=</PHRASE> <PHRASE Label="la_fld_FirstName" Module="Core" Type="1" Column="Rmlyc3QgTmFtZQ==">Rmlyc3QgTmFtZQ==</PHRASE> <PHRASE Label="la_fld_Font" Module="Core" Type="1">Rm9udA==</PHRASE> <PHRASE Label="la_fld_FontColor" Module="Core" Type="1">Rm9udCBDb2xvcg==</PHRASE> <PHRASE Label="la_fld_FontFamily" Module="Core" Type="1">Rm9udCBGYW1pbHk=</PHRASE> <PHRASE Label="la_fld_FontSize" Module="Core" Type="1">Rm9udCBTaXpl</PHRASE> <PHRASE Label="la_fld_FontStyle" Module="Core" Type="1">Rm9udCBTdHlsZQ==</PHRASE> <PHRASE Label="la_fld_FontWeight" Module="Core" Type="1">Rm9udCBXZWlnaHQ=</PHRASE> <PHRASE Label="la_fld_Form" Module="Core" Type="1">T25saW5lIEZvcm0=</PHRASE> <PHRASE Label="la_fld_FormSubmittedTemplate" Module="Core" Type="1">T25saW5lIEZvcm0gU3VibWl0dGVkIFRlbXBsYXRl</PHRASE> <PHRASE Label="la_fld_FriendlyURL" Module="Core" Type="1">U2hvcnQgVVJM</PHRASE> <PHRASE Label="la_fld_FromEmail" Module="Core" Type="1" Column="RnJvbSBFLW1haWw=">RnJvbSBFbWFpbA==</PHRASE> <PHRASE Label="la_fld_FrontEndOnly" Module="Core" Type="1" Column="RnJvbnQtRW5kIE9ubHk=">RnJvbnQtRW5kIE9ubHk=</PHRASE> <PHRASE Label="la_fld_FrontLanguage" Module="Core" Type="1" Column="TGFuZ3VhZ2U=">TGFuZ3VhZ2U=</PHRASE> <PHRASE Label="la_fld_FrontRegistration" Module="Core" Type="1" Column="QWxsb3cgUmVnaXN0cmF0aW9u">QWxsb3cgUmVnaXN0cmF0aW9uIG9uIEZyb250LWVuZA==</PHRASE> <PHRASE Label="la_fld_FullName" Module="Core" Type="1" Column="RnVsbCBOYW1l">RnVsbCBOYW1l</PHRASE> <PHRASE Label="la_fld_Group" Module="Core" Type="1" Column="R3JvdXA=">VXNlciBHcm91cA==</PHRASE> <PHRASE Label="la_fld_GroupDisplayOrder" Module="Core" Type="1" Hint="T3JkZXIgdG8gdXNlLCB3aGVuIHNvcnRpbmcgMiByZWNvcmRzIHdpdGggc2FtZSAiRGlzcGxheSBPcmRlciIu">R3JvdXAgRGlzcGxheSBPcmRlcg==</PHRASE> <PHRASE Label="la_fld_GroupId" Module="Core" Type="1">SUQ=</PHRASE> <PHRASE Label="la_fld_GroupName" Module="Core" Type="1" Column="R3JvdXAgTmFtZQ==">R3JvdXAgTmFtZQ==</PHRASE> <PHRASE Label="la_fld_Height" Module="Core" Type="1">SGVpZ2h0</PHRASE> <PHRASE Label="la_fld_HelpfulCount" Module="Core" Type="1" Column="SGVscGZ1bA==">UmV2aWV3IFdhcyBIZWxwZnVs</PHRASE> <PHRASE Label="la_fld_HintTranslation" Module="Core" Type="1" Hint="QXNzb2NpYXRlZCBmaWVsZCB1c2FnZSB0cmFuc2xhdGlvbg==">SGludCBQaHJhc2U=</PHRASE> <PHRASE Label="la_fld_Hits" Module="Core" Type="1" Column="SGl0cw==">SGl0cw==</PHRASE> <PHRASE Label="la_fld_Hot" Module="Core" Type="1">SG90</PHRASE> <PHRASE Label="la_fld_HtmlEmailTemplate" Module="Core" Type="1" Hint="VGhpcyBmaWVsZCBoYXMgYW4gSFRNTCB2ZXJzaW9uIG9mIEVtYWlsIERlc2lnbiBUZW1wbGF0ZSBhbmQgbXVzdCBjb250YWlucyBhdCBsZWFzdCBhICIkYm9keSIgdGFnIHVubGVzcyBUZXh0IFZlcnNpb24gZmllbGQgaXMgcG9wdWxhdGVkIGFscmVhZHkuIEl0IHdpbGwgYmUgdXNlZCBhcyBhIGRlc2lnbiBmb3IgYWxsIGVtYWlscyBzZW5kIG91dCBieSB0aGUgd2Vic2l0ZS4=">SFRNTCBWZXJzaW9u</PHRASE> <PHRASE Label="LA_FLD_HTMLVERSION" Module="Core" Type="1">SFRNTCBWZXJzaW9u</PHRASE> <PHRASE Label="la_fld_IconDisabledURL" Module="Core" Type="1">SWNvbiBVUkwgKGRpc2FibGVkKQ==</PHRASE> <PHRASE Label="la_fld_IconURL" Module="Core" Type="1">SWNvbiBVUkw=</PHRASE> <PHRASE Label="la_fld_Id" Module="Core" Type="1" Column="SUQ=">SUQ=</PHRASE> <PHRASE Label="la_fld_Image" Module="Core" Type="1" Column="SW1hZ2U=">SW1hZ2U=</PHRASE> <PHRASE Label="la_fld_ImportCategory" Module="Core" Type="1">SW1wb3J0IFNlY3Rpb24=</PHRASE> <PHRASE Label="la_fld_ImportColumns" Module="Core" Type="1">SW1wb3J0IENvbHVtbnM=</PHRASE> <PHRASE Label="la_fld_ImportFile" Module="Core" Type="1">SW1wb3J0IEZpbGU=</PHRASE> <PHRASE Label="la_fld_ImportFilename" Module="Core" Type="1">SW1wb3J0IEZpbGVuYW1l</PHRASE> <PHRASE Label="la_fld_ImportOverwrite" Module="Core" Type="1" Hint="RW5hYmxpbmcgdGhpcyBvcHRpb24gd2lsbCB1bmRvIGFueSBjaGFuZ2VzIHlvdSBoYXZlIG1hZGUgdG8gZXhpc3RpbmcgcGhyYXNlcw==">T3ZlcndyaXRlIEV4aXN0aW5nIFBocmFzZXM=</PHRASE> <PHRASE Label="la_fld_ImportSynced" Module="Core" Type="1" Hint="RW5hYmxpbmcgdGhpcyBvcHRpb24gd2lsbCBtYXJrIGFsbCBuZXcgbGFiZWxzIGZyb20gdGhpcyBsYW5ndWFnZSBwYWNrIHN5bmNlZCB3aXRoIGFsbCBvdGhlciBsYW5ndWFnZXMu">SW1wb3J0IE5ldyBQaHJhc2VzIGFzIFN5bmNlZA==</PHRASE> <PHRASE Label="la_fld_IncludeFieldTitles" Module="Core" Type="1">SW5jbHVkZSBmaWVsZCB0aXRsZXM=</PHRASE> <PHRASE Label="la_fld_IncludeSublevels" Module="Core" Type="1" Column="SW5jbHVkZSBTdWJsZXZlbHM=">SW5jbHVkZSBTdWJsZXZlbHM=</PHRASE> <PHRASE Label="la_fld_InputDateFormat" Module="Core" Type="1">SW5wdXQgRGF0ZSBGb3JtYXQ=</PHRASE> <PHRASE Label="la_fld_InputTimeFormat" Module="Core" Type="1">SW5wdXQgVGltZSBGb3JtYXQ=</PHRASE> <PHRASE Label="la_fld_InstallModules" Module="Core" Type="1">SW5zdGFsbCBNb2R1bGVz</PHRASE> <PHRASE Label="la_fld_InstallPhraseTypes" Module="Core" Type="1">SW5zdGFsbCBQaHJhc2UgVHlwZXM=</PHRASE> <PHRASE Label="la_fld_IPAddress" Module="Core" Type="1" Column="SVAgQWRkcmVzcw==">SVAgQWRkcmVzcw==</PHRASE> <PHRASE Label="la_fld_IPRestrictions" Module="Core" Type="1">SVAgUmVzdHJpY3Rpb25z</PHRASE> <PHRASE Label="la_fld_IsBaseCategory" Module="Core" Type="1">VXNlIGN1cnJlbnQgc2VjdGlvbiBhcyByb290IGZvciB0aGUgZXhwb3J0</PHRASE> <PHRASE Label="la_fld_ISOCode" Module="Core" Type="1" Column="SVNPIENvZGU=">SVNPIENvZGU=</PHRASE> <PHRASE Label="la_fld_IsPrimary" Module="Core" Type="1" Column="UHJpbWFyeQ==">UHJpbWFyeQ==</PHRASE> <PHRASE Label="la_fld_IsRequired" Module="Core" Type="1">UmVxdWlyZWQ=</PHRASE> <PHRASE Label="la_fld_IsSystem" Module="Core" Type="1" Column="U3lzdGVt">SXMgU3lzdGVt</PHRASE> <PHRASE Label="la_fld_IsSystemTemplate" Module="Core" Type="1">U3lzdGVtIFRlbXBsYXRl</PHRASE> <PHRASE Label="la_fld_ItemField" Module="Core" Type="1" Column="VXNlciBGaWVsZA==">VXNlciBGaWVsZA==</PHRASE> <PHRASE Label="la_fld_ItemId" Module="Core" Type="1" Column="SXRlbSBJRA==">SXRlbSBJRA==</PHRASE> <PHRASE Label="la_fld_ItemName" Module="Core" Type="1" Column="SXRlbSBOYW1l">SXRlbSBOYW1l</PHRASE> <PHRASE Label="la_fld_ItemPrefix" Module="Core" Type="1" Hint="VW5pdCBjb25maWcgcHJlZml4LCB3aGVyZSBmaWx0ZXIgZmllbGQgYmVsb25ncw==" Column="SXRlbSBQcmVmaXg=">SXRlbSBQcmVmaXg=</PHRASE> <PHRASE Label="la_fld_ItemTemplate" Module="Core" Type="1">SXRlbSBUZW1wbGF0ZQ==</PHRASE> <PHRASE Label="la_fld_Language" Module="Core" Type="1" Column="TGFuZ3VhZ2U=">TGFuZ3VhZ2U=</PHRASE> <PHRASE Label="la_fld_LanguageFile" Module="Core" Type="1">TGFuZ3VhZ2UgRmlsZQ==</PHRASE> <PHRASE Label="la_fld_LanguageId" Module="Core" Type="1">TGFuZ3VhZ2UgSUQ=</PHRASE> <PHRASE Label="la_fld_Languages" Module="Core" Type="1">TGFuZ3VhZ2Vz</PHRASE> <PHRASE Label="la_fld_LastName" Module="Core" Type="1" Column="TGFzdCBOYW1l">TGFzdCBOYW1l</PHRASE> <PHRASE Label="la_fld_LastRunOn" Module="Core" Type="1" Column="TGFzdCBSdW4gT24=">TGFzdCBSdW4gT24=</PHRASE> <PHRASE Label="la_fld_LastRunStatus" Module="Core" Type="1" Column="TGFzdCBSdW4gU3RhdHVz">TGFzdCBSdW4gU3RhdHVz</PHRASE> <PHRASE Label="la_fld_LastTimeoutOn" Module="Core" Type="1" Hint="RGF0YS90aW1lIHdoZW4gdGhlIGV2ZW50IGhhcyB0aW1lZCBvdXQgbGFzdCB0aW1lLg==" Column="TGFzdCBUaW1lb3V0IE9u">TGFzdCBUaW1lb3V0IE9u</PHRASE> <PHRASE Label="la_fld_LastUpdatedOn" Module="Core" Type="1" Column="TGFzdCBVcGRhdGVkIE9u">TGFzdCBVcGRhdGVkIE9u</PHRASE> <PHRASE Label="la_fld_Left" Module="Core" Type="1">TGVmdA==</PHRASE> <PHRASE Label="la_fld_LineEndings" Module="Core" Type="1">TGluZSBlbmRpbmdz</PHRASE> <PHRASE Label="la_fld_LineEndingsInside" Module="Core" Type="1">TGluZSBFbmRpbmdzIEluc2lkZSBGaWVsZHM=</PHRASE> <PHRASE Label="la_fld_LinkType" Module="Core" Type="1" Column="TGluayBUeXBl">TGluayBUeXBl</PHRASE> <PHRASE Label="la_fld_ListingId" Module="Core" Type="1" Column="TGlzdGluZyBJZA==">SUQ=</PHRASE> <PHRASE Label="la_fld_ListingType" Module="Core" Type="1" Column="TGlzdGluZyBUeXBl">TGlzdGluZyBUeXBl</PHRASE> <PHRASE Label="la_fld_Locale" Module="Core" Type="1">TG9jYWxl</PHRASE> <PHRASE Label="la_fld_LocalName" Module="Core" Type="1" Column="TmFtZQ==">TG9jYWwgTmFtZQ==</PHRASE> <PHRASE Label="la_fld_Location" Module="Core" Type="1" Column="TG9jYXRpb24=">TG9jYXRpb24=</PHRASE> <PHRASE Label="la_fld_LogBacktrace" Module="Core" Type="1" Column="QmFja3RyYWNl">QmFja3RyYWNl</PHRASE> <PHRASE Label="la_fld_LogCode" Module="Core" Type="1" Column="Q29kZQ==">Q29kZQ==</PHRASE> <PHRASE Label="la_fld_LogEventName" Module="Core" Type="1" Column="RXZlbnQgTmFtZQ==">RXZlbnQgTmFtZQ==</PHRASE> <PHRASE Label="la_fld_LogHostname" Module="Core" Type="1" Column="SG9zdG5hbWU=">SG9zdG5hbWU=</PHRASE> <PHRASE Label="la_fld_Login" Module="Core" Type="1" Column="TG9naW4=">TG9naW4=</PHRASE> <PHRASE Label="la_fld_LogInterface" Module="Core" Type="1" Column="SW50ZXJmYWNl">SW50ZXJmYWNl</PHRASE> <PHRASE Label="la_fld_LogLevel" Module="Core" Type="1" Column="TG9nIExldmVs">TG9nIExldmVs</PHRASE> <PHRASE Label="la_fld_LogMemoryUsed" Module="Core" Type="1" Column="TWVtb3J5IFVzZWQ=">TWVtb3J5IFVzZWQ=</PHRASE> <PHRASE Label="la_fld_LogMessage" Module="Core" Type="1" Column="TWVzc2FnZQ==">TWVzc2FnZQ==</PHRASE> <PHRASE Label="la_fld_LogNotificationStatus" Module="Core" Type="1" Column="Tm90aWZpY2F0aW9uIFN0YXR1cw==">Tm90aWZpY2F0aW9uIFN0YXR1cw==</PHRASE> <PHRASE Label="la_fld_Logo" Module="Core" Type="1">TG9nbyBpbWFnZQ==</PHRASE> <PHRASE Label="la_fld_LogoBottom" Module="Core" Type="1">Qm90dG9tIExvZ28gSW1hZ2U=</PHRASE> <PHRASE Label="la_fld_LogoLogin" Module="Core" Type="1">TG9nbyBMb2dpbg==</PHRASE> <PHRASE Label="la_fld_LogProcessId" Module="Core" Type="1" Column="UHJvY2VzcyBJRA==">UHJvY2VzcyBJRA==</PHRASE> <PHRASE Label="la_fld_LogRequestData" Module="Core" Type="1" Column="UmVxdWVzdCBEYXRh">UmVxdWVzdCBEYXRh</PHRASE> <PHRASE Label="la_fld_LogRequestSource" Module="Core" Type="1" Column="UmVxdWVzdCBTb3VyY2U=">UmVxdWVzdCBTb3VyY2U=</PHRASE> <PHRASE Label="la_fld_LogRequestURI" Module="Core" Type="1" Column="UmVxdWVzdCBVUkk=">UmVxdWVzdCBVUkk=</PHRASE> <PHRASE Label="la_fld_LogSessionData" Module="Core" Type="1" Column="U2Vzc2lvbiBEYXRh">U2Vzc2lvbiBEYXRh</PHRASE> <PHRASE Label="la_fld_LogSessionKey" Module="Core" Type="1" Column="U2Vzc2lvbiBLZXk=">U2Vzc2lvbiBLZXk=</PHRASE> <PHRASE Label="la_fld_LogSourceFileLine" Module="Core" Type="1" Column="U291cmNlIEZpbGUgTGluZQ==">U291cmNlIEZpbGUgTGluZQ==</PHRASE> <PHRASE Label="la_fld_LogSourceFilename" Module="Core" Type="1" Column="U291cmNlIEZpbGVuYW1l">U291cmNlIEZpbGVuYW1l</PHRASE> <PHRASE Label="la_fld_LogTimestamp" Module="Core" Type="1" Column="VGltZXN0YW1w">VGltZXN0YW1w</PHRASE> <PHRASE Label="la_fld_LogType" Module="Core" Type="1" Column="VHlwZQ==">VHlwZQ==</PHRASE> <PHRASE Label="la_fld_LogUserData" Module="Core" Type="1" Column="VXNlciBEYXRh">VXNlciBEYXRh</PHRASE> <PHRASE Label="la_fld_MarginBottom" Module="Core" Type="1">TWFyZ2luIEJvdHRvbQ==</PHRASE> <PHRASE Label="la_fld_MarginLeft" Module="Core" Type="1">TWFyZ2luIExlZnQ=</PHRASE> <PHRASE Label="la_fld_MarginRight" Module="Core" Type="1">TWFyZ2luIFJpZ2h0</PHRASE> <PHRASE Label="la_fld_Margins" Module="Core" Type="1">TWFyZ2lucw==</PHRASE> <PHRASE Label="la_fld_MarginTop" Module="Core" Type="1">TWFyZ2luIFRvcA==</PHRASE> <PHRASE Label="la_fld_MasterId" Module="Core" Type="1" Column="TWFzdGVyIElE">TWFzdGVyIElE</PHRASE> <PHRASE Label="la_fld_MasterPrefix" Module="Core" Type="1" Column="TWFzdGVyIFByZWZpeA==">TWFzdGVyIFByZWZpeA==</PHRASE> <PHRASE Label="la_fld_MaxCategories" Module="Core" Type="1">TWF4aW11bSBudW1iZXIgb2YgU2VjdGlvbnMgb24gSXRlbSBjYW4gYmUgYWRkZWQgdG8=</PHRASE> <PHRASE Label="la_fld_MenuIcon" Module="Core" Type="1">Q3VzdG9tIE1lbnUgSWNvbiAoaWUuIGltZy9tZW51X3Byb2R1Y3RzLmdpZik=</PHRASE> <PHRASE Label="la_fld_MenuStatus" Module="Core" Type="1">TWVudSBTdGF0dXM=</PHRASE> <PHRASE Label="la_fld_MergeToSubmission" Module="Core" Type="1">TWVyZ2UgdG8gU3VibWlzc2lvbg==</PHRASE> <PHRASE Label="la_fld_Message" Module="Core" Type="1" Column="TWVzc2FnZQ==">TWVzc2FnZQ==</PHRASE> <PHRASE Label="la_fld_MessageBody" Module="Core" Type="1">TWVzc2FnZSBCb2R5</PHRASE> <PHRASE Label="la_fld_MessageText" Module="Core" Type="1" Column="UGxhaW4gVGV4dA==">UGxhaW4gVGV4dCBWZXJzaW9u</PHRASE> <PHRASE Label="la_fld_MessageType" Module="Core" Type="1">TWVzc2FnZSBUeXBl</PHRASE> <PHRASE Label="la_fld_MetaDescription" Module="Core" Type="1">TWV0YSBEZXNjcmlwdGlvbg==</PHRASE> <PHRASE Label="la_fld_MetaKeywords" Module="Core" Type="1">TWV0YSBLZXl3b3Jkcw==</PHRASE> <PHRASE Label="la_fld_MisspelledWord" Module="Core" Type="1" Column="TWlzc3BlbGxlZCBXb3Jk">TWlzc3BlbGxlZCBXb3Jk</PHRASE> <PHRASE Label="la_fld_Mode" Module="Core" Type="1" Column="TW9kZQ==">TW9kZQ==</PHRASE> <PHRASE Label="la_fld_Modified" Module="Core" Type="1" Column="TW9kaWZpZWQgT24=">TW9kaWZpZWQ=</PHRASE> <PHRASE Label="la_fld_Module" Module="Core" Type="1" Column="TW9kdWxl">TW9kdWxl</PHRASE> <PHRASE Label="la_fld_ModuleName" Module="Core" Type="1">TW9kdWxl</PHRASE> <PHRASE Label="la_fld_MultiLingual" Module="Core" Type="1">TXVsdGlsaW5ndWFs</PHRASE> <PHRASE Label="la_fld_Name" Module="Core" Type="1" Column="TmFtZQ==">TmFtZQ==</PHRASE> <PHRASE Label="la_fld_New" Module="Core" Type="1">TmV3</PHRASE> <PHRASE Label="la_fld_NextRunOn" Module="Core" Type="1" Column="TmV4dCBSdW4gT24=">TmV4dCBSdW4gT24=</PHRASE> <PHRASE Label="la_fld_Notes" Module="Core" Type="1">Tm90ZXM=</PHRASE> <PHRASE Label="la_fld_NotHelpfulCount" Module="Core" Type="1" Column="Tm90IEhlbHBmdWw=">UmV2aWV3IFdhc24ndCBIZWxwZnVs</PHRASE> <PHRASE Label="la_fld_NumberOfClicks" Module="Core" Type="1" Column="TnVtYmVyIE9mIENsaWNrcw==">TnVtYmVyIE9mIENsaWNrcw==</PHRASE> <PHRASE Label="la_fld_NumberOfViews" Module="Core" Type="1" Column="TnVtYmVyIE9mIFZpZXdz">TnVtYmVyIE9mIFZpZXdz</PHRASE> <PHRASE Label="la_fld_OccuredOn" Module="Core" Type="1" Column="T2NjdXJlZCBPbg==">T2NjdXJlZCBPbg==</PHRASE> <PHRASE Label="la_fld_OpenInNewWindow" Module="Core" Type="1">T3BlbiBJbiBOZXcgV2luZG93</PHRASE> <PHRASE Label="la_fld_Options" Module="Core" Type="1">T3B0aW9ucw==</PHRASE> <PHRASE Label="la_fld_OptionTitle" Module="Core" Type="1">T3B0aW9uIFRpdGxl</PHRASE> <PHRASE Label="la_fld_Order" Module="Core" Type="1" Column="T3JkZXI=">T3JkZXI=</PHRASE> <PHRASE Label="la_fld_OtherRecipients" Module="Core" Type="1" Column="T3RoZXIgUmVjaXBpZW50cw==">T3RoZXIgUmVjaXBpZW50cw==</PHRASE> <PHRASE Label="la_fld_Output" Module="Core" Type="1" Column="T3V0cHV0">T3V0cHV0</PHRASE> <PHRASE Label="la_fld_OverridePageCacheKey" Module="Core" Type="1">T3ZlcndyaXRlIERlZmF1bHQgQ2FjaGluZyBLZXk=</PHRASE> <PHRASE Label="la_fld_PackName" Module="Core" Type="1" Column="UGFjayBOYW1l">UGFjayBOYW1l</PHRASE> <PHRASE Label="la_fld_PaddingBottom" Module="Core" Type="1">UGFkZGluZyBCb3R0b20=</PHRASE> <PHRASE Label="la_fld_PaddingLeft" Module="Core" Type="1">UGFkZGluZyBMZWZ0</PHRASE> <PHRASE Label="la_fld_PaddingRight" Module="Core" Type="1">UGFkZGluZyBSaWdodA==</PHRASE> <PHRASE Label="la_fld_Paddings" Module="Core" Type="1">UGFkZGluZ3M=</PHRASE> <PHRASE Label="la_fld_PaddingTop" Module="Core" Type="1">UGFkZGluZyBUb3A=</PHRASE> <PHRASE Label="la_fld_PageCacheKey" Module="Core" Type="1">Q3VzdG9tIENhY2hpbmcgS2V5</PHRASE> <PHRASE Label="la_fld_PageContentTitle" Module="Core" Type="1">Jmx0O1RJVExFJmd0OyBUYWc=</PHRASE> <PHRASE Label="la_fld_PageExpiration" Module="Core" Type="1" Hint="SG93IHNvb24gKGluIHNlY29uZHMpIHRoZSBzZWN0aW9uIGNhY2hlIHNob3VsZCBhdXRvLWV4cGlyZSBhZnRlciBpdCdzIGNyZWF0aW9uLiBCeSBkZWZhdWx0IHN5c3RlbSB0ZW5kcyB0byByZWJ1aWxkIHRoZSBjYWNoZSBvbmx5IHdoZW4gaXQncyBwcm9wZXJ0aWVzIG9yIGVsZW1lbnRzIGhhdmUgY2hhbmdlZC4=">Q2FjaGUgRXhwaXJhdGlvbiBpbiBzZWNvbmRz</PHRASE> <PHRASE Label="la_fld_PageMentTitle" Module="Core" Type="1">VGl0bGUgKE1lbnUgSXRlbSk=</PHRASE> <PHRASE Label="la_fld_PageTitle" Module="Core" Type="1" Column="U2VjdGlvbiBUaXRsZQ==">U2VjdGlvbiBUaXRsZQ==</PHRASE> <PHRASE Label="la_fld_ParentItemId" Module="Core" Type="1" Column="UGFyZW50IEl0ZW0gSUQ=">UGFyZW50IEl0ZW0gSUQ=</PHRASE> <PHRASE Label="la_fld_ParentItemName" Module="Core" Type="1" Column="UGFyZW50IEl0ZW0gTmFtZQ==">UGFyZW50IEl0ZW0gTmFtZQ==</PHRASE> <PHRASE Label="la_fld_ParentSection" Module="Core" Type="1">UGFyZW50IFNlY3Rpb24=</PHRASE> <PHRASE Label="la_fld_Password" Module="Core" Type="1">UGFzc3dvcmQ=</PHRASE> <PHRASE Label="la_fld_PercentsCompleted" Module="Core" Type="1">UGVyY2VudHMgQ29tcGxldGVk</PHRASE> <PHRASE Label="la_fld_Phone" Module="Core" Type="1" Column="UGhvbmU=">UGhvbmU=</PHRASE> <PHRASE Label="la_fld_Phrase" Module="Core" Type="1" Column="UGhyYXNl">TGFiZWw=</PHRASE> <PHRASE Label="la_fld_PhraseType" Module="Core" Type="1" Column="VHlwZQ==">UGhyYXNlIFR5cGU=</PHRASE> <PHRASE Label="la_fld_Pop" Module="Core" Type="1">UG9w</PHRASE> <PHRASE Label="la_fld_Popular" Module="Core" Type="1">UG9wdWxhcg==</PHRASE> <PHRASE Label="la_fld_Port" Module="Core" Type="1">UG9ydA==</PHRASE> <PHRASE Label="la_fld_Position" Module="Core" Type="1">UG9zaXRpb24=</PHRASE> <PHRASE Label="la_fld_Prefix" Module="Core" Type="1" Column="UHJlZml4">UHJlZml4</PHRASE> <PHRASE Label="la_fld_Primary" Module="Core" Type="1" Column="UHJpbWFyeQ==">UHJpbWFyeQ==</PHRASE> <PHRASE Label="la_fld_PrimaryCategory" Module="Core" Type="1">UHJpbWFyeSBTZWN0aW9u</PHRASE> <PHRASE Label="la_fld_PrimaryLang" Module="Core" Type="1">UHJpbWFyeQ==</PHRASE> <PHRASE Label="la_fld_PrimaryTranslation" Module="Core" Type="1">UHJpbWFyeSBMYW5ndWFnZSBQaHJhc2U=</PHRASE> <PHRASE Label="la_fld_Priority" Module="Core" Type="1" Column="T3JkZXI=">T3JkZXI=</PHRASE> <PHRASE Label="la_fld_ProcessUnmatchedEmails" Module="Core" Type="1">Q29udmVydCB1bm1hdGNoZWQgZS1tYWlscyBpbnRvIG5ldyBzdWJtaXNzaW9ucw==</PHRASE> <PHRASE Label="la_fld_PromoBlockGroup" Module="Core" Type="1">UHJvbW8gQmxvY2sgR3JvdXA=</PHRASE> <PHRASE Label="la_fld_Protected" Module="Core" Type="1" Column="UHJvdGVjdGVk">UHJvdGVjdGVk</PHRASE> <PHRASE Label="la_fld_Qty" Module="Core" Type="1" Column="UXR5">UXVhbnRpdHk=</PHRASE> <PHRASE Label="la_fld_RangeCount" Module="Core" Type="1" Column="UmFuZ2UgQ291bnQ=">UmFuZ2UgQ291bnQ=</PHRASE> <PHRASE Label="la_fld_Rating" Module="Core" Type="1" Column="UmF0aW5n">UmF0aW5n</PHRASE> <PHRASE Label="la_fld_Recipient" Module="Core" Type="1" Column="UmVjaXBpZW50">UmVjaXBpZW50</PHRASE> <PHRASE Label="la_fld_RecipientAddress" Module="Core" Type="1">UmVjaXBpZW50J3MgQWRkcmVzcw==</PHRASE> <PHRASE Label="la_fld_RecipientAddressType" Module="Core" Type="1">UmVjaXBpZW50J3MgQWRkcmVzcyBUeXBl</PHRASE> <PHRASE Label="la_fld_RecipientName" Module="Core" Type="1">UmVjaXBpZW50J3MgTmFtZQ==</PHRASE> <PHRASE Label="la_fld_Recipients" Module="Core" Type="1">UmVjaXBpZW50cw==</PHRASE> <PHRASE Label="la_fld_RecipientType" Module="Core" Type="1" Column="UmVjaXBpZW50IFR5cGU=">UmVjaXBpZW50IFR5cGU=</PHRASE> <PHRASE Label="la_fld_RecipientUser" Module="Core" Type="1" Column="UmVjaXBpZW50IFVzZXI=">UmVjaXBpZW50IFVzZXI=</PHRASE> <PHRASE Label="la_fld_RedirectOnIPMatch" Module="Core" Type="1">Rm9yY2UgUmVkaXJlY3QgKHdoZW4gdXNlcidzIElQIG1hdGNoZXMp</PHRASE> <PHRASE Label="la_fld_ReferrerURL" Module="Core" Type="1" Column="UmVmZXJyZXIgVVJM">UmVmZXJyZXIgVVJM</PHRASE> <PHRASE Label="la_fld_RelatedSearchKeyword" Module="Core" Type="1">S2V5d29yZA==</PHRASE> <PHRASE Label="la_fld_RelationshipType" Module="Core" Type="1" Column="UmVsYXRpb24gVHlwZQ==">VHlwZQ==</PHRASE> <PHRASE Label="la_fld_RemoteUrl" Module="Core" Type="1">UmVtb3RlIFVSTA==</PHRASE> <PHRASE Label="la_fld_ReplaceDuplicates" Module="Core" Type="1">UmVwbGFjZSBEdXBsaWNhdGVz</PHRASE> <PHRASE Label="la_fld_Replacement" Module="Core" Type="1" Column="UmVwbGFjZW1lbnQ=">UmVwbGFjZW1lbnQ=</PHRASE> <PHRASE Label="la_fld_ReplacementTags" Module="Core" Type="1">UmVwbGFjZW1lbnQgVGFncw==</PHRASE> <PHRASE Label="la_fld_RepliedOn" Module="Core" Type="1" Column="UmVwbGllZCBPbg==">UmVwbGllZCBPbg==</PHRASE> <PHRASE Label="la_fld_ReplyBcc" Module="Core" Type="1">UmVwbHkgQmNj</PHRASE> <PHRASE Label="la_fld_ReplyCc" Module="Core" Type="1">UmVwbHkgQ2M=</PHRASE> <PHRASE Label="la_fld_ReplyFromEmail" Module="Core" Type="1">UmVwbHkgRnJvbSBFLW1haWw=</PHRASE> <PHRASE Label="la_fld_ReplyFromName" Module="Core" Type="1">UmVwbHkgRnJvbSBOYW1l</PHRASE> <PHRASE Label="la_fld_ReplyMessageSignature" Module="Core" Type="1">UmVwbHkgTWVzc2FnZSBTaWduYXR1cmU=</PHRASE> <PHRASE Label="la_fld_ReplyStatus" Module="Core" Type="1" Column="UmVwbGllZA==">UmVwbGllZA==</PHRASE> <PHRASE Label="la_fld_ReportedBy" Module="Core" Type="1" Column="UmVwb3J0ZWQgQnk=">UmVwb3J0ZWQgQnk=</PHRASE> <PHRASE Label="la_fld_ReportedOn" Module="Core" Type="1" Column="UmVwb3J0ZWQgT24=">UmVwb3J0ZWQgT24=</PHRASE> <PHRASE Label="la_fld_Required" Module="Core" Type="1" Column="UmVxdWlyZWQ=">UmVxdWlyZWQ=</PHRASE> <PHRASE Label="la_fld_RequireLogin" Module="Core" Type="1" Column="UmVxdWlyZSBMb2dpbg==">UmVxdWlyZSBMb2dpbg==</PHRASE> <PHRASE Label="la_fld_RequireSSL" Module="Core" Type="1" Column="UmVxdWlyZSBTU0w=">UmVxdWlyZSBTU0w=</PHRASE> <PHRASE Label="la_fld_ReviewText" Module="Core" Type="1" Column="Q29tbWVudA==">Q29tbWVudA==</PHRASE> <PHRASE Label="la_fld_RevisionNumber" Module="Core" Type="1" Column="UmV2aXNpb24gTnVtYmVy">UmV2aXNpb24gTnVtYmVy</PHRASE> <PHRASE Label="la_fld_RevisionTitle" Module="Core" Type="1" Column="UmV2aXNpb24gVGl0bGU=">UmV2aXNpb24gVGl0bGU=</PHRASE> <PHRASE Label="la_fld_RotationDelay" Module="Core" Type="1" Column="UHJvbW8gUm90YXRpb24gRGVsYXkgKHNlY29uZHMp">UHJvbW8gUm90YXRpb24gRGVsYXkgKHNlY29uZHMp</PHRASE> <PHRASE Label="la_fld_RuleType" Module="Core" Type="1" Column="UnVsZSBUeXBl">UnVsZSBUeXBl</PHRASE> <PHRASE Label="la_fld_RunSchedule" Module="Core" Type="1" Column="UnVuIFNjaGVkdWxl">UnVuIFNjaGVkdWxl</PHRASE> <PHRASE Label="la_fld_RunTime" Module="Core" Type="1" Column="UnVuIFRpbWU=">UnVuIFRpbWU=</PHRASE> <PHRASE Label="la_fld_SameAsThumb" Module="Core" Type="1">U2FtZSBBcyBUaHVtYg==</PHRASE> <PHRASE Label="la_fld_ScheduleDate" Module="Core" Type="1">U2NoZWR1bGUgRGF0ZQ==</PHRASE> <PHRASE Label="la_fld_SearchTerm" Module="Core" Type="1" Column="U2VhcmNoIFRlcm0=">U2VhcmNoIFRlcm0=</PHRASE> <PHRASE Label="la_fld_Sender" Module="Core" Type="1" Column="U2VuZGVy">U2VuZGVy</PHRASE> <PHRASE Label="la_fld_SenderAddress" Module="Core" Type="1">U2VuZGVyJ3MgQWRkcmVzcw==</PHRASE> <PHRASE Label="la_fld_SenderName" Module="Core" Type="1">U2VuZGVyJ3MgTmFtZQ==</PHRASE> <PHRASE Label="la_fld_SentOn" Module="Core" Type="1" Column="U2VudCBPbg==">U2VudCBPbg==</PHRASE> <PHRASE Label="la_fld_SentStatus" Module="Core" Type="1" Column="U2VudA==">U2VudA==</PHRASE> <PHRASE Label="la_fld_Server" Module="Core" Type="1">U2VydmVy</PHRASE> <PHRASE Label="la_fld_SessionLogId" Module="Core" Type="1" Column="U2Vzc2lvbiBMb2cgSUQ=">U2Vzc2lvbiBMb2cgSUQ=</PHRASE> <PHRASE Label="la_fld_Settings" Module="Core" Type="1" Column="U2V0dGluZ3M=">U2V0dGluZ3M=</PHRASE> <PHRASE Label="la_fld_ShortDateFormat" Module="Core" Type="1" Column="U2hvcnQgRGF0ZSBGb3JtYXQ=">U2hvcnQgRGF0ZSBGb3JtYXQ=</PHRASE> <PHRASE Label="la_fld_ShortIsoCode" Module="Core" Type="1" Column="U2hvcnQgSVNPIENvZGU=">U2hvcnQgSVNPIENvZGU=</PHRASE> <PHRASE Label="la_fld_ShortTimeFormat" Module="Core" Type="1" Column="U2hvcnQgVGltZSBGb3JtYXQ=">U2hvcnQgVGltZSBGb3JtYXQ=</PHRASE> <PHRASE Label="la_fld_SimpleSearch" Module="Core" Type="1">U2ltcGxlIFNlYXJjaA==</PHRASE> <PHRASE Label="la_fld_SiteDomainLimitation" Module="Core" Type="1" Column="U2l0ZSBEb21haW4gTGltaXRhdGlvbg==">U2l0ZSBEb21haW4gTGltaXRhdGlvbg==</PHRASE> <PHRASE Label="la_fld_SkinName" Module="Core" Type="1" Column="TmFtZQ==">TmFtZQ==</PHRASE> <PHRASE Label="la_fld_SkipFirstRow" Module="Core" Type="1">U2tpcCBGaXJzdCBSb3c=</PHRASE> <PHRASE Label="la_fld_SortValues" Module="Core" Type="1">U29ydCBWYWx1ZXM=</PHRASE> <PHRASE Label="la_fld_SourceColumnTranslation" Module="Core" Type="1" Column="U291cmNlIENvbHVtbiBQaHJhc2U=">U291cmNlIENvbHVtbiBQaHJhc2UgKGZyb20gJXMp</PHRASE> <PHRASE Label="la_fld_SourceHintTranslation" Module="Core" Type="1" Column="U291cmNlIEhpbnQgUGhyYXNl">U291cmNlIEhpbnQgUGhyYXNlIChmcm9tICVzKQ==</PHRASE> <PHRASE Label="la_fld_SourceHtmlBody" Module="Core" Type="1" Column="U291cmNlIEhUTUwgVmVyc2lvbg==">U291cmNlIEhUTUwgVmVyc2lvbiAoZnJvbSAlcyk=</PHRASE> <PHRASE Label="la_fld_SourcePlainTextBody" Module="Core" Type="1" Column="U291cmNlIFRleHQgVmVyc2lvbg==">U291cmNlIFRleHQgVmVyc2lvbiAoZnJvbSAlcyk=</PHRASE> <PHRASE Label="la_fld_SourceSubject" Module="Core" Type="1" Column="U291cmNlIFN1YmplY3Q=">U291cmNlIFN1YmplY3QgKGZyb20gJXMp</PHRASE> <PHRASE Label="la_fld_SourceTranslation" Module="Core" Type="1" Column="U291cmNl">U291cmNlIFBocmFzZSAoZnJvbSAlcyk=</PHRASE> <PHRASE Label="la_fld_SSLDomainName" Module="Core" Type="1" Hint="d3d3LmRvbWFpbi5jb20=" Column="U1NMIERvbWFpbiBOYW1l">U1NMIERvbWFpbiBOYW1l</PHRASE> <PHRASE Label="la_fld_StartDate" Module="Core" Type="1" Column="U3RhcnQgRGF0ZQ==">U3RhcnQgRGF0ZQ==</PHRASE> <PHRASE Label="la_fld_State" Module="Core" Type="1" Column="U3RhdGU=">U3RhdGU=</PHRASE> <PHRASE Label="la_fld_StateCountry" Module="Core" Type="1" Column="U3RhdGUgQ291bnRyeQ==">U3RhdGUgQ291bnRyeQ==</PHRASE> <PHRASE Label="la_fld_Status" Module="Core" Type="1" Column="U3RhdHVz">U3RhdHVz</PHRASE> <PHRASE Label="la_fld_Sticky" Module="Core" Type="1" Column="U3RpY2t5">U3RpY2t5</PHRASE> <PHRASE Label="la_fld_StopWord" Module="Core" Type="1" Column="U3RvcCBXb3Jk">U3RvcCBXb3Jk</PHRASE> <PHRASE Label="la_fld_StylesheetFile" Module="Core" Type="1" Hint="UmVsYXRpdmUgcGF0aCB0byBzdHlsZXNoZWV0IGZpbGUgd2l0aGluIHRoZW1lIChlLmcuICJpbmMvc3R5bGUuY3NzIiku" Column="U3R5bGVzaGVldCBGaWxl">U3R5bGVzaGVldCBGaWxl</PHRASE> <PHRASE Label="la_fld_Subject" Module="Core" Type="1" Column="U3ViamVjdA==">U3ViamVjdA==</PHRASE> <PHRASE Label="la_fld_SubmissionTime" Module="Core" Type="1">U3VibWl0dGVkIE9u</PHRASE> <PHRASE Label="la_fld_SubmitNotifyEmail" Module="Core" Type="1" Hint="RW1haWwgYWRkcmVzcyB3aGVyZSBub3RpZmljYXRpb24gYWJvdXQgbmV3IHN1Ym1pc3Npb25zIHdpbGwgYmUgc2VuZCB0by4gRGVmYXVsdCBlbWFpbCBhZGRyZXNzIHdpbGwgYmUgdXNlZCBpZiB0aGlzIGZpZWxkIGlzIGxlZnQgYmxhbmsu" Column="U3VibWlzc2lvbiBOb3RpZmljYXRpb24gRW1haWw=">U3VibWlzc2lvbiBOb3RpZmljYXRpb24gRW1haWw=</PHRASE> <PHRASE Label="la_fld_SubscribedOn" Module="Core" Type="1" Column="U3Vic2NyaWJlZCBPbg==">U3Vic2NyaWJlZCBPbg==</PHRASE> <PHRASE Label="la_fld_SuggestedCorrection" Module="Core" Type="1" Column="U3VnZ2VzdGVkIENvcnJlY3Rpb24=">U3VnZ2VzdGVkIENvcnJlY3Rpb24=</PHRASE> <PHRASE Label="la_fld_SymLinkCategoryId" Module="Core" Type="1">UG9pbnRzIHRvIFNlY3Rpb24=</PHRASE> <PHRASE Label="la_fld_SynchronizationModes" Module="Core" Type="1" Hint="IlRvIG90aGVycyIgLSBhbGxvdyB0cmFuc2xhdGVkIHBocmFzZXMgZnJvbSB0aGlzIGxhbmd1YWdlIHRvIGJlIHVzZWQgaW5zdGVhZCBvZiBtaXNzaW5nIHBocmFzZXMgb24gb3RoZXIgbGFuZ3VhZ2VzLiAiRnJvbSBvdGhlcnMiIC0gdXNlIHRyYW5zbGF0ZWQgcGhyYXNlcyBmcm9tIG90aGVyIGxhbmd1YWdlcyBpbnN0ZWFkIG9mIG1pc3NpbmcgcGhyYXNlcyBvbiB0aGlzIGxhbmd1YWdlLg==" Column="U3luY2hyb25pemUgTGFuZ3VhZ2U=">U3luY2hyb25pemUgTGFuZ3VhZ2U=</PHRASE> <PHRASE Label="la_fld_SystemEvent" Module="Core" Type="1" Column="U3lzdGVtIEV2ZW50">U3lzdGVtIEV2ZW50</PHRASE> <PHRASE Label="la_fld_SystemSettingHint" Module="Core" Type="1" Hint="UGxhY2luZyAiaGludDpsYV9jb25maWdfVmFyaWFibGVOYW1lIiB2YWx1ZSB3b3VsZCB1c3VhbGx5IGRvIHRoZSB0cmljay4=">U2V0dGluZyBIaW50IExhYmVs</PHRASE> <PHRASE Label="la_fld_SystemSettingName" Module="Core" Type="1">U2V0dGluZyBOYW1l</PHRASE> <PHRASE Label="la_fld_SystemSettingSection" Module="Core" Type="1" Hint="U2VjdGlvbiwgd2hlcmUgdGhpcyBzeXN0ZW0gc2V0dGluZyB3aWxsIGJlIGRpc3BsYXllZC4=">U2VjdGlvbg==</PHRASE> <PHRASE Label="la_fld_SystemSettingValidation" Module="Core" Type="1" Hint="U2VyaWFsaXplZCBhcnJheSBvZiB2YWxpZGF0aW9uIG9wdGlvbnMgc3VjaCBhcyAicmVxdWlyZWQiLCAiZm9ybWF0dGVyIiwgZXRjLg==">VmFsaWRhdGlvbiBMb2dpYw==</PHRASE> <PHRASE Label="la_fld_SystemSettingValue" Module="Core" Type="1">U2V0dGluZyBWYWx1ZQ==</PHRASE> <PHRASE Label="la_fld_TableName" Module="Core" Type="1">VGFibGUgTmFtZSBpbiBEYXRhYmFzZSA=</PHRASE> <PHRASE Label="la_fld_Tag" Module="Core" Type="1">VGFn</PHRASE> <PHRASE Label="la_fld_TargetId" Module="Core" Type="1" Column="SXRlbQ==">SXRlbQ==</PHRASE> <PHRASE Label="la_fld_TemplateFile" Module="Core" Type="1">VGVtcGxhdGUgRmlsZQ==</PHRASE> <PHRASE Label="la_fld_TemplateName" Module="Core" Type="1" Column="VGVtcGxhdGUgTmFtZQ==">VGVtcGxhdGUgTmFtZQ==</PHRASE> <PHRASE Label="la_fld_TemplateType" Module="Core" Type="1" Column="VGVtcGxhdGU=">VGVtcGxhdGU=</PHRASE> <PHRASE Label="la_fld_Text" Module="Core" Type="1">VGV4dA==</PHRASE> <PHRASE Label="la_fld_TextAlign" Module="Core" Type="1">VGV4dCBBbGlnbg==</PHRASE> <PHRASE Label="la_fld_TextDecoration" Module="Core" Type="1">VGV4dCBEZWNvcmF0aW9u</PHRASE> <PHRASE Label="la_fld_TextEmailTemplate" Module="Core" Type="1" Hint="VGhpcyBmaWVsZCBoYXMgYSBQbGFpbiBUZXh0IHZlcnNpb24gb2YgRW1haWwgRGVzaWduIFRlbXBsYXRlIGFuZCBtdXN0IGNvbnRhaW5zIGF0IGxlYXN0IGEgIiRib2R5IiB0YWcgdW5sZXNzIEhUTUwgVmVyc2lvbiBmaWVsZCBpcyBwb3B1bGF0ZWQgYWxyZWFkeS4gSXQgd2lsbCBiZSB1c2VkIGFzIGEgZGVzaWduIGZvciBhbGwgZW1haWxzIHNlbmQgb3V0IGJ5IHRoZSB3ZWJzaXRlLg==">VGV4dCBWZXJzaW9u</PHRASE> <PHRASE Label="LA_FLD_TEXTVERSION" Module="Core" Type="1">VGV4dCBWZXJzaW9u</PHRASE> <PHRASE Label="la_fld_Theme" Module="Core" Type="1" Column="VGhlbWU=">VGhlbWU=</PHRASE> <PHRASE Label="la_fld_Themes" Module="Core" Type="1">VGhlbWVz</PHRASE> <PHRASE Label="la_fld_ThesaurusTerm" Module="Core" Type="1" Column="VGhlc2F1cnVzIFRlcm0=">VGhlc2F1cnVzIFRlcm0=</PHRASE> <PHRASE Label="la_fld_ThesaurusType" Module="Core" Type="1" Column="VGhlc2F1cnVzIFR5cGU=">VGhlc2F1cnVzIFR5cGU=</PHRASE> <PHRASE Label="la_fld_ThousandSep" Module="Core" Type="1">VGhvdXNhbmRzIFNlcGFyYXRvcg==</PHRASE> <PHRASE Label="la_fld_TimeFormat" Module="Core" Type="1">VGltZSBGb3JtYXQ=</PHRASE> <PHRASE Label="la_fld_Timeout" Module="Core" Type="1" Hint="VGltZW91dCBvZiBldmVudCBzY2hlZHVsZSAoaW4gc2Vjb25kcykuIFNwZWNpZmllZCB2YWx1ZSBtdXN0IGJlIGdyZWF0ZXIgdGhhbiAwIGluIG9yZGVyIGZvciBzeXN0ZW0gdG8gdGltZW91dCBhbHJlYWR5IHJ1bm5pbmcgZXZlbnQgaWYgaXQncyBydW5uaW5nIHRpbWUgZXhjZWVkcyB0aGUgc3BlY2lmaWVkIGhlcmUgdGltZW91dCB2YWx1ZS4=" Column="VGltZW91dA==">VGltZW91dA==</PHRASE> <PHRASE Label="la_fld_TimeZone" Module="Core" Type="1" Hint="TmV3IFRpbWUgWm9uZSB3aWxsIGJlIGluIGVmZmVjdCBuZXh0IHRpbWUgdXNlciBsb2dpbnM=">VGltZSBab25l</PHRASE> <PHRASE Label="la_fld_Title" Module="Core" Type="1" Column="VGl0bGU=">VGl0bGU=</PHRASE> <PHRASE Label="la_fld_To" Module="Core" Type="1">VG8=</PHRASE> <PHRASE Label="la_fld_ToEmail" Module="Core" Type="1" Column="VG8gRS1tYWls">VG8gRS1tYWls</PHRASE> <PHRASE Label="la_fld_Top" Module="Core" Type="1">VG9w</PHRASE> <PHRASE Label="la_fld_TrackingCode" Module="Core" Type="1">QW5hbHl0aWNzIFRyYWNraW5nIENvZGU=</PHRASE> <PHRASE Label="la_fld_TransitionControls" Module="Core" Type="1" Column="UHJvbW8gVHJhbnNpdGlvbiBDb250cm9scw==">UHJvbW8gVHJhbnNpdGlvbiBDb250cm9scw==</PHRASE> <PHRASE Label="la_fld_TransitionEffect" Module="Core" Type="1" Column="UHJvbW8gVHJhbnNpdGlvbiBFZmZlY3Q=">UHJvbW8gVHJhbnNpdGlvbiBFZmZlY3Q=</PHRASE> <PHRASE Label="la_fld_TransitionEffectCustom" Module="Core" Type="1" Column="UHJvbW8gVHJhbnNpdGlvbiBFZmZlY3QgKGN1c3RvbSk=">UHJvbW8gVHJhbnNpdGlvbiBFZmZlY3QgKGN1c3RvbSk=</PHRASE> <PHRASE Label="la_fld_TransitionTime" Module="Core" Type="1" Column="VHJhbnNpdGlvbiBEZWxheSAoc2Vjb25kcyk=">VHJhbnNpdGlvbiBEZWxheSAoc2Vjb25kcyk=</PHRASE> <PHRASE Label="la_fld_TranslateFromLanguage" Module="Core" Type="1" Column="VHJhbnNsYXRlIEZyb20gTGFuZ2F1Z2U=">VHJhbnNsYXRlIEZyb20gTGFuZ2F1Z2U=</PHRASE> <PHRASE Label="la_fld_Translation" Module="Core" Type="1" Column="VmFsdWU=">UGhyYXNl</PHRASE> <PHRASE Label="la_fld_TranslationInSync" Module="Core" Type="1" Column="VHJhbnNsYXRpb24gaW4gU3luYw==">VHJhbnNsYXRpb24gaW4gU3luYw==</PHRASE> <PHRASE Label="la_fld_Type" Module="Core" Type="1" Column="VHlwZQ==">VHlwZQ==</PHRASE> <PHRASE Label="la_fld_UnitSystem" Module="Core" Type="1">TWVhc3VyZXMgU3lzdGVt</PHRASE> <PHRASE Label="la_fld_Upload" Module="Core" Type="1">VXBsb2FkIEZpbGUgRnJvbSBMb2NhbCBQQw==</PHRASE> <PHRASE Label="la_fld_UploadExtensions" Module="Core" Type="1" Hint="TGlzdCBvZiBGaWxlIEV4dGVuc2lvbnMgYWxsb3dlZCBmb3IgdXBsb2FkIHNlcGFyYXRlZCBieSBjb21tYS4gRXhhbXBsZToganBnLHBuZyxnaWcscGRm">QWxsb3dlZCBGaWxlIEV4dGVuc2lvbnM=</PHRASE> <PHRASE Label="la_fld_UploadMaxSize" Module="Core" Type="1" Hint="TWF4aW11bSBmaWxlIHNpemUgKGluIEtieXRlcykgYWxsb3dlZCBmb3IgdGhlIHVwbG9hZC4gRXhhbXBsZTogMSBNQnl0ZSA9IDEwMjQgS0J5dGVzLCAxMCBNQnl0ZXMgPSAxMDI0MCBLQnl0ZXMu">TWF4aW11bSBGaWxlIFNpemU=</PHRASE> <PHRASE Label="la_fld_URL" Module="Core" Type="1" Column="VVJM">VVJM</PHRASE> <PHRASE Label="la_fld_UseExternalUrl" Module="Core" Type="1">TGluayB0byBFeHRlcm5hbCBVUkw=</PHRASE> <PHRASE Label="la_fld_UseMenuIcon" Module="Core" Type="1">VXNlIEN1c3RvbSBNZW51IEljb24=</PHRASE> <PHRASE Label="la_fld_UserDocsUrl" Module="Core" Type="1">VXNlciBEb2N1bWVudGF0aW9uIFVSTA==</PHRASE> <PHRASE Label="la_fld_UserGroups" Module="Core" Type="1">VXNlciBHcm91cHM=</PHRASE> <PHRASE Label="la_fld_Username" Module="Core" Type="1" Column="VXNlcm5hbWU=">VXNlcm5hbWU=</PHRASE> <PHRASE Label="la_fld_UseSecurityImage" Module="Core" Type="1" Column="VXNlIFNlY3VyaXR5IEltYWdl">VXNlIFNlY3VyaXR5IEltYWdl</PHRASE> <PHRASE Label="la_fld_VerifyPassword" Module="Core" Type="1">UmUtZW50ZXIgUGFzc3dvcmQ=</PHRASE> <PHRASE Label="la_fld_Version" Module="Core" Type="1" Column="VmVyc2lvbg==">VmVyc2lvbg==</PHRASE> <PHRASE Label="la_fld_Visibility" Module="Core" Type="1" Column="VmlzaWJpbGl0eQ==">VmlzaWJpbGl0eQ==</PHRASE> <PHRASE Label="la_fld_Votes" Module="Core" Type="1">Vm90ZXM=</PHRASE> <PHRASE Label="la_fld_Width" Module="Core" Type="1">V2lkdGg=</PHRASE> <PHRASE Label="la_fld_Z-Index" Module="Core" Type="1">Wi1JbmRleA==</PHRASE> <PHRASE Label="la_fld_ZIP" Module="Core" Type="1" Column="WklQ">WklQ</PHRASE> <PHRASE Label="la_Font" Module="Core" Type="1">Rm9udCBQcm9wZXJ0aWVz</PHRASE> <PHRASE Label="la_FormCancelConfirmation" Module="Core" Type="1">RG8geW91IHdhbnQgdG8gc2F2ZSB0aGUgY2hhbmdlcz8=</PHRASE> <PHRASE Label="la_FormResetConfirmation" Module="Core" Type="1">QXJlIHlvdSBzdXJlIHlvdSB3b3VsZCBsaWtlIHRvIGRpc2NhcmQgdGhlIGNoYW5nZXM/</PHRASE> <PHRASE Label="la_From" Module="Core" Type="1">RnJvbQ==</PHRASE> <PHRASE Label="la_from_date" Module="Core" Type="1">RnJvbSBEYXRl</PHRASE> <PHRASE Label="la_GeneralSections" Module="Core" Type="1">R2VuZXJhbCBTZWN0aW9ucw==</PHRASE> <PHRASE Label="la_HeadFrame" Module="Core" Type="1">SGVhZCBGcmFtZQ==</PHRASE> <PHRASE Label="la_Hide" Module="Core" Type="1">SGlkZQ==</PHRASE> <PHRASE Label="la_hint_AllFiles" Module="Core" Type="1">QWxsIEZpbGVz</PHRASE> <PHRASE Label="la_hint_ClickToEdit" Module="Core" Type="1">Q2xpY2sgdG8gZWRpdA==</PHRASE> <PHRASE Label="la_hint_CSVFiles" Module="Core" Type="1">Q1NWIEZpbGVz</PHRASE> <PHRASE Label="la_hint_ImageFiles" Module="Core" Type="1">SW1hZ2UgRmlsZXM=</PHRASE> <PHRASE Label="la_hint_PopPort" Module="Core" Type="1">UE9QMyBTZXJ2ZXIgUG9ydC4gRm9yIGV4LiAiMTEwIiBmb3IgcmVndWxhciBjb25uZWN0aW9uLCAiOTk1IiBmb3Igc2VjdXJlIGNvbm5lY3Rpb24u</PHRASE> <PHRASE Label="la_hint_PopServer" Module="Core" Type="1">UE9QMyBTZXJ2ZXIgQWRkcmVzcy4gRm9yIGV4LiB1c2UgInNzbDovL3BvcC5nbWFpbC5jb20iIGZvciBHbWFpbCwgInBvcC5tYWlsLnlhaG9vLmNvbSIgZm9yIFlhaG9vLg==</PHRASE> <PHRASE Label="la_hint_SystemToolsCacheKeys" Module="Core" Type="1">Q2FjaGUgS2V5KHMp</PHRASE> <PHRASE Label="la_hint_SystemToolsDatabaseCache" Module="Core" Type="1">ZGF0YWJhc2UgY2FjaGU=</PHRASE> <PHRASE Label="la_hint_SystemToolsMemoryCache" Module="Core" Type="1">bWVtb3J5IGNhY2hl</PHRASE> <PHRASE Label="la_hint_UsingRegularExpression" Module="Core" Type="1">VXNpbmcgUmVndWxhciBFeHByZXNzaW9u</PHRASE> <PHRASE Label="la_Hot" Module="Core" Type="1">SG90</PHRASE> <PHRASE Label="la_Html" Module="Core" Type="1">SFRNTA==</PHRASE> <PHRASE Label="la_IDField" Module="Core" Type="1">SUQgRmllbGQ=</PHRASE> <PHRASE Label="la_invalid_email" Module="Core" Type="1">SW52YWxpZCBFLU1haWw=</PHRASE> <PHRASE Label="la_invalid_integer" Module="Core" Type="1">SW5jb3JyZWN0IGRhdGEgZm9ybWF0LCBwbGVhc2UgdXNlIGludGVnZXI=</PHRASE> <PHRASE Label="la_invalid_license" Module="Core" Type="1">TWlzc2luZyBvciBpbnZhbGlkIEluLVBvcnRhbCBMaWNlbnNl</PHRASE> <PHRASE Label="la_Invalid_Password" Module="Core" Type="1">SW5jb3JyZWN0IFVzZXJuYW1lIG9yIFBhc3N3b3Jk</PHRASE> <PHRASE Label="la_invalid_state" Module="Core" Type="1">SW52YWxpZCBzdGF0ZQ==</PHRASE> <PHRASE Label="la_ItemTab_Categories" Module="Core" Type="1">U2VjdGlvbnM=</PHRASE> <PHRASE Label="la_LessThen1Second" Module="Core" Type="1">PCAxIHNlYy4=</PHRASE> <PHRASE Label="la_link_editorspick_prompt" Module="Core" Type="1">RGlzcGxheSBlZGl0b3IgUElDS3MgYWJvdmUgcmVndWxhciBsaW5rcw==</PHRASE> <PHRASE Label="la_link_newdays_prompt" Module="Core" Type="1">TnVtYmVyIG9mIGRheXMgZm9yIGEgbGluayB0byBiZSBORVc=</PHRASE> <PHRASE Label="la_link_perpage_prompt" Module="Core" Type="1">TnVtYmVyIG9mIGxpbmtzIHBlciBwYWdl</PHRASE> <PHRASE Label="la_link_perpage_short_prompt" Module="Core" Type="1">TnVtYmVyIG9mIGxpbmtzIHBlciBwYWdlIG9uIGEgc2hvcnQgbGlzdGluZw==</PHRASE> <PHRASE Label="la_link_sortfield2_prompt" Module="Core" Type="1">QW5kIHRoZW4gYnk=</PHRASE> <PHRASE Label="la_link_sortfield_prompt" Module="Core" Type="1">T3JkZXIgbGlua3MgYnk=</PHRASE> <PHRASE Label="la_Linux" Module="Core" Type="1">TGludXg=</PHRASE> <PHRASE Label="la_Local" Module="Core" Type="1">TG9jYWw=</PHRASE> <PHRASE Label="la_LocalImage" Module="Core" Type="1">TG9jYWwgSW1hZ2U=</PHRASE> <PHRASE Label="la_LogBacktraceFunction" Module="Core" Type="1" Hint="U2hvdy9IaWRlIEZ1bmN0aW9uIEFyZ3VtZW50cw==">RnVuY3Rpb24=</PHRASE> <PHRASE Label="la_Logged_in_as" Module="Core" Type="1">TG9nZ2VkIGluIGFz</PHRASE> <PHRASE Label="la_login" Module="Core" Type="1">TG9naW4=</PHRASE> <PHRASE Label="la_Logout" Module="Core" Type="1">TG9nb3V0</PHRASE> <PHRASE Label="la_m0" Module="Core" Type="1">KEdNVCk=</PHRASE> <PHRASE Label="la_m1" Module="Core" Type="1">KEdNVCAtMDE6MDAp</PHRASE> <PHRASE Label="la_m10" Module="Core" Type="1">KEdNVCAtMTA6MDAp</PHRASE> <PHRASE Label="la_m11" Module="Core" Type="1">KEdNVCAtMTE6MDAp</PHRASE> <PHRASE Label="la_m12" Module="Core" Type="1">KEdNVCAtMTI6MDAp</PHRASE> <PHRASE Label="la_m2" Module="Core" Type="1">KEdNVCAtMDI6MDAp</PHRASE> <PHRASE Label="la_m3" Module="Core" Type="1">KEdNVCAtMDM6MDAp</PHRASE> <PHRASE Label="la_m4" Module="Core" Type="1">KEdNVCAtMDQ6MDAp</PHRASE> <PHRASE Label="la_m5" Module="Core" Type="1">KEdNVCAtMDU6MDAp</PHRASE> <PHRASE Label="la_m6" Module="Core" Type="1">KEdNVCAtMDY6MDAp</PHRASE> <PHRASE Label="la_m7" Module="Core" Type="1">KEdNVCAtMDc6MDAp</PHRASE> <PHRASE Label="la_m8" Module="Core" Type="1">KEdNVCAtMDg6MDAp</PHRASE> <PHRASE Label="la_m9" Module="Core" Type="1">KEdNVCAtMDk6MDAp</PHRASE> <PHRASE Label="la_Margins" Module="Core" Type="1">TWFyZ2lucw==</PHRASE> <PHRASE Label="la_MembershipExpirationReminder" Module="Core" Type="1">R3JvdXAgTWVtYmVyc2hpcCBFeHBpcmF0aW9uIFJlbWluZGVyIChkYXlzKQ==</PHRASE> <PHRASE Label="la_Metric" Module="Core" Type="1">TWV0cmlj</PHRASE> <PHRASE Label="la_MixedCategoryPath" Module="Core" Type="1">U2VjdGlvbiBwYXRoIGluIG9uZSBmaWVsZA==</PHRASE> <PHRASE Label="la_module_not_licensed" Module="Core" Type="1">TW9kdWxlIG5vdCBsaWNlbnNlZA==</PHRASE> <PHRASE Label="la_monday" Module="Core" Type="1">TW9uZGF5</PHRASE> <PHRASE Label="la_msg_ChangeLogIsCurrentlyDisabledTurnOnSettingToEnableIt" Module="Core" Type="1">Q2hhbmdlIGxvZyBpcyBjdXJyZW50bHkgZGlzYWJsZWQuIFR1cm4gb24gIiVzIiBzZXR0aW5nIHRvIGVuYWJsZSBpdC4=</PHRASE> <PHRASE Label="la_msg_EnableTrackingDatabaseChangesToChangeLog" Module="Core" Type="1">RW5hYmxlIHRyYWNraW5nIGRhdGFiYXNlIGNoYW5nZXMgdG8gY2hhbmdlIGxvZz8=</PHRASE> <PHRASE Label="la_msg_LastOperationHasBeenSuccessfullyCompleted" Module="Core" Type="1">TGFzdCBvcGVyYXRpb24gaGFzIGJlZW4gc3VjY2Vzc2Z1bGx5IGNvbXBsZXRlZCE=</PHRASE> <PHRASE Label="la_Msg_PropagateCategoryStatus" Module="Core" Type="1">QXBwbHkgdG8gYWxsIFN1Yi1zZWN0aW9ucz8=</PHRASE> <PHRASE Label="la_msg_RootPasswordWasReset" Module="Core" Type="1">WW91ciAicm9vdCIgcGFzc3dvcmQgaGFzIGJlZW4gcmVzZXQuIFBsZWFzZSByZW1vdmUgREJHX1JFU0VUX1JPT1QgY29uc3RhbnQgYW5kIGNoZWNrIHlvdXIgZS1tYWlsIGFkZHJlc3Mu</PHRASE> <PHRASE Label="la_msg_YourChangesWereSuccessfullySaved" Module="Core" Type="1">WW91ciBjaGFuZ2VzIHdlcmUgc3VjY2Vzc2Z1bGx5IHNhdmVkIQ==</PHRASE> <PHRASE Label="la_Never" Module="Core" Type="1">TmV2ZXI=</PHRASE> <PHRASE Label="la_NeverExpires" Module="Core" Type="1">TmV2ZXIgRXhwaXJlcw==</PHRASE> <PHRASE Label="la_New" Module="Core" Type="1">TmV3</PHRASE> <PHRASE Label="la_nextcategory" Module="Core" Type="1">TmV4dCBzZWN0aW9u</PHRASE> <PHRASE Label="la_No" Module="Core" Type="1">Tm8=</PHRASE> <PHRASE Label="la_none" Module="Core" Type="1">Tm9uZQ==</PHRASE> <PHRASE Label="la_no_permissions" Module="Core" Type="1">Tm8gUGVybWlzc2lvbnM=</PHRASE> <PHRASE Label="la_NumberSuffixNd" Module="Core" Type="1">bmQ=</PHRASE> <PHRASE Label="la_NumberSuffixRd" Module="Core" Type="1">cmQ=</PHRASE> <PHRASE Label="la_NumberSuffixSt" Module="Core" Type="1">c3Q=</PHRASE> <PHRASE Label="la_NumberSuffixTh" Module="Core" Type="1">dGg=</PHRASE> <PHRASE Label="la_Off" Module="Core" Type="1">T2Zm</PHRASE> <PHRASE Label="la_On" Module="Core" Type="1">T24=</PHRASE> <PHRASE Label="la_OneWay" Module="Core" Type="1">T25lIFdheQ==</PHRASE> <PHRASE Label="la_OnLine" Module="Core" Type="1">b24gbGluZQ==</PHRASE> <PHRASE Label="la_opt_ActionCreate" Module="Core" Type="1">Y3JlYXRlZA==</PHRASE> <PHRASE Label="la_opt_ActionDelete" Module="Core" Type="1">ZGVsZXRlZA==</PHRASE> <PHRASE Label="la_opt_ActionUpdate" Module="Core" Type="1">dXBkYXRlZA==</PHRASE> <PHRASE Label="la_opt_Active" Module="Core" Type="1">QWN0aXZl</PHRASE> <PHRASE Label="la_opt_Address" Module="Core" Type="1">QWRkcmVzcw==</PHRASE> <PHRASE Label="la_opt_After" Module="Core" Type="1">QWZ0ZXI=</PHRASE> <PHRASE Label="la_opt_Allow" Module="Core" Type="1">QWxsb3c=</PHRASE> <PHRASE Label="la_opt_AnimationCustom" Module="Core" Type="1">Q3VzdG9t</PHRASE> <PHRASE Label="la_opt_AnimationFade" Module="Core" Type="1">RmFkZQ==</PHRASE> <PHRASE Label="la_opt_AnimationSlide" Module="Core" Type="1">U2xpZGU=</PHRASE> <PHRASE Label="la_opt_April" Module="Core" Type="1">QXByaWw=</PHRASE> <PHRASE Label="la_opt_August" Module="Core" Type="1">QXVndXN0</PHRASE> <PHRASE Label="la_opt_AutoDetect" Module="Core" Type="1">QXV0by1EZXRlY3Q=</PHRASE> <PHRASE Label="la_opt_Automatic" Module="Core" Type="1">QXV0b21hdGlj</PHRASE> <PHRASE Label="la_opt_Before" Module="Core" Type="1">QmVmb3Jl</PHRASE> <PHRASE Label="la_opt_Bounce" Module="Core" Type="1">Qm91bmNlZA==</PHRASE> <PHRASE Label="la_opt_Cancelled" Module="Core" Type="1">Q2FuY2VsZWQ=</PHRASE> <PHRASE Label="la_opt_City" Module="Core" Type="1">Q2l0eQ==</PHRASE> <PHRASE Label="la_opt_Colon" Module="Core" Type="1">Q29sb24=</PHRASE> <PHRASE Label="la_opt_Comma" Module="Core" Type="1">Q29tbWE=</PHRASE> <PHRASE Label="la_opt_CommentText" Module="Core" Type="1">Q29tbWVudCBUZXh0</PHRASE> <PHRASE Label="la_opt_Cookies" Module="Core" Type="1">Q29va2llcw==</PHRASE> <PHRASE Label="la_opt_Countries" Module="Core" Type="1">Q291bnRyaWVz</PHRASE> <PHRASE Label="la_opt_Country" Module="Core" Type="1">Q291bnRyeQ==</PHRASE> <PHRASE Label="la_opt_CreatedOn" Module="Core" Type="1">Q3JlYXRlZCBPbg==</PHRASE> <PHRASE Label="la_opt_CronCommonSettings" Module="Core" Type="1">LS0gQ29tbW9uIFNldHRpbmdzIC0t</PHRASE> <PHRASE Label="la_opt_CronDays" Module="Core" Type="1">LS0gRGF5cyAtLQ==</PHRASE> <PHRASE Label="la_opt_CronEveryDay" Module="Core" Type="1">RXZlcnkgZGF5</PHRASE> <PHRASE Label="la_opt_CronEveryFifteenMinutes" Module="Core" Type="1">RXZlcnkgMTUgbWludXRlcw==</PHRASE> <PHRASE Label="la_opt_CronEveryFiveMinutes" Module="Core" Type="1">RXZlcnkgNSBtaW51dGVz</PHRASE> <PHRASE Label="la_opt_CronEveryFourHours" Module="Core" Type="1">RXZlcnkgNCBob3Vycw==</PHRASE> <PHRASE Label="la_opt_CronEveryHour" Module="Core" Type="1">RXZlcnkgaG91cg==</PHRASE> <PHRASE Label="la_opt_CronEveryMinute" Module="Core" Type="1">RXZlcnkgbWludXRl</PHRASE> <PHRASE Label="la_opt_CronEveryMonth" Module="Core" Type="1">RXZlcnkgbW9udGg=</PHRASE> <PHRASE Label="la_opt_CronEveryOtherDay" Module="Core" Type="1">RXZlcnkgb3RoZXIgZGF5</PHRASE> <PHRASE Label="la_opt_CronEveryOtherHour" Module="Core" Type="1">RXZlcnkgb3RoZXIgaG91cg==</PHRASE> <PHRASE Label="la_opt_CronEveryOtherMinute" Module="Core" Type="1">RXZlcnkgb3RoZXIgbWludXRl</PHRASE> <PHRASE Label="la_opt_CronEveryOtherMonth" Module="Core" Type="1">RXZlcnkgb3RoZXIgbW9udGg=</PHRASE> <PHRASE Label="la_opt_CronEverySixHours" Module="Core" Type="1">RXZlcnkgNiBob3Vycw==</PHRASE> <PHRASE Label="la_opt_CronEverySixMonths" Module="Core" Type="1">RXZlcnkgNiBtb250aHM=</PHRASE> <PHRASE Label="la_opt_CronEveryTenMinutes" Module="Core" Type="1">RXZlcnkgMTAgbWludXRlcw==</PHRASE> <PHRASE Label="la_opt_CronEveryThirtyMinutes" Module="Core" Type="1">RXZlcnkgMzAgbWludXRlcw==</PHRASE> <PHRASE Label="la_opt_CronEveryThreeHours" Module="Core" Type="1">RXZlcnkgMyBob3Vycw==</PHRASE> <PHRASE Label="la_opt_CronEveryThreeMonths" Module="Core" Type="1">RXZlcnkgMyBtb250aHM=</PHRASE> <PHRASE Label="la_opt_CronEveryTwelveHours" Module="Core" Type="1">RXZlcnkgMTIgaG91cnM=</PHRASE> <PHRASE Label="la_opt_CronEveryWeekday" Module="Core" Type="1">RXZlcnkgd2Vla2RheQ==</PHRASE> <PHRASE Label="la_opt_CronHours" Module="Core" Type="1">LS0gSG91cnMgLS0=</PHRASE> <PHRASE Label="la_opt_CronMinutes" Module="Core" Type="1">LS0gTWludXRlcyAtLQ==</PHRASE> <PHRASE Label="la_opt_CronMondayThroughFriday" Module="Core" Type="1">TW9uIHRocnUgRnJp</PHRASE> <PHRASE Label="la_opt_CronMondayWednesdayAndFriday" Module="Core" Type="1">TW9uLCBXZWQsIEZyaQ==</PHRASE> <PHRASE Label="la_opt_CronMonths" Module="Core" Type="1">LS0gTW9udGhzIC0t</PHRASE> <PHRASE Label="la_opt_CronOnceADay" Module="Core" Type="1">T25jZSBhIGRheQ==</PHRASE> <PHRASE Label="la_opt_CronOnceAMonth" Module="Core" Type="1">T25jZSBhIG1vbnRo</PHRASE> <PHRASE Label="la_opt_CronOnceAnHour" Module="Core" Type="1">T25jZSBhbiBob3Vy</PHRASE> <PHRASE Label="la_opt_CronOnceAWeek" Module="Core" Type="1">T25jZSBhIHdlZWs=</PHRASE> <PHRASE Label="la_opt_CronOnceAYear" Module="Core" Type="1">T25jZSBhIHllYXI=</PHRASE> <PHRASE Label="la_opt_CronSaturdayAndSunday" Module="Core" Type="1">U2F0IGFuZCBTdW4=</PHRASE> <PHRASE Label="la_opt_CronTuesdayAndThursday" Module="Core" Type="1">VHVlcywgVGh1cnM=</PHRASE> <PHRASE Label="la_opt_CronTwiceADay" Module="Core" Type="1">VHdpY2UgYSBkYXk=</PHRASE> <PHRASE Label="la_opt_CronTwiceAMonth" Module="Core" Type="1">MXN0IGFuZCAxNXRo</PHRASE> <PHRASE Label="la_opt_CronTwiceAnHour" Module="Core" Type="1">VHdpY2UgYW4gaG91cg==</PHRASE> <PHRASE Label="la_opt_CronWeekdays" Module="Core" Type="1">LS0gV2Vla2RheXMgLS0=</PHRASE> <PHRASE Label="la_opt_CurrentDomain" Module="Core" Type="1">Q3VycmVudCBEb21haW4=</PHRASE> <PHRASE Label="la_opt_CustomRecipients" Module="Core" Type="1">Q3VzdG9tICJUbyIgUmVjaXBpZW50KC1zKQ==</PHRASE> <PHRASE Label="la_opt_CustomSender" Module="Core" Type="1">Q3VzdG9tIFNlbmRlcg==</PHRASE> <PHRASE Label="la_opt_day" Module="Core" Type="1">ZGF5KHMp</PHRASE> <PHRASE Label="la_opt_December" Module="Core" Type="1">RGVjZW1iZXI=</PHRASE> <PHRASE Label="la_opt_Declined" Module="Core" Type="1">RGVjbGluZWQ=</PHRASE> <PHRASE Label="la_opt_DefaultAddress" Module="Core" Type="1">RGVmYXVsdCBXZWJzaXRlIGFkZHJlc3M=</PHRASE> <PHRASE Label="la_opt_Deny" Module="Core" Type="1">RGVueQ==</PHRASE> <PHRASE Label="la_opt_Description" Module="Core" Type="1">RGVzY3JpcHRpb24=</PHRASE> <PHRASE Label="la_opt_Disabled" Module="Core" Type="1">RGlzYWJsZWQ=</PHRASE> <PHRASE Label="la_opt_DoesntMatch" Module="Core" Type="1">RG9lc24ndCBtYXRjaA==</PHRASE> <PHRASE Label="la_opt_EditorsPick" Module="Core" Type="1">RWRpdG9yJ3MgUGljaw==</PHRASE> <PHRASE Label="la_opt_Email" Module="Core" Type="1">RS1tYWls</PHRASE> <PHRASE Label="la_opt_EmailBody" Module="Core" Type="1">RS1tYWlsIEJvZHk=</PHRASE> <PHRASE Label="la_opt_EmailDeliveryImmediate" Module="Core" Type="1">SW1tZWRpYXRl</PHRASE> <PHRASE Label="la_opt_EmailDeliveryQueue" Module="Core" Type="1">RW1haWwgUXVldWU=</PHRASE> <PHRASE Label="la_opt_EmailLogKeepForever" Module="Core" Type="1">Rm9yZXZlciAobmV2ZXIgZGVsZXRlZCBhdXRvbWF0aWNhbGx5KQ==</PHRASE> <PHRASE Label="la_opt_EmailSubject" Module="Core" Type="1">RS1tYWlsIFN1YmplY3Q=</PHRASE> <PHRASE Label="la_opt_EmailTemplates" Module="Core" Type="1">RS1tYWlsIFRlbXBsYXRlcw==</PHRASE> <PHRASE Label="la_opt_Error" Module="Core" Type="1">RXJyb3I=</PHRASE> <PHRASE Label="la_opt_Everyone" Module="Core" Type="1">RXZlcnlvbmU=</PHRASE> <PHRASE Label="la_opt_Exact" Module="Core" Type="1">RXhhY3Q=</PHRASE> <PHRASE Label="la_opt_Expired" Module="Core" Type="1">RXhwaXJlZA==</PHRASE> <PHRASE Label="la_opt_External" Module="Core" Type="1">RXh0ZXJuYWw=</PHRASE> <PHRASE Label="la_opt_ExternalUrl" Module="Core" Type="1">RXh0ZXJuYWwgVXJs</PHRASE> <PHRASE Label="la_opt_Failed" Module="Core" Type="1">RmFpbGVk</PHRASE> <PHRASE Label="la_opt_February" Module="Core" Type="1">RmVicnVhcnk=</PHRASE> <PHRASE Label="la_opt_FirstName" Module="Core" Type="1">Rmlyc3QgTmFtZQ==</PHRASE> <PHRASE Label="la_opt_FiveYears" Module="Core" Type="1">NSB5ZWFycw==</PHRASE> <PHRASE Label="la_opt_Friday" Module="Core" Type="1">RnJpZGF5</PHRASE> <PHRASE Label="la_opt_Group" Module="Core" Type="1">R3JvdXA=</PHRASE> <PHRASE Label="la_opt_GuestsOnly" Module="Core" Type="1">R3Vlc3RzIE9ubHk=</PHRASE> <PHRASE Label="la_opt_hour" Module="Core" Type="1">aG91cihzKQ==</PHRASE> <PHRASE Label="la_opt_InheritFromParent" Module="Core" Type="1">SW5oZXJpdCBmcm9tIFBhcmVudA==</PHRASE> <PHRASE Label="la_opt_Internal" Module="Core" Type="1">SW50ZXJuYWw=</PHRASE> <PHRASE Label="la_opt_Invalid" Module="Core" Type="1">SW52YWxpZA==</PHRASE> <PHRASE Label="la_opt_IP_Address" Module="Core" Type="1">SVAgQWRkcmVzcw==</PHRASE> <PHRASE Label="la_opt_IsUnique" Module="Core" Type="1">SXMgdW5pcXVl</PHRASE> <PHRASE Label="la_opt_January" Module="Core" Type="1">SmFudWFyeQ==</PHRASE> <PHRASE Label="la_opt_July" Module="Core" Type="1">SnVseQ==</PHRASE> <PHRASE Label="la_opt_June" Module="Core" Type="1">SnVuZQ==</PHRASE> <PHRASE Label="la_opt_LastName" Module="Core" Type="1">TGFzdCBOYW1l</PHRASE> <PHRASE Label="la_opt_LoggedOut" Module="Core" Type="1">TG9nZ2VkIE91dA==</PHRASE> <PHRASE Label="la_opt_LogNotificationDisabled" Module="Core" Type="1">RGlzYWJsZWQ=</PHRASE> <PHRASE Label="la_opt_LogNotificationPending" Module="Core" Type="1">UGVuZGluZw==</PHRASE> <PHRASE Label="la_opt_LogNotificationSent" Module="Core" Type="1">U2VudA==</PHRASE> <PHRASE Label="la_opt_LogTypeDatabase" Module="Core" Type="1">RGF0YWJhc2U=</PHRASE> <PHRASE Label="la_opt_LogTypeOther" Module="Core" Type="1">T3RoZXI=</PHRASE> <PHRASE Label="la_opt_LogTypePhp" Module="Core" Type="1">UEhQ</PHRASE> <PHRASE Label="la_opt_Manual" Module="Core" Type="1">TWFudWFs</PHRASE> <PHRASE Label="la_opt_March" Module="Core" Type="1">TWFyY2g=</PHRASE> <PHRASE Label="la_opt_May" Module="Core" Type="1">TWF5</PHRASE> <PHRASE Label="la_opt_min" Module="Core" Type="1">bWludXRlKHMp</PHRASE> <PHRASE Label="la_opt_ModalWindow" Module="Core" Type="1">TW9kYWwgV2luZG93</PHRASE> <PHRASE Label="la_opt_Monday" Module="Core" Type="1">TW9uZGF5</PHRASE> <PHRASE Label="la_opt_month" Module="Core" Type="1">bW9udGgocyk=</PHRASE> <PHRASE Label="la_opt_NewEmail" Module="Core" Type="1">TmV3IEUtbWFpbA==</PHRASE> <PHRASE Label="la_opt_NotEmpty" Module="Core" Type="1">Tm90IGVtcHR5</PHRASE> <PHRASE Label="la_opt_NotLike" Module="Core" Type="1">Tm90IGxpa2U=</PHRASE> <PHRASE Label="la_opt_NotProcessed" Module="Core" Type="1">Tm90IFByb2Nlc3NlZA==</PHRASE> <PHRASE Label="la_opt_NotReplied" Module="Core" Type="1">Tm90IFJlcGxpZWQ=</PHRASE> <PHRASE Label="la_opt_November" Module="Core" Type="1">Tm92ZW1iZXI=</PHRASE> <PHRASE Label="la_opt_October" Module="Core" Type="1">T2N0b2Jlcg==</PHRASE> <PHRASE Label="la_opt_OneDay" Module="Core" Type="1">MSBkYXk=</PHRASE> <PHRASE Label="la_opt_OneMonth" Module="Core" Type="1">MSBtb250aA==</PHRASE> <PHRASE Label="la_opt_OneWeek" Module="Core" Type="1">MSB3ZWVr</PHRASE> <PHRASE Label="la_opt_OneYear" Module="Core" Type="1">MSB5ZWFy</PHRASE> <PHRASE Label="la_opt_PartiallyProcessed" Module="Core" Type="1">UGFydGlhbGx5IFByb2Nlc3NlZA==</PHRASE> <PHRASE Label="la_opt_Pending" Module="Core" Type="1">UGVuZGluZw==</PHRASE> <PHRASE Label="la_opt_Phone" Module="Core" Type="1">UGhvbmU=</PHRASE> <PHRASE Label="la_opt_Phrases" Module="Core" Type="1">TGFiZWxz</PHRASE> <PHRASE Label="la_opt_PopupWindow" Module="Core" Type="1">UG9wdXAgV2luZG93</PHRASE> <PHRASE Label="la_opt_Processed" Module="Core" Type="1">UHJvY2Vzc2Vk</PHRASE> <PHRASE Label="la_opt_Published" Module="Core" Type="1">UHVibGlzaGVk</PHRASE> <PHRASE Label="la_opt_QueryString" Module="Core" Type="1">UXVlcnkgU3RyaW5nIChTSUQp</PHRASE> <PHRASE Label="la_opt_Rating" Module="Core" Type="1">UmF0aW5n</PHRASE> <PHRASE Label="la_opt_RecipientEmail" Module="Core" Type="1">UmVjaXBpZW50IEUtbWFpbA==</PHRASE> <PHRASE Label="la_opt_RecipientName" Module="Core" Type="1">UmVjaXBpZW50IE5hbWU=</PHRASE> <PHRASE Label="la_opt_Replied" Module="Core" Type="1">UmVwbGllZA==</PHRASE> <PHRASE Label="la_opt_Running" Module="Core" Type="1">UnVubmluZw==</PHRASE> <PHRASE Label="la_opt_SameWindow" Module="Core" Type="1">U2FtZSBXaW5kb3c=</PHRASE> <PHRASE Label="la_opt_Saturday" Module="Core" Type="1">U2F0dXJkYXk=</PHRASE> <PHRASE Label="la_opt_sec" Module="Core" Type="1">c2Vjb25kKHMp</PHRASE> <PHRASE Label="la_opt_Semicolon" Module="Core" Type="1">U2VtaS1jb2xvbg==</PHRASE> <PHRASE Label="la_opt_Sent" Module="Core" Type="1">U2VudA==</PHRASE> <PHRASE Label="la_opt_September" Module="Core" Type="1">U2VwdGVtYmVy</PHRASE> <PHRASE Label="la_opt_Silent" Module="Core" Type="1">U2lsZW50</PHRASE> <PHRASE Label="la_opt_Skipped" Module="Core" Type="1">U2tpcHBlZA==</PHRASE> <PHRASE Label="la_opt_Space" Module="Core" Type="1">U3BhY2U=</PHRASE> <PHRASE Label="la_opt_State" Module="Core" Type="1">U3RhdGU=</PHRASE> <PHRASE Label="la_opt_Sub-match" Module="Core" Type="1">U3ViLW1hdGNo</PHRASE> <PHRASE Label="la_opt_Success" Module="Core" Type="1">U3VjY2Vzcw==</PHRASE> <PHRASE Label="la_opt_Sunday" Module="Core" Type="1">U3VuZGF5</PHRASE> <PHRASE Label="la_opt_SynchronizeFromOthers" Module="Core" Type="1">RnJvbSBvdGhlcnM=</PHRASE> <PHRASE Label="la_opt_SynchronizeToOthers" Module="Core" Type="1">VG8gb3RoZXJz</PHRASE> <PHRASE Label="la_opt_System" Module="Core" Type="1">U3lzdGVt</PHRASE> <PHRASE Label="la_opt_SystemLogKeepForever" Module="Core" Type="1">Rm9yZXZlciAobmV2ZXIgZGVsZXRlZCBhdXRvbWF0aWNhbGx5KQ==</PHRASE> <PHRASE Label="la_opt_Tab" Module="Core" Type="1">VGFi</PHRASE> <PHRASE Label="la_opt_Template" Module="Core" Type="1">VGVtcGxhdGU=</PHRASE> <PHRASE Label="la_opt_ThreeMonths" Module="Core" Type="1">MyBtb250aHM=</PHRASE> <PHRASE Label="la_opt_ThreeYears" Module="Core" Type="1">MyB5ZWFycw==</PHRASE> <PHRASE Label="la_opt_Thursday" Module="Core" Type="1">VGh1cnNkYXk=</PHRASE> <PHRASE Label="la_opt_Title" Module="Core" Type="1">VGl0bGU=</PHRASE> <PHRASE Label="la_opt_Tuesday" Module="Core" Type="1">VHVlc2RheQ==</PHRASE> <PHRASE Label="la_opt_TwoWeeks" Module="Core" Type="1">MiB3ZWVrcw==</PHRASE> <PHRASE Label="la_opt_TwoYears" Module="Core" Type="1">MiB5ZWFycw==</PHRASE> <PHRASE Label="la_opt_User" Module="Core" Type="1">VXNlcg==</PHRASE> <PHRASE Label="la_opt_UserEmailActivation" Module="Core" Type="1">RW1haWwgQWN0aXZhdGlvbg==</PHRASE> <PHRASE Label="la_opt_UserInstantRegistration" Module="Core" Type="1">SW1tZWRpYXRlIA==</PHRASE> <PHRASE Label="la_opt_Username" Module="Core" Type="1">VXNlcm5hbWU=</PHRASE> <PHRASE Label="la_opt_UserNotAllowedRegistration" Module="Core" Type="1">Tm90IEFsbG93ZWQ=</PHRASE> <PHRASE Label="la_opt_UserUponApprovalRegistration" Module="Core" Type="1">VXBvbiBBcHByb3ZhbA==</PHRASE> <PHRASE Label="la_opt_Virtual" Module="Core" Type="1">VmlydHVhbA==</PHRASE> <PHRASE Label="la_opt_Wednesday" Module="Core" Type="1">V2VkbmVzZGF5</PHRASE> <PHRASE Label="la_opt_week" Module="Core" Type="1">d2VlayhzKQ==</PHRASE> <PHRASE Label="la_opt_year" Module="Core" Type="1">eWVhcihzKQ==</PHRASE> <PHRASE Label="la_opt_Zip" Module="Core" Type="1">Wmlw</PHRASE> <PHRASE Label="la_OtherFields" Module="Core" Type="1">T3RoZXIgRmllbGRz</PHRASE> <PHRASE Label="la_OutOf" Module="Core" Type="1">b3V0IG9m</PHRASE> <PHRASE Label="la_p1" Module="Core" Type="1">KEdNVCArMDE6MDAp</PHRASE> <PHRASE Label="la_p10" Module="Core" Type="1">KEdNVCArMTA6MDAp</PHRASE> <PHRASE Label="la_p11" Module="Core" Type="1">KEdNVCArMTE6MDAp</PHRASE> <PHRASE Label="la_p12" Module="Core" Type="1">KEdNVCArMTI6MDAp</PHRASE> <PHRASE Label="la_p13" Module="Core" Type="1">KEdNVCArMTM6MDAp</PHRASE> <PHRASE Label="la_p2" Module="Core" Type="1">KEdNVCArMDI6MDAp</PHRASE> <PHRASE Label="la_p3" Module="Core" Type="1">KEdNVCArMDM6MDAp</PHRASE> <PHRASE Label="la_p4" Module="Core" Type="1">KEdNVCArMDQ6MDAp</PHRASE> <PHRASE Label="la_p5" Module="Core" Type="1">KEdNVCArMDU6MDAp</PHRASE> <PHRASE Label="la_p6" Module="Core" Type="1">KEdNVCArMDY6MDAp</PHRASE> <PHRASE Label="la_p7" Module="Core" Type="1">KEdNVCArMDc6MDAp</PHRASE> <PHRASE Label="la_p8" Module="Core" Type="1">KEdNVCArMDg6MDAp</PHRASE> <PHRASE Label="la_p9" Module="Core" Type="1">KEdNVCArMDk6MDAp</PHRASE> <PHRASE Label="la_Paddings" Module="Core" Type="1">UGFkZGluZ3M=</PHRASE> <PHRASE Label="la_Page" Module="Core" Type="1">UGFnZQ==</PHRASE> <PHRASE Label="la_PageCurrentlyEditing1" Module="Core" Type="1">QXR0ZW50aW9uOiAlcyBpcyBjdXJyZW50bHkgZWRpdGluZyB0aGlzIHNlY3Rpb24h</PHRASE> <PHRASE Label="la_PageCurrentlyEditing2" Module="Core" Type="1">QXR0ZW50aW9uOiAlcyBhcmUgY3VycmVudGx5IGVkaXRpbmcgdGhpcyBzZWN0aW9uISA=</PHRASE> <PHRASE Label="la_PageCurrentlyEditing5" Module="Core" Type="1">QXR0ZW50aW9uOiAlcyBhcmUgY3VycmVudGx5IGVkaXRpbmcgdGhpcyBzZWN0aW9uIQ==</PHRASE> <PHRASE Label="la_passwords_do_not_match" Module="Core" Type="1">UGFzc3dvcmRzIGRvIG5vdCBtYXRjaA==</PHRASE> <PHRASE Label="la_passwords_too_short" Module="Core" Type="1">UGFzc3dvcmQgaXMgdG9vIHNob3J0LCBwbGVhc2UgZW50ZXIgYXQgbGVhc3QgJXMgY2hhcmFjdGVycw==</PHRASE> <PHRASE Label="la_Pending" Module="Core" Type="1">UGVuZGluZw==</PHRASE> <PHRASE Label="la_performing_backup" Module="Core" Type="1">UGVyZm9ybWluZyBCYWNrdXA=</PHRASE> <PHRASE Label="la_performing_import" Module="Core" Type="1">UGVyZm9ybWluZyBJbXBvcnQ=</PHRASE> <PHRASE Label="la_performing_restore" Module="Core" Type="1">UGVyZm9ybWluZyBSZXN0b3Jl</PHRASE> <PHRASE Label="la_permission_in-portal:configure_lang.advanced:export" Module="Core" Type="1">RXhwb3J0IExhbmd1YWdlIHBhY2s=</PHRASE> <PHRASE Label="la_permission_in-portal:configure_lang.advanced:import" Module="Core" Type="1">SW1wb3J0IExhbmd1YWdlIHBhY2s=</PHRASE> <PHRASE Label="la_permission_in-portal:configure_lang.advanced:set_primary" Module="Core" Type="1">U2V0IFByaW1hcnkgTGFuZ3VhZ2U=</PHRASE> <PHRASE Label="la_permission_in-portal:mod_status.advanced:approve" Module="Core" Type="1">RW5hYmxlIE1vZHVsZXM=</PHRASE> <PHRASE Label="la_permission_in-portal:mod_status.advanced:decline" Module="Core" Type="1">RGlzYWJsZSBNb2R1bGVz</PHRASE> <PHRASE Label="la_permission_in-portal:user_groups.advanced:manage_permissions" Module="Core" Type="1">TWFuYWdlIFBlcm1pc3Npb25z</PHRASE> <PHRASE Label="la_permission_in-portal:user_groups.advanced:send_email" Module="Core" Type="1">U2VuZCBFLW1haWwgdG8gR3JvdXBzIGluIEFkbWlu</PHRASE> <PHRASE Label="la_permission_in-portal:user_list.advanced:ban" Module="Core" Type="1">QmFuIFVzZXJz</PHRASE> <PHRASE Label="la_permission_in-portal:user_list.advanced:send_email" Module="Core" Type="1">U2VuZCBFLW1haWwgdG8gVXNlcnMgaW4gQWRtaW4=</PHRASE> <PHRASE Label="la_PermName_Admin_desc" Module="Core" Type="1">QWRtaW4gTG9naW4=</PHRASE> <PHRASE Label="la_PermName_Category.AddPending_desc" Module="Core" Type="1">QWRkIFBlbmRpbmcgQ2F0ZWdvcnk=</PHRASE> <PHRASE Label="la_PermName_Category.Add_desc" Module="Core" Type="1">QWRkIENhdGVnb3J5</PHRASE> <PHRASE Label="la_PermName_Category.Delete_desc" Module="Core" Type="1">RGVsZXRlIENhdGVnb3J5</PHRASE> <PHRASE Label="la_PermName_Category.Modify_desc" Module="Core" Type="1">TW9kaWZ5IENhdGVnb3J5</PHRASE> <PHRASE Label="la_PermName_Category.Revision.Add.Pending_desc" Module="Core" Type="1">QWxsb3cgQWRkaW5nIFBlbmRpbmcgQ29udGVudCBSZXZpc2lvbnM=</PHRASE> <PHRASE Label="la_PermName_Category.Revision.Add_desc" Module="Core" Type="1">QWxsb3cgQWRkaW5nIENvbnRlbnQgUmV2aXNpb25z</PHRASE> <PHRASE Label="la_PermName_Category.Revision.History.Restore_desc" Module="Core" Type="1">QWxsb3cgUmVzdG9yaW5nIENvbnRlbnQgUmV2aXNpb25zIGZyb20gSGlzdG9yeQ==</PHRASE> <PHRASE Label="la_PermName_Category.Revision.History.View_desc" Module="Core" Type="1">QWxsb3cgVmlld2luZyBIaXN0b3J5IG9mIENvbnRlbnQgUmV2aXNpb25z</PHRASE> <PHRASE Label="la_PermName_Category.Revision.Moderate_desc" Module="Core" Type="1">QWxsb3cgTW9kZXJhdGluZyAoQXBwcm92ZS9EZWNsaW5lKSBDb250ZW50IFJldmlzaW9ucw==</PHRASE> <PHRASE Label="la_PermName_Category.View_desc" Module="Core" Type="1">VmlldyBDYXRlZ29yeQ==</PHRASE> <PHRASE Label="la_PermName_Debug.Info_desc" Module="Core" Type="1">QXBwZW5kIHBocGluZm8gdG8gYWxsIHBhZ2VzIChEZWJ1Zyk=</PHRASE> <PHRASE Label="la_PermName_Debug.Item_desc" Module="Core" Type="1">RGlzcGxheSBJdGVtIFF1ZXJpZXMgKERlYnVnKQ==</PHRASE> <PHRASE Label="la_PermName_Debug.List_desc" Module="Core" Type="1">RGlzcGxheSBJdGVtIExpc3QgUXVlcmllcyAoRGVidWcp</PHRASE> <PHRASE Label="la_PermName_favorites_desc" Module="Core" Type="1">QWxsb3cgZmF2b3JpdGVz</PHRASE> <PHRASE Label="la_PermName_Login_desc" Module="Core" Type="1">QWxsb3cgTG9naW4=</PHRASE> <PHRASE Label="la_PermName_Profile.Modify_desc" Module="Core" Type="1">Q2hhbmdlIFVzZXIgUHJvZmlsZXM=</PHRASE> <PHRASE Label="la_PermName_ShowLang_desc" Module="Core" Type="1">U2hvdyBMYW5ndWFnZSBUYWdz</PHRASE> <PHRASE Label="la_PermName_SystemAccess.ReadOnly_desc" Module="Core" Type="1">UmVhZC1Pbmx5IEFjY2VzcyBUbyBEYXRhYmFzZQ==</PHRASE> <PHRASE Label="la_PhraseNotTranslated" Module="Core" Type="1">Tm90IFRyYW5zbGF0ZWQ=</PHRASE> <PHRASE Label="la_PhraseTranslated" Module="Core" Type="1">VHJhbnNsYXRlZA==</PHRASE> <PHRASE Label="la_PhraseType_Admin" Module="Core" Type="1">QWRtaW4=</PHRASE> <PHRASE Label="la_PhraseType_Both" Module="Core" Type="1">Qm90aA==</PHRASE> <PHRASE Label="la_PhraseType_Front" Module="Core" Type="1">RnJvbnQ=</PHRASE> <PHRASE Label="la_Pick" Module="Core" Type="1">UGljaw==</PHRASE> <PHRASE Label="la_PickedColumns" Module="Core" Type="1">U2VsZWN0ZWQgQ29sdW1ucw==</PHRASE> <PHRASE Label="la_Pop" Module="Core" Type="1">UG9wdWxhcg==</PHRASE> <PHRASE Label="la_PositionAndVisibility" Module="Core" Type="1">UG9zaXRpb24gQW5kIFZpc2liaWxpdHk=</PHRASE> <PHRASE Label="la_prevcategory" Module="Core" Type="1">UHJldmlvdXMgc2VjdGlvbg==</PHRASE> <PHRASE Label="la_PrimaryCategory" Module="Core" Type="1">UHJpbWFyeQ==</PHRASE> <PHRASE Label="la_prompt_ActiveCategories" Module="Core" Type="1">QWN0aXZlIFNlY3Rpb25z</PHRASE> <PHRASE Label="la_prompt_ActiveUsers" Module="Core" Type="1">QWN0aXZlIFVzZXJz</PHRASE> <PHRASE Label="la_prompt_AddressTo" Module="Core" Type="1">U2VudCBUbw==</PHRASE> <PHRASE Label="la_prompt_AdminMailFrom" Module="Core" Type="1">TWVzc2FnZXMgZnJvbSBTaXRlIEFkbWluIGFyZSBmcm9t</PHRASE> <PHRASE Label="la_prompt_AdvancedSearch" Module="Core" Type="1">QWR2YW5jZWQgU2VhcmNo</PHRASE> <PHRASE Label="la_prompt_AdvancedUserManagement" Module="Core" Type="1">QWR2YW5jZWQgVXNlciBNYW5hZ2VtZW50</PHRASE> <PHRASE Label="la_prompt_allow_reset" Module="Core" Type="1">QWxsb3cgcGFzc3dvcmQgcmVzZXQgYWZ0ZXI=</PHRASE> <PHRASE Label="la_prompt_AutoGen_Excerpt" Module="Core" Type="1">R2VuZXJhdGUgZnJvbSB0aGUgYXJ0aWNsZSBib2R5</PHRASE> <PHRASE Label="la_Prompt_Backup_Date" Module="Core" Type="1">RGF0ZSBvZiBCYWNrdXA6</PHRASE> <PHRASE Label="la_prompt_Backup_Path" Module="Core" Type="1">QmFja3VwIFBhdGg=</PHRASE> <PHRASE Label="la_Prompt_Backup_Status" Module="Core" Type="1">QmFja3VwIHN0YXR1cw==</PHRASE> <PHRASE Label="la_prompt_BannedUsers" Module="Core" Type="1">QmFubmVkIFVzZXJz</PHRASE> <PHRASE Label="la_prompt_birthday" Module="Core" Type="1">RGF0ZSBvZiBCaXJ0aA==</PHRASE> <PHRASE Label="la_prompt_CategoryEditorsPick" Module="Core" Type="1">RWRpdG9yJ3MgUGljayBTZWN0aW9ucw==</PHRASE> <PHRASE Label="la_prompt_CurrentSessions" Module="Core" Type="1">Q3VycmVudCBTZXNzaW9ucw==</PHRASE> <PHRASE Label="la_prompt_DataSize" Module="Core" Type="1">VG90YWwgU2l6ZSBvZiB0aGUgRGF0YWJhc2U=</PHRASE> <PHRASE Label="la_prompt_Default" Module="Core" Type="1">RGVmYXVsdA==</PHRASE> <PHRASE Label="la_prompt_DefaultUserId" Module="Core" Type="1">VXNlciBJRCBmb3IgRGVmYXVsdCBQZXJzaXN0ZW50IFNldHRpbmdz</PHRASE> <PHRASE Label="la_prompt_DefaultValue" Module="Core" Type="1">RGVmYXVsdCBWYWx1ZQ==</PHRASE> <PHRASE Label="la_prompt_DisabledCategories" Module="Core" Type="1">RGlzYWJsZWQgU2VjdGlvbnM=</PHRASE> <PHRASE Label="la_prompt_DisplayInGrid" Module="Core" Type="1">RGlzcGxheSBpbiBHcmlk</PHRASE> <PHRASE Label="la_prompt_DisplayOrder" Module="Core" Type="1">RGlzcGxheSBPcmRlcg==</PHRASE> <PHRASE Label="la_prompt_DupRating" Module="Core" Type="1">QWxsb3cgRHVwbGljYXRlIFJhdGluZyBWb3Rlcw==</PHRASE> <PHRASE Label="la_prompt_DupReviews" Module="Core" Type="1">QWxsb3cgRHVwbGljYXRlIFJldmlld3M=</PHRASE> <PHRASE Label="la_prompt_EditorsPick" Module="Core" Type="1">RWRpdG9yJ3MgUGljaw==</PHRASE> <PHRASE Label="la_prompt_ElementType" Module="Core" Type="1">VHlwZQ==</PHRASE> <PHRASE Label="la_prompt_EmailCompleteMessage" Module="Core" Type="1">VGhlIEVtYWlsIE1lc3NhZ2UgaGFzIGJlZW4gc2VudA==</PHRASE> <PHRASE Label="la_prompt_ExportCompleteMessage" Module="Core" Type="1">RXhwb3J0IENvbXBsZXRlIQ==</PHRASE> <PHRASE Label="la_prompt_FieldId" Module="Core" Type="1">RmllbGQgSWQ=</PHRASE> <PHRASE Label="la_prompt_FieldLabel" Module="Core" Type="1">RmllbGQgTGFiZWw=</PHRASE> <PHRASE Label="la_prompt_FieldName" Module="Core" Type="1">RmllbGQgTmFtZQ==</PHRASE> <PHRASE Label="la_prompt_FieldPrompt" Module="Core" Type="1">RmllbGQgUHJvbXB0</PHRASE> <PHRASE Label="la_prompt_Frequency" Module="Core" Type="1">RnJlcXVlbmN5</PHRASE> <PHRASE Label="la_prompt_heading" Module="Core" Type="1">SGVhZGluZw==</PHRASE> <PHRASE Label="la_prompt_HitLimits" Module="Core" Type="1">KE1pbmltdW0gNCk=</PHRASE> <PHRASE Label="la_prompt_Import_Source" Module="Core" Type="1">SW1wb3J0IFNvdXJjZQ==</PHRASE> <PHRASE Label="la_prompt_InputType" Module="Core" Type="1">SW5wdXQgVHlwZQ==</PHRASE> <PHRASE Label="la_prompt_KeepSessionOnBrowserClose" Module="Core" Type="1">S2VlcCBTZXNzaW9uIFdoZW4gQnJvc3dlciBJcyBDbG9zZWQ=</PHRASE> <PHRASE Label="la_prompt_lang_cache_timeout" Module="Core" Type="1">TGFuZ3VhZ2UgQ2FjaGUgVGltZW91dA==</PHRASE> <PHRASE Label="la_prompt_LastCategoryUpdate" Module="Core" Type="1">TGFzdCBTZWN0aW9uIFVwZGF0ZQ==</PHRASE> <PHRASE Label="la_prompt_LastLinkUpdate" Module="Core" Type="1">TGFzdCBVcGRhdGVkIExpbms=</PHRASE> <PHRASE Label="la_prompt_mailauthenticate" Module="Core" Type="1">U2VydmVyIFJlcXVpcmVzIEF1dGhlbnRpY2F0aW9u</PHRASE> <PHRASE Label="la_prompt_mailport" Module="Core" Type="1">UG9ydCAoZS5nLiBwb3J0IDI1KQ==</PHRASE> <PHRASE Label="la_prompt_mailserver" Module="Core" Type="1">TWFpbCBTZXJ2ZXIgQWRkcmVzcw==</PHRASE> <PHRASE Label="la_prompt_max_import_category_levels" Module="Core" Type="1">TWF4aW1hbCBpbXBvcnRlZCBzZWN0aW9uIGxldmVs</PHRASE> <PHRASE Label="la_prompt_MembershipExpires" Module="Core" Type="1">TWVtYmVyc2hpcCBFeHBpcmVz</PHRASE> <PHRASE Label="la_prompt_MenuFrameWidth" Module="Core" Type="1">TGVmdCBNZW51IChUcmVlKSBXaWR0aA==</PHRASE> <PHRASE Label="la_prompt_movedown" Module="Core" Type="1">TW92ZSBkb3du</PHRASE> <PHRASE Label="la_prompt_moveup" Module="Core" Type="1">TW92ZSB1cA==</PHRASE> <PHRASE Label="la_prompt_multipleshow" Module="Core" Type="1">U2hvdyBtdWx0aXBsZQ==</PHRASE> <PHRASE Label="la_prompt_NewCategories" Module="Core" Type="1">TmV3IFNlY3Rpb25z</PHRASE> <PHRASE Label="la_prompt_NewestCategoryDate" Module="Core" Type="1">TmV3ZXN0IFNlY3Rpb24gRGF0ZQ==</PHRASE> <PHRASE Label="la_prompt_NewestLinkDate" Module="Core" Type="1">TmV3ZXN0IExpbmsgRGF0ZQ==</PHRASE> <PHRASE Label="la_prompt_NewestUserDate" Module="Core" Type="1">TmV3ZXN0IFVzZXIgRGF0ZQ==</PHRASE> <PHRASE Label="la_prompt_NonExpiredSessions" Module="Core" Type="1">Q3VycmVudGx5IEFjdGl2ZSBVc2VyIFNlc3Npb25z</PHRASE> <PHRASE Label="la_prompt_PendingCategories" Module="Core" Type="1">UGVuZGluZyBTZWN0aW9ucw==</PHRASE> <PHRASE Label="la_prompt_PendingItems" Module="Core" Type="1">UGVuZGluZyBJdGVtcw==</PHRASE> <PHRASE Label="la_prompt_perform_now" Module="Core" Type="1">UGVyZm9ybSB0aGlzIG9wZXJhdGlvbiBub3c/</PHRASE> <PHRASE Label="la_prompt_PerPage" Module="Core" Type="1">UGVyIFBhZ2U=</PHRASE> <PHRASE Label="la_prompt_PersonalInfo" Module="Core" Type="1">UGVyc29uYWwgSW5mb3JtYXRpb24=</PHRASE> <PHRASE Label="la_prompt_PrimaryGroup" Module="Core" Type="1">UHJpbWFyeSBHcm91cA==</PHRASE> <PHRASE Label="la_prompt_Priority" Module="Core" Type="1">UHJpb3JpdHk=</PHRASE> <PHRASE Label="la_prompt_Rating" Module="Core" Type="1">UmF0aW5n</PHRASE> <PHRASE Label="la_prompt_RatingLimits" Module="Core" Type="1">KE1pbmltdW0gMCwgTWF4aW11bSA1KQ==</PHRASE> <PHRASE Label="la_prompt_RecordsCount" Module="Core" Type="1">TnVtYmVyIG9mIERhdGFiYXNlIFJlY29yZHM=</PHRASE> <PHRASE Label="la_prompt_RegionsCount" Module="Core" Type="1">TnVtYmVyIG9mIFJlZ2lvbiBQYWNrcw==</PHRASE> <PHRASE Label="la_prompt_relevence_percent" Module="Core" Type="1">U2VhcmNoIFJlbGV2YW5jZSBkZXBlbmRzIG9u</PHRASE> <PHRASE Label="la_prompt_relevence_settings" Module="Core" Type="1">U2VhcmNoIFJlbGV2ZW5jZSBTZXR0aW5ncw==</PHRASE> <PHRASE Label="la_prompt_Required" Module="Core" Type="1">UmVxdWlyZWQ=</PHRASE> <PHRASE Label="la_prompt_required_field_increase" Module="Core" Type="1">SW5jcmVhc2UgaW1wb3J0YW5jZSBpZiBmaWVsZCBjb250YWlucyBhIHJlcXVpcmVkIGtleXdvcmQgYnk=</PHRASE> <PHRASE Label="la_Prompt_Restore_Failed" Module="Core" Type="1">UmVzdG9yZSBoYXMgZmFpbGVkIGFuIGVycm9yIG9jY3VyZWQ6</PHRASE> <PHRASE Label="la_Prompt_Restore_Filechoose" Module="Core" Type="1">Q2hvb3NlIG9uZSBvZiB0aGUgZm9sbG93aW5nIGJhY2t1cCBkYXRlcyB0byByZXN0b3JlIG9yIGRlbGV0ZQ==</PHRASE> <PHRASE Label="la_Prompt_Restore_Status" Module="Core" Type="1">UmVzdG9yZSBTdGF0dXM=</PHRASE> <PHRASE Label="la_Prompt_Restore_Success" Module="Core" Type="1">UmVzdG9yZSBoYXMgYmVlbiBjb21wbGV0ZWQgc3VjY2Vzc2Z1bGx5</PHRASE> <PHRASE Label="la_prompt_RootCategory" Module="Core" Type="1">U2VsZWN0IE1vZHVsZSBSb290IFNlY3Rpb246</PHRASE> <PHRASE Label="la_prompt_root_pass" Module="Core" Type="1">Um9vdCBQYXNzd29yZA==</PHRASE> <PHRASE Label="la_prompt_SearchType" Module="Core" Type="1">U2VhcmNoIFR5cGU=</PHRASE> <PHRASE Label="la_prompt_Select_Source" Module="Core" Type="1">U2VsZWN0IFNvdXJjZSBMYW5ndWFnZQ==</PHRASE> <PHRASE Label="la_prompt_SentOn" Module="Core" Type="1">U2VudCBPbg==</PHRASE> <PHRASE Label="la_prompt_session_cookie_name" Module="Core" Type="1">U2Vzc2lvbiBDb29raWUgTmFtZQ==</PHRASE> <PHRASE Label="la_prompt_session_management" Module="Core" Type="1">U2Vzc2lvbiBNYW5hZ2VtZW50IE1ldGhvZA==</PHRASE> <PHRASE Label="la_prompt_session_timeout" Module="Core" Type="1">U2Vzc2lvbiBJbmFjdGl2aXR5IFRpbWVvdXQgKHNlY29uZHMp</PHRASE> <PHRASE Label="la_prompt_showgeneraltab" Module="Core" Type="1">U2hvdyBvbiB0aGUgZ2VuZXJhbCB0YWI=</PHRASE> <PHRASE Label="la_prompt_SimpleSearch" Module="Core" Type="1">U2ltcGxlIFNlYXJjaA==</PHRASE> <PHRASE Label="la_prompt_smtpheaders" Module="Core" Type="1">QWRkaXRpb25hbCBNZXNzYWdlIEhlYWRlcnM=</PHRASE> <PHRASE Label="la_prompt_smtp_pass" Module="Core" Type="1">TWFpbCBTZXJ2ZXIgUGFzc3dvcmQ=</PHRASE> <PHRASE Label="la_prompt_smtp_user" Module="Core" Type="1">TWFpbCBTZXJ2ZXIgVXNlcm5hbWU=</PHRASE> <PHRASE Label="la_prompt_socket_blocking_mode" Module="Core" Type="1">VXNlIG5vbi1ibG9ja2luZyBzb2NrZXQgbW9kZQ==</PHRASE> <PHRASE Label="la_prompt_sqlquery" Module="Core" Type="1">U1FMIFF1ZXJ5Og==</PHRASE> <PHRASE Label="la_prompt_sqlquery_header" Module="Core" Type="1">UGVyZm9ybSBTUUwgUXVlcnk=</PHRASE> <PHRASE Label="la_Prompt_Step_One" Module="Core" Type="1">U3RlcCBPbmU=</PHRASE> <PHRASE Label="la_prompt_SumbissionTime" Module="Core" Type="1">U3VibWl0dGVkIE9u</PHRASE> <PHRASE Label="la_prompt_syscache_enable" Module="Core" Type="1">RW5hYmxlIFRhZyBDYWNoaW5n</PHRASE> <PHRASE Label="la_prompt_SystemFileSize" Module="Core" Type="1">VG90YWwgU2l6ZSBvZiBTeXN0ZW0gRmlsZXM=</PHRASE> <PHRASE Label="la_prompt_TablesCount" Module="Core" Type="1">TnVtYmVyIG9mIERhdGFiYXNlIFRhYmxlcw==</PHRASE> <PHRASE Label="la_prompt_ThemeCount" Module="Core" Type="1">TnVtYmVyIG9mIFRoZW1lcw==</PHRASE> <PHRASE Label="la_prompt_TotalCategories" Module="Core" Type="1">VG90YWwgU2VjdGlvbnM=</PHRASE> <PHRASE Label="la_prompt_TotalUserGroups" Module="Core" Type="1">VG90YWwgVXNlciBHcm91cHM=</PHRASE> <PHRASE Label="la_prompt_UsersActive" Module="Core" Type="1">QWN0aXZlIFVzZXJz</PHRASE> <PHRASE Label="la_prompt_UsersDisabled" Module="Core" Type="1">RGlzYWJsZWQgVXNlcnM=</PHRASE> <PHRASE Label="la_prompt_UsersPending" Module="Core" Type="1">UGVuZGluZyBVc2Vycw==</PHRASE> <PHRASE Label="la_prompt_UsersUniqueCountries" Module="Core" Type="1">TnVtYmVyIG9mIFVuaXF1ZSBDb3VudHJpZXMgb2YgVXNlcnM=</PHRASE> <PHRASE Label="la_prompt_UsersUniqueStates" Module="Core" Type="1">TnVtYmVyIG9mIFVuaXF1ZSBTdGF0ZXMgb2YgVXNlcnM=</PHRASE> <PHRASE Label="la_prompt_validation" Module="Core" Type="1">VmFsaWRhdGlvbg==</PHRASE> <PHRASE Label="la_prompt_valuelist" Module="Core" Type="1">TGlzdCBvZiBWYWx1ZXM=</PHRASE> <PHRASE Label="la_prompt_VoteLimits" Module="Core" Type="1">KE1pbmltdW0gMSk=</PHRASE> <PHRASE Label="la_Prompt_Warning" Module="Core" Type="1">V2FybmluZyE=</PHRASE> <PHRASE Label="la_prompt_weight" Module="Core" Type="1">V2VpZ2h0</PHRASE> <PHRASE Label="la_Quotes" Module="Core" Type="1">U2luZ2xlLVF1b3RlcyAoaWUuICcp</PHRASE> <PHRASE Label="la_Reciprocal" Module="Core" Type="1">UmVjaXByb2NhbA==</PHRASE> <PHRASE Label="la_Records" Module="Core" Type="1">UmVjb3Jkcw==</PHRASE> <PHRASE Label="la_record_being_edited_by" Module="Core" Type="1">VGhpcyByZWNvcmQgaXMgYmVpbmcgZWRpdGVkIGJ5IHRoZSBmb2xsb3dpbmcgdXNlcnM6DQolcw==</PHRASE> <PHRASE Label="la_registration_captcha" Module="Core" Type="1">VXNlIENhcHRjaGEgY29kZSBvbiBSZWdpc3RyYXRpb24=</PHRASE> <PHRASE Label="la_Regular" Module="Core" Type="1">UmVndWxhcg==</PHRASE> <PHRASE Label="la_RemoveFrom" Module="Core" Type="1">UmVtb3ZlIEZyb20=</PHRASE> <PHRASE Label="la_RequiredWarning" Module="Core" Type="1">Tm90IGFsbCByZXF1aXJlZCBmaWVsZHMgYXJlIGZpbGxlZC4gUGxlYXNlIGZpbGwgdGhlbSBmaXJzdC4=</PHRASE> <PHRASE Label="la_review_perpage_prompt" Module="Core" Type="1">Q29tbWVudHMgcGVyIFBhZ2U=</PHRASE> <PHRASE Label="la_review_perpage_short_prompt" Module="Core" Type="1">Q29tbWVudHMgcGVyIFBhZ2UgKHNob3J0LWxpc3Qp</PHRASE> <PHRASE Label="la_RevisionNumber" Module="Core" Type="1">UmV2aXNpb24gIyVz</PHRASE> <PHRASE Label="la_rootcategory_name" Module="Core" Type="1">SG9tZQ==</PHRASE> <PHRASE Label="la_SampleText" Module="Core" Type="1">U2FtcGxlIFRleHQ=</PHRASE> <PHRASE Label="la_SavedAt" Module="Core" Type="1">c2F2ZWQgYXQgJXM=</PHRASE> <PHRASE Label="la_SaveLogin" Module="Core" Type="1">U2F2ZSBVc2VybmFtZSBvbiBUaGlzIENvbXB1dGVy</PHRASE> <PHRASE Label="la_Search" Module="Core" Type="1">U2VhcmNo</PHRASE> <PHRASE Label="la_section_BasicPermissions" Module="Core" Type="1">QmFzaWMgUGVybWlzc2lvbnM=</PHRASE> <PHRASE Label="la_section_Category" Module="Core" Type="1">U2VjdGlvbg==</PHRASE> <PHRASE Label="la_section_Configs" Module="Core" Type="1">Q29uZmlnIEZpbGVz</PHRASE> <PHRASE Label="la_section_Counters" Module="Core" Type="1">Q291bnRlcnM=</PHRASE> <PHRASE Label="la_section_CustomFields" Module="Core" Type="1">Q3VzdG9tIEZpZWxkcw==</PHRASE> <PHRASE Label="la_section_Data" Module="Core" Type="1">U3VibWlzc2lvbiBEYXRh</PHRASE> <PHRASE Label="la_section_EmailDesignTemplates" Module="Core" Type="1">RS1tYWlsIERlc2lnbiBUZW1wbGF0ZXM=</PHRASE> <PHRASE Label="la_section_File" Module="Core" Type="1">RmlsZQ==</PHRASE> <PHRASE Label="la_section_FrontEnd" Module="Core" Type="1">RnJvbnQtZW5k</PHRASE> <PHRASE Label="la_section_FullSizeImage" Module="Core" Type="1">RnVsbCBTaXplIEltYWdl</PHRASE> <PHRASE Label="la_section_General" Module="Core" Type="1">R2VuZXJhbA==</PHRASE> <PHRASE Label="la_section_Image" Module="Core" Type="1">SW1hZ2U=</PHRASE> <PHRASE Label="la_section_ImageSettings" Module="Core" Type="1">SW1hZ2UgU2V0dGluZ3M=</PHRASE> <PHRASE Label="la_section_ImportCompleted" Module="Core" Type="1">SW1wb3J0IENvbXBsZXRlZA==</PHRASE> <PHRASE Label="la_section_Items" Module="Core" Type="1">VXNlciBJdGVtcw==</PHRASE> <PHRASE Label="la_section_MemoryCache" Module="Core" Type="1">TWVtb3J5IENhY2hl</PHRASE> <PHRASE Label="la_section_Message" Module="Core" Type="1">TWVzc2FnZQ==</PHRASE> <PHRASE Label="la_section_overview" Module="Core" Type="1">U2VjdGlvbiBPdmVydmlldw==</PHRASE> <PHRASE Label="la_section_Page" Module="Core" Type="1">U2VjdGlvbiBQcm9wZXJ0aWVz</PHRASE> <PHRASE Label="la_section_PageCaching" Module="Core" Type="1">U2VjdGlvbiBDYWNoaW5n</PHRASE> <PHRASE Label="la_section_ProjectDeployment" Module="Core" Type="1">UHJvamVjdCBEZXBsb3ltZW50</PHRASE> <PHRASE Label="la_section_Properties" Module="Core" Type="1">UHJvcGVydGllcw==</PHRASE> <PHRASE Label="la_section_QuickLinks" Module="Core" Type="1">UXVpY2sgTGlua3M=</PHRASE> <PHRASE Label="la_section_RecipientsInfo" Module="Core" Type="1">UmVjaXBpZW50cyBJbmZvcm1hdGlvbg==</PHRASE> <PHRASE Label="la_section_Relation" Module="Core" Type="1">UmVsYXRpb24=</PHRASE> <PHRASE Label="la_section_ReplacementTags" Module="Core" Type="1">UmVwbGFjZW1lbnQgVGFncw==</PHRASE> <PHRASE Label="la_section_SenderInfo" Module="Core" Type="1">U2VuZGVyIEluZm9ybWF0aW9u</PHRASE> <PHRASE Label="la_section_Settings" Module="Core" Type="1">U2V0dGluZ3M=</PHRASE> <PHRASE Label="la_section_Settings3rdPartyAPI" Module="Core" Type="1">M3JkIFBhcnR5IEFQSSBTZXR0aW5ncw==</PHRASE> <PHRASE Label="la_section_SettingsAdmin" Module="Core" Type="1">QWRtaW4gQ29uc29sZSBTZXR0aW5ncw==</PHRASE> <PHRASE Label="la_section_SettingsCSVExport" Module="Core" Type="1">Q1NWIEV4cG9ydCBTZXR0aW5ncw==</PHRASE> <PHRASE Label="la_section_SettingsLogs" Module="Core" Type="1">TG9ncyBTZXR0aW5ncw==</PHRASE> <PHRASE Label="la_section_SettingsMailling" Module="Core" Type="1">TWFpbGluZyBTZXR0aW5ncw==</PHRASE> <PHRASE Label="la_section_SettingsMaintenance" Module="Core" Type="1">TWFpbnRlbmFuY2UgU2V0dGluZ3M=</PHRASE> <PHRASE Label="la_section_SettingsSession" Module="Core" Type="1">U2Vzc2lvbiBTZXR0aW5ncw==</PHRASE> <PHRASE Label="la_section_SettingsSSL" Module="Core" Type="1">U1NMIFNldHRpbmdz</PHRASE> <PHRASE Label="la_section_SettingsSystem" Module="Core" Type="1">U3lzdGVtIFNldHRpbmdz</PHRASE> <PHRASE Label="la_section_SettingsWebsite" Module="Core" Type="1">V2Vic2l0ZSBTZXR0aW5ncw==</PHRASE> <PHRASE Label="la_section_SubmissionNotes" Module="Core" Type="1">U3VibWlzc2lvbiBOb3Rlcw==</PHRASE> <PHRASE Label="la_section_Templates" Module="Core" Type="1">VGVtcGxhdGVz</PHRASE> <PHRASE Label="la_section_ThumbnailImage" Module="Core" Type="1">VGh1bWJuYWlsIEltYWdl</PHRASE> <PHRASE Label="la_section_Translation" Module="Core" Type="1">VHJhbnNsYXRpb24=</PHRASE> <PHRASE Label="la_section_UsersSearch" Module="Core" Type="1">U2VhcmNoIFVzZXJz</PHRASE> <PHRASE Label="la_section_Values" Module="Core" Type="1">VmFsdWVz</PHRASE> <PHRASE Label="la_SelectColumns" Module="Core" Type="1">U2VsZWN0IENvbHVtbnM=</PHRASE> <PHRASE Label="la_SelectedItems" Module="Core" Type="1">U2VsZWN0ZWQgSXRlbXM=</PHRASE> <PHRASE Label="la_selecting_categories" Module="Core" Type="1">U2VsZWN0aW5nIFNlY3Rpb25z</PHRASE> <PHRASE Label="la_SeparatedCategoryPath" Module="Core" Type="1">T25lIGZpZWxkIGZvciBlYWNoIHNlY3Rpb24gbGV2ZWw=</PHRASE> <PHRASE Label="la_ShortToolTip_Clone" Module="Core" Type="1">Q2xvbmU=</PHRASE> <PHRASE Label="la_ShortToolTip_CloneUser" Module="Core" Type="1">Q2xvbmU=</PHRASE> <PHRASE Label="la_ShortToolTip_Continue" Module="Core" Type="1">Q29udGludWU=</PHRASE> <PHRASE Label="la_ShortToolTip_Edit" Module="Core" Type="1">RWRpdA==</PHRASE> <PHRASE Label="la_ShortToolTip_Export" Module="Core" Type="1">RXhwb3J0</PHRASE> <PHRASE Label="la_ShortToolTip_GoUp" Module="Core" Type="1">R28gVXA=</PHRASE> <PHRASE Label="la_ShortToolTip_Import" Module="Core" Type="1">SW1wb3J0</PHRASE> <PHRASE Label="la_ShortToolTip_MoveDown" Module="Core" Type="1">RG93bg==</PHRASE> <PHRASE Label="la_ShortToolTip_MoveUp" Module="Core" Type="1">VXA=</PHRASE> <PHRASE Label="la_ShortToolTip_New" Module="Core" Type="1">TmV3</PHRASE> <PHRASE Label="la_ShortToolTip_Rebuild" Module="Core" Type="1">UmVidWlsZA==</PHRASE> <PHRASE Label="la_ShortToolTip_RescanThemes" Module="Core" Type="1">UmVzY2FuIFRoZW1lcw==</PHRASE> <PHRASE Label="la_ShortToolTip_ResetSettings" Module="Core" Type="1">UmVzZXQ=</PHRASE> <PHRASE Label="la_ShortToolTip_SetPrimary" Module="Core" Type="1">UHJpbWFyeQ==</PHRASE> <PHRASE Label="la_ShortToolTip_SynchronizeLanguages" Module="Core" Type="1">U3luY2hyb25pemU=</PHRASE> <PHRASE Label="la_ShortToolTip_View" Module="Core" Type="1">Vmlldw==</PHRASE> <PHRASE Label="la_Show" Module="Core" Type="1">U2hvdw==</PHRASE> <PHRASE Label="la_SQLAffectedRows" Module="Core" Type="1">QWZmZWN0ZWQgcm93cw==</PHRASE> <PHRASE Label="la_SQLRuntime" Module="Core" Type="1">RXhlY3V0ZWQgaW46</PHRASE> <PHRASE Label="la_step" Module="Core" Type="1">U3RlcA==</PHRASE> <PHRASE Label="la_StyleDefinition" Module="Core" Type="1">RGVmaW5pdGlvbg==</PHRASE> <PHRASE Label="la_StylePreview" Module="Core" Type="1">UHJldmlldw==</PHRASE> <PHRASE Label="la_sunday" Module="Core" Type="1">U3VuZGF5</PHRASE> <PHRASE Label="la_System" Module="Core" Type="1">U3lzdGVt</PHRASE> <PHRASE Label="la_tab_AdminUI" Module="Core" Type="1">QWRtaW5pc3RyYXRpb24gUGFuZWwgVUk=</PHRASE> <PHRASE Label="la_tab_AdvancedView" Module="Core" Type="1">QWR2YW5jZWQgVmlldw==</PHRASE> <PHRASE Label="la_tab_Backup" Module="Core" Type="1">QmFja3Vw</PHRASE> <PHRASE Label="la_tab_BanList" Module="Core" Type="1">QmFuIFJ1bGVz</PHRASE> <PHRASE Label="la_tab_BaseStyles" Module="Core" Type="1">QmFzZSBTdHlsZXM=</PHRASE> <PHRASE Label="la_tab_BlockStyles" Module="Core" Type="1">QmxvY2sgU3R5bGVz</PHRASE> <PHRASE Label="la_tab_Browse" Module="Core" Type="1">Q2F0YWxvZw==</PHRASE> <PHRASE Label="la_tab_BrowsePages" Module="Core" Type="1">QnJvd3NlIFdlYnNpdGU=</PHRASE> <PHRASE Label="la_tab_Categories" Module="Core" Type="1">U2VjdGlvbnM=</PHRASE> <PHRASE Label="la_tab_ChangeLog" Module="Core" Type="1">Q2hhbmdlcyBMb2c=</PHRASE> <PHRASE Label="la_tab_CMSForms" Module="Core" Type="1">Rm9ybXM=</PHRASE> <PHRASE Label="la_tab_Community" Module="Core" Type="1">VXNlciBNYW5hZ2VtZW50</PHRASE> <PHRASE Label="la_tab_ConfigCustom" Module="Core" Type="1">Q3VzdG9tIEZpZWxkcw==</PHRASE> <PHRASE Label="la_tab_ConfigE-mail" Module="Core" Type="1">RS1tYWlsIEV2ZW50cw==</PHRASE> <PHRASE Label="la_tab_ConfigGeneral" Module="Core" Type="1">R2VuZXJhbCBTZXR0aW5ncw==</PHRASE> <PHRASE Label="la_tab_ConfigOutput" Module="Core" Type="1">T3V0cHV0</PHRASE> <PHRASE Label="la_tab_ConfigSearch" Module="Core" Type="1">U2VhcmNo</PHRASE> <PHRASE Label="la_tab_ConfigSettings" Module="Core" Type="1">R2VuZXJhbA==</PHRASE> <PHRASE Label="la_tab_Custom" Module="Core" Type="1">Q3VzdG9t</PHRASE> <PHRASE Label="la_tab_EmailCommunication" Module="Core" Type="1">RS1tYWlsIENvbW11bmljYXRpb24=</PHRASE> <PHRASE Label="la_tab_EmailLog" Module="Core" Type="1">RS1tYWlsIExvZw==</PHRASE> <PHRASE Label="la_tab_EmailQueue" Module="Core" Type="1">RW1haWwgUXVldWU=</PHRASE> <PHRASE Label="la_tab_EmailTemplates" Module="Core" Type="1">RS1tYWlsIFRlbXBsYXRlcw==</PHRASE> <PHRASE Label="la_tab_Fields" Module="Core" Type="1">RmllbGRz</PHRASE> <PHRASE Label="la_tab_Files" Module="Core" Type="1">RmlsZXM=</PHRASE> <PHRASE Label="la_tab_FormsConfig" Module="Core" Type="1">Rm9ybXMgQ29uZmlndXJhdGlvbg==</PHRASE> <PHRASE Label="la_tab_General" Module="Core" Type="1">R2VuZXJhbA==</PHRASE> <PHRASE Label="la_tab_GeneralSettings" Module="Core" Type="1">R2VuZXJhbA==</PHRASE> <PHRASE Label="la_tab_Groups" Module="Core" Type="1">R3JvdXBz</PHRASE> <PHRASE Label="la_tab_Help" Module="Core" Type="1">SGVscA==</PHRASE> <PHRASE Label="la_tab_Images" Module="Core" Type="1">SW1hZ2Vz</PHRASE> <PHRASE Label="la_tab_ImportData" Module="Core" Type="1">SW1wb3J0IERhdGE=</PHRASE> <PHRASE Label="la_tab_Items" Module="Core" Type="1">SXRlbXM=</PHRASE> <PHRASE Label="la_tab_Labels" Module="Core" Type="1">TGFiZWxz</PHRASE> <PHRASE Label="la_tab_LogsAndReports" Module="Core" Type="1">TG9ncyAmIFJlcG9ydHM=</PHRASE> <PHRASE Label="la_tab_Messages" Module="Core" Type="1">TWVzc2FnZXM=</PHRASE> <PHRASE Label="la_tab_PackageContent" Module="Core" Type="1">UGFja2FnZSBDb250ZW50</PHRASE> <PHRASE Label="la_tab_Permissions" Module="Core" Type="1">UGVybWlzc2lvbnM=</PHRASE> <PHRASE Label="la_tab_PermissionTypes" Module="Core" Type="1">UGVybWlzc2lvbiBUeXBlcw==</PHRASE> <PHRASE Label="la_tab_PromoBlocks" Module="Core" Type="1">UHJvbW8gQmxvY2tz</PHRASE> <PHRASE Label="la_tab_Properties" Module="Core" Type="1">UHJvcGVydGllcw==</PHRASE> <PHRASE Label="la_tab_QueryDB" Module="Core" Type="1">UXVlcnkgRGF0YWJhc2U=</PHRASE> <PHRASE Label="la_tab_Regional" Module="Core" Type="1">UmVnaW9uYWw=</PHRASE> <PHRASE Label="la_tab_Related_Searches" Module="Core" Type="1">UmVsYXRlZCBTZWFyY2hlcw==</PHRASE> <PHRASE Label="la_tab_Relations" Module="Core" Type="1">UmVsYXRpb25z</PHRASE> <PHRASE Label="la_tab_Restore" Module="Core" Type="1">UmVzdG9yZQ==</PHRASE> <PHRASE Label="la_tab_Reviews" Module="Core" Type="1">Q29tbWVudHM=</PHRASE> <PHRASE Label="la_Tab_Search" Module="Core" Type="1">U2VhcmNo</PHRASE> <PHRASE Label="la_tab_SearchLog" Module="Core" Type="1">U2VhcmNoIExvZw==</PHRASE> <PHRASE Label="la_tab_ServerInfo" Module="Core" Type="1">UEhQIEluZm9ybWF0aW9u</PHRASE> <PHRASE Label="la_Tab_Service" Module="Core" Type="1">U3lzdGVtIFRvb2xz</PHRASE> <PHRASE Label="la_tab_SessionLog" Module="Core" Type="1">U2Vzc2lvbiBMb2c=</PHRASE> <PHRASE Label="la_tab_SessionLogs" Module="Core" Type="1">U2Vzc2lvbiBMb2c=</PHRASE> <PHRASE Label="la_tab_Settings" Module="Core" Type="1">U2V0dGluZ3M=</PHRASE> <PHRASE Label="la_tab_ShowAll" Module="Core" Type="1">U2hvdyBBbGw=</PHRASE> <PHRASE Label="la_tab_ShowStructure" Module="Core" Type="1">U2hvdyBTdHJ1Y3R1cmU=</PHRASE> <PHRASE Label="la_tab_Site_Structure" Module="Core" Type="1">V2Vic2l0ZSAmIENvbnRlbnQ=</PHRASE> <PHRASE Label="la_tab_Skins" Module="Core" Type="1">QWRtaW4gU2tpbnM=</PHRASE> <PHRASE Label="la_tab_Summary" Module="Core" Type="1">U3VtbWFyeQ==</PHRASE> <PHRASE Label="la_tab_SystemLog" Module="Core" Type="1">U3lzdGVtIExvZw==</PHRASE> <PHRASE Label="la_tab_Sys_Config" Module="Core" Type="1">Q29uZmlndXJhdGlvbg==</PHRASE> <PHRASE Label="la_tab_taglibrary" Module="Core" Type="1">VGFnIGxpYnJhcnk=</PHRASE> <PHRASE Label="la_tab_Themes" Module="Core" Type="1">VGhlbWVz</PHRASE> <PHRASE Label="la_tab_Tools" Module="Core" Type="1">VG9vbHM=</PHRASE> <PHRASE Label="la_tab_Users" Module="Core" Type="1">VXNlcnM=</PHRASE> <PHRASE Label="la_tab_User_Groups" Module="Core" Type="1">R3JvdXBz</PHRASE> <PHRASE Label="la_tab_User_List" Module="Core" Type="1">VXNlcnM=</PHRASE> <PHRASE Label="la_tab_VisitorLog" Module="Core" Type="1">VmlzaXRvciBMb2c=</PHRASE> <PHRASE Label="la_tab_Visits" Module="Core" Type="1">VmlzaXRz</PHRASE> <PHRASE Label="la_Text" Module="Core" Type="1">dGV4dA==</PHRASE> <PHRASE Label="la_Text_Admin" Module="Core" Type="1">QWRtaW4=</PHRASE> <PHRASE Label="la_text_advanced" Module="Core" Type="1">QWR2YW5jZWQ=</PHRASE> <PHRASE Label="la_Text_All" Module="Core" Type="1">QWxs</PHRASE> <PHRASE Label="la_text_AutoRefresh" Module="Core" Type="1">QXV0by1SZWZyZXNo</PHRASE> <PHRASE Label="la_Text_BackupComplete" Module="Core" Type="1">QmFjayB1cCBoYXMgYmVlbiBjb21wbGV0ZWQuIFRoZSBiYWNrdXAgZmlsZSBpczo=</PHRASE> <PHRASE Label="la_Text_backup_access" Module="Core" Type="1">SW4tUG9ydGFsIGRvZXMgbm90IGhhdmUgYWNjZXNzIHRvIHdyaXRlIHRvIHRoaXMgZGlyZWN0b3J5</PHRASE> <PHRASE Label="la_Text_Backup_Info" Module="Core" Type="1">VGhpcyB1dGlsaXR5IGFsbG93cyB5b3UgdG8gYmFja3VwIHlvdXIgSW4tUG9ydGFsIGRhdGFiYXNlIHNvIGl0IGNhbiBiZSByZXN0b3JlZCBhdCBsYXRlciBpbiBuZWVkZWQu</PHRASE> <PHRASE Label="la_text_Bytes" Module="Core" Type="1">Ynl0ZXM=</PHRASE> <PHRASE Label="la_Text_Catalog" Module="Core" Type="1">Q2F0YWxvZw==</PHRASE> <PHRASE Label="la_Text_Categories" Module="Core" Type="1">U2VjdGlvbnM=</PHRASE> <PHRASE Label="la_Text_Category" Module="Core" Type="1">U2VjdGlvbg==</PHRASE> <PHRASE Label="la_text_ClearClipboardWarning" Module="Core" Type="1">WW91IGFyZSBhYm91dCB0byBjbGVhciBjbGlwYm9hcmQgY29udGVudCENClByZXNzIE9LIHRvIGNvbnRpbnVlIG9yIENhbmNlbCB0byByZXR1cm4gdG8gcHJldmlvdXMgc2NyZWVuLg==</PHRASE> <PHRASE Label="la_Text_CustomFields" Module="Core" Type="1">Q3VzdG9tIEZpZWxkcw==</PHRASE> <PHRASE Label="la_Text_DataType_1" Module="Core" Type="1">c2VjdGlvbnM=</PHRASE> <PHRASE Label="la_Text_Date_Time_Settings" Module="Core" Type="1">RGF0ZS9UaW1lIFNldHRpbmdz</PHRASE> <PHRASE Label="la_text_db_warning" Module="Core" Type="1">UnVubmluZyB0aGlzIHV0aWxpdHkgd2lsbCBhZmZlY3QgeW91ciBkYXRhYmFzZS4gUGxlYXNlIGJlIGFkdmlzZWQgdGhhdCB5b3UgY2FuIHVzZSB0aGlzIHV0aWxpdHkgYXQgeW91ciBvd24gcmlzay4gSW4tUG9ydGFsIG9yIGl0J3MgZGV2ZWxvcGVycyBjYW4gbm90IGJlIGhlbGQgbGlhYmxlIGZvciBhbnkgY29ycnVwdCBkYXRhIG9yIGRhdGEgbG9zcy4=</PHRASE> <PHRASE Label="la_Text_Default" Module="Core" Type="1">RGVmYXVsdA==</PHRASE> <PHRASE Label="la_Text_Delete" Module="Core" Type="1">RGVsZXRl</PHRASE> <PHRASE Label="la_Text_Disable" Module="Core" Type="1">RGlzYWJsZQ==</PHRASE> <PHRASE Label="la_text_disclaimer_part1" Module="Core" Type="1">UnVubmluZyB0aGlzIHV0aWxpdHkgd2lsbCBhZmZlY3QgeW91ciBkYXRhYmFzZS4gUGxlYXNlIGJlIGFkdmlzZWQgdGhhdCB5b3UgY2FuIHVzZSB0aGlzIHV0aWxpdHkgYXQgeW91ciBvd24gcmlzay4gSW4tUG9ydGFsIG9yIGl0J3MgZGV2ZWxvcGVycyBjYW4gbm90IGJlIGhlbGQgbGlhYmxlIGZvciBhbnkgY29ycnVwdCBkYXRhIG9yIGRhdGEgbG9zcy4=</PHRASE> <PHRASE Label="la_text_disclaimer_part2" Module="Core" Type="1">UGxlYXNlIG1ha2Ugc3VyZSB0byBCQUNLVVAgeW91ciBkYXRhYmFzZShzKSBiZWZvcmUgcnVubmluZyB0aGlzIHV0aWxpdHkh</PHRASE> <PHRASE Label="la_Text_Edit" Module="Core" Type="1">RWRpdA==</PHRASE> <PHRASE Label="la_Text_Email" Module="Core" Type="1">RW1haWw=</PHRASE> <PHRASE Label="la_text_FollowingLinesWereNotImported" Module="Core" Type="1">Rm9sbG93aW5nIGxpbmVzIHdlcmUgTk9UIGltcG9ydGVk</PHRASE> <PHRASE Label="la_Text_FrontOnly" Module="Core" Type="1">RnJvbnQtRW5kIE9ubHk=</PHRASE> <PHRASE Label="la_Text_General" Module="Core" Type="1">R2VuZXJhbA==</PHRASE> <PHRASE Label="la_Text_Hot" Module="Core" Type="1">SG90</PHRASE> <PHRASE Label="la_Text_IAgree" Module="Core" Type="1">SSBhZ3JlZSB0byB0aGUgdGVybXMgYW5kIGNvbmRpdGlvbnM=</PHRASE> <PHRASE Label="la_text_ImportResults" Module="Core" Type="1">SW1wb3J0IFJlc3VsdHM=</PHRASE> <PHRASE Label="la_Text_InDevelopment" Module="Core" Type="1">SW4gRGV2ZWxvcG1lbnQ=</PHRASE> <PHRASE Label="la_Text_Invert" Module="Core" Type="1">SW52ZXJ0</PHRASE> <PHRASE Label="la_text_keyword" Module="Core" Type="1">S2V5d29yZA==</PHRASE> <PHRASE Label="la_Text_Link" Module="Core" Type="1">TGluaw==</PHRASE> <PHRASE Label="la_Text_MetaInfo" Module="Core" Type="1">RGVmYXVsdCBNRVRBIGtleXdvcmRz</PHRASE> <PHRASE Label="la_text_min_password" Module="Core" Type="1">TWluaW11bSBwYXNzd29yZCBsZW5ndGg=</PHRASE> <PHRASE Label="la_text_min_username" Module="Core" Type="1">VXNlciBuYW1lIGxlbmd0aCAobWluIC0gbWF4KQ==</PHRASE> <PHRASE Label="la_text_multipleshow" Module="Core" Type="1">U2hvdyBtdWx0aXBsZQ==</PHRASE> <PHRASE Label="la_Text_New" Module="Core" Type="1">TmV3</PHRASE> <PHRASE Label="la_Text_None" Module="Core" Type="1">Tm9uZQ==</PHRASE> <PHRASE Label="la_text_NoPermission" Module="Core" Type="1">Tm8gUGVybWlzc2lvbg==</PHRASE> <PHRASE Label="la_text_Or" Module="Core" Type="1">b3I=</PHRASE> <PHRASE Label="la_Text_Phone" Module="Core" Type="1">UGhvbmU=</PHRASE> <PHRASE Label="la_Text_Pop" Module="Core" Type="1">UG9wdWxhcg==</PHRASE> <PHRASE Label="la_text_popularity" Module="Core" Type="1">UG9wdWxhcml0eQ==</PHRASE> <PHRASE Label="la_text_ready_to_install" Module="Core" Type="1">UmVhZHkgdG8gSW5zdGFsbA==</PHRASE> <PHRASE Label="la_text_RecordsAdded" Module="Core" Type="1">cmVjb3JkcyBhZGRlZA==</PHRASE> <PHRASE Label="la_text_RecordsUpdated" Module="Core" Type="1">cmVjb3JkcyB1cGRhdGVk</PHRASE> <PHRASE Label="la_text_RequiredFields" Module="Core" Type="1">UmVxdWlyZWQgZmllbGRz</PHRASE> <PHRASE Label="la_Text_Restore_Heading" Module="Core" Type="1">SGVyZSB5b3UgY2FuIHJlc3RvcmUgeW91ciBkYXRhYmFzZSBmcm9tIGEgcHJldmlvdXNseSBiYWNrZWQgdXAgc25hcHNob3QuIFJlc3RvcmluZyB5b3VyIGRhdGFiYXNlIHdpbGwgZGVsZXRlIGFsbCBvZiB5b3VyIGN1cnJlbnQgZGF0YSBhbmQgbG9nIHlvdSBvdXQgb2YgdGhlIHN5c3RlbS4=</PHRASE> <PHRASE Label="la_text_Review" Module="Core" Type="1">Q29tbWVudA==</PHRASE> <PHRASE Label="la_Text_Reviews" Module="Core" Type="1">Q29tbWVudHM=</PHRASE> <PHRASE Label="la_Text_RootCategory" Module="Core" Type="1">TW9kdWxlIFJvb3QgU2VjdGlvbg==</PHRASE> <PHRASE Label="la_text_Save" Module="Core" Type="1">U2F2ZQ==</PHRASE> <PHRASE Label="la_Text_Select" Module="Core" Type="1">U2VsZWN0</PHRASE> <PHRASE Label="la_text_sess_expired" Module="Core" Type="1">U2Vzc2lvbiBFeHBpcmVk</PHRASE> <PHRASE Label="la_Text_Simple" Module="Core" Type="1">U2ltcGxl</PHRASE> <PHRASE Label="la_Text_Sort" Module="Core" Type="1">U29ydA==</PHRASE> <PHRASE Label="la_Text_Unselect" Module="Core" Type="1">VW5zZWxlY3Q=</PHRASE> <PHRASE Label="la_Text_User" Module="Core" Type="1">VXNlcg==</PHRASE> <PHRASE Label="la_Text_Users" Module="Core" Type="1">VXNlcnM=</PHRASE> <PHRASE Label="la_Text_Version" Module="Core" Type="1">VmVyc2lvbg==</PHRASE> <PHRASE Label="la_Text_View" Module="Core" Type="1">Vmlldw==</PHRASE> <PHRASE Label="la_title_AddingAdministrator" Module="Core" Type="1">QWRkaW5nIEFkbWluaXN0cmF0b3I=</PHRASE> <PHRASE Label="la_title_AddingBanRule" Module="Core" Type="1">QWRkaW5nIEJhbiBSdWxl</PHRASE> <PHRASE Label="la_title_AddingCountryState" Module="Core" Type="1">QWRkaW5nIENvdW50cnkvU3RhdGU=</PHRASE> <PHRASE Label="la_title_addingCustom" Module="Core" Type="1">QWRkaW5nIEN1c3RvbSBGaWVsZA==</PHRASE> <PHRASE Label="la_title_AddingEmailTemplate" Module="Core" Type="1">QWRkaW5nIEUtbWFpbCBUZW1wbGF0ZQ==</PHRASE> <PHRASE Label="la_title_AddingFile" Module="Core" Type="1">QWRkaW5nIEZpbGU=</PHRASE> <PHRASE Label="la_title_AddingItemFilter" Module="Core" Type="1">QWRkaW5nIEl0ZW0gRmlsdGVy</PHRASE> <PHRASE Label="la_title_AddingMailingList" Module="Core" Type="1">QWRkaW5nIE1haWxpbmcgTGlzdA==</PHRASE> <PHRASE Label="la_title_AddingPermissionType" Module="Core" Type="1">QWRkaW5nIFBlcm1pc3Npb24gVHlwZQ==</PHRASE> <PHRASE Label="la_title_AddingPromoBlock" Module="Core" Type="1">QWRkaW5nIFByb21vIEJsb2Nr</PHRASE> <PHRASE Label="la_title_AddingPromoBlockGroup" Module="Core" Type="1">QWRkaW5nIFByb21vIEJsb2NrIEdyb3Vw</PHRASE> <PHRASE Label="la_title_AddingScheduledTask" Module="Core" Type="1">QWRkaW5nIFNjaGVkdWxlZCBUYXNr</PHRASE> <PHRASE Label="la_title_AddingSiteDomain" Module="Core" Type="1">QWRkaW5nIFNpdGUgRG9tYWlu</PHRASE> <PHRASE Label="la_title_AddingSkin" Module="Core" Type="1">QWRkaW5nIFNraW4=</PHRASE> <PHRASE Label="la_title_AddingSpellingDictionary" Module="Core" Type="1">QWRkaW5nIFNwZWxsaW5nIERpY3Rpb25hcnk=</PHRASE> <PHRASE Label="la_title_AddingStopWord" Module="Core" Type="1">QWRkaW5nIFN0b3AgV29yZA==</PHRASE> <PHRASE Label="la_title_AddingSystemEventSubscription" Module="Core" Type="1">QWRkaW5nIFN5c3RlbSBFdmVudCBTdWJzY3JpcHRpb24=</PHRASE> <PHRASE Label="la_title_AddingSystemSetting" Module="Core" Type="1">QWRkaW5nIFN5c3RlbSBTZXR0aW5n</PHRASE> <PHRASE Label="la_title_AddingThemeFile" Module="Core" Type="1">QWRkaW5nIFRoZW1lIFRlbXBsYXRl</PHRASE> <PHRASE Label="la_title_AddingThesaurus" Module="Core" Type="1">QWRkaW5nIFRoZXNhdXJ1cw==</PHRASE> <PHRASE Label="la_title_Adding_BaseStyle" Module="Core" Type="1">QWRkaW5nIEJhc2UgU3R5bGU=</PHRASE> <PHRASE Label="la_title_Adding_BlockStyle" Module="Core" Type="1">QWRkaW5nIEJsb2NrIFN0eWxl</PHRASE> <PHRASE Label="la_title_Adding_Category" Module="Core" Type="1">QWRkaW5nIFNlY3Rpb24=</PHRASE> <PHRASE Label="la_title_Adding_ConfigSearch" Module="Core" Type="1">QWRkaW5nIFNlYXJjaCBGaWVsZA==</PHRASE> <PHRASE Label="la_title_Adding_Content" Module="Core" Type="1">QWRkaW5nIENNUyBCbG9jaw==</PHRASE> <PHRASE Label="la_title_Adding_Form" Module="Core" Type="1">QWRkaW5nIEZvcm0=</PHRASE> <PHRASE Label="la_title_Adding_FormField" Module="Core" Type="1">QWRkaW5nIEZvcm0gRmllbGQ=</PHRASE> <PHRASE Label="la_title_Adding_Group" Module="Core" Type="1">QWRkaW5nIEdyb3Vw</PHRASE> <PHRASE Label="la_title_Adding_Image" Module="Core" Type="1">QWRkaW5nIEltYWdl</PHRASE> <PHRASE Label="la_title_Adding_Language" Module="Core" Type="1">QWRkaW5nIExhbmd1YWdl</PHRASE> <PHRASE Label="la_title_Adding_Phrase" Module="Core" Type="1">QWRkaW5nIFBocmFzZQ==</PHRASE> <PHRASE Label="la_title_Adding_RelatedSearch_Keyword" Module="Core" Type="1">QWRkaW5nIEtleXdvcmQ=</PHRASE> <PHRASE Label="la_title_Adding_Relationship" Module="Core" Type="1">QWRkaW5nIFJlbGF0aW9uc2hpcA==</PHRASE> <PHRASE Label="la_title_Adding_Review" Module="Core" Type="1">QWRkaW5nIENvbW1lbnQ=</PHRASE> <PHRASE Label="la_title_Adding_Theme" Module="Core" Type="1">QWRkaW5nIFRoZW1l</PHRASE> <PHRASE Label="la_title_Adding_User" Module="Core" Type="1">QWRkaW5nIFVzZXI=</PHRASE> <PHRASE Label="la_title_AdditionalPermissions" Module="Core" Type="1">QWRkaXRpb25hbCBQZXJtaXNzaW9ucw==</PHRASE> <PHRASE Label="la_title_Administrators" Module="Core" Type="1">QWRtaW5pc3RyYXRvcnM=</PHRASE> <PHRASE Label="la_title_Advanced" Module="Core" Type="1">QWR2YW5jZWQ=</PHRASE> <PHRASE Label="la_title_AdvancedView" Module="Core" Type="1">U2hvd2luZyBhbGwgcmVnYXJkbGVzcyBvZiBTdHJ1Y3R1cmU=</PHRASE> <PHRASE Label="la_title_BaseStyles" Module="Core" Type="1">QmFzZSBTdHlsZXM=</PHRASE> <PHRASE Label="la_title_BlockStyles" Module="Core" Type="1">QmxvY2sgU3R5bGVz</PHRASE> <PHRASE Label="la_title_BounceSettings" Module="Core" Type="1">Qm91bmNlIFBPUDMgU2VydmVyIFNldHRpbmdz</PHRASE> <PHRASE Label="la_title_Categories" Module="Core" Type="1">U2VjdGlvbnM=</PHRASE> <PHRASE Label="la_title_category_select" Module="Core" Type="1">U2VsZWN0IHNlY3Rpb24=</PHRASE> <PHRASE Label="la_title_ColumnPicker" Module="Core" Type="1">Q29sdW1uIFBpY2tlcg==</PHRASE> <PHRASE Label="la_title_Configuration" Module="Core" Type="1">Q29uZmlndXJhdGlvbg==</PHRASE> <PHRASE Label="la_Title_ContactInformation" Module="Core" Type="1">Q29udGFjdCBJbmZvcm1hdGlvbg==</PHRASE> <PHRASE Label="la_title_CountryStates" Module="Core" Type="1">Q291bnRyaWVzICYgU3RhdGVz</PHRASE> <PHRASE Label="la_title_CSVExport" Module="Core" Type="1">Q1NWIEV4cG9ydA==</PHRASE> <PHRASE Label="la_title_CSVImport" Module="Core" Type="1">Q1NWIEltcG9ydA==</PHRASE> <PHRASE Label="la_title_Custom" Module="Core" Type="1">Q3VzdG9t</PHRASE> <PHRASE Label="la_title_CustomFields" Module="Core" Type="1">Q3VzdG9tIEZpZWxkcw==</PHRASE> <PHRASE Label="la_title_Deployment" Module="Core" Type="1">RGVwbG95bWVudA==</PHRASE> <PHRASE Label="la_title_EditingAdministrator" Module="Core" Type="1">RWRpdGluZyBBZG1pbmlzdHJhdG9y</PHRASE> <PHRASE Label="la_title_EditingBanRule" Module="Core" Type="1">RWRpdGluZyBCYW4gUnVsZQ==</PHRASE> <PHRASE Label="la_title_EditingChangeLog" Module="Core" Type="1">RWRpdGluZyBDaGFuZ2VzIExvZw==</PHRASE> <PHRASE Label="la_title_EditingContentAutosaved" Module="Core" Type="1">Q29udGVudCBFZGl0b3IgLSBBdXRvLXNhdmVkIGF0ICVz</PHRASE> <PHRASE Label="la_title_EditingCountryState" Module="Core" Type="1">RWRpdGluZyBDb3VudHJ5L1N0YXRl</PHRASE> <PHRASE Label="la_title_EditingDraft" Module="Core" Type="1">RWRpdGluZyBEcmFmdCAoJTIkcyk=</PHRASE> <PHRASE Label="la_title_EditingEmailTemplate" Module="Core" Type="1">RWRpdGluZyBFLW1haWwgVGVtcGxhdGU=</PHRASE> <PHRASE Label="la_title_EditingFile" Module="Core" Type="1">RWRpdGluZyBGaWxl</PHRASE> <PHRASE Label="la_title_EditingItemFilter" Module="Core" Type="1">RWRpdGluZyBJdGVtIEZpbHRlcg==</PHRASE> <PHRASE Label="la_title_EditingMembership" Module="Core" Type="1">RWRpdGluZyBNZW1iZXJzaGlw</PHRASE> <PHRASE Label="la_title_EditingPermissionType" Module="Core" Type="1">RWRpdGluZyBQZXJtaXNzaW9uIFR5cGU=</PHRASE> <PHRASE Label="la_title_EditingPromoBlock" Module="Core" Type="1">RWRpdGluZyBQcm9tbyBCbG9jaw==</PHRASE> <PHRASE Label="la_title_EditingPromoBlockGroup" Module="Core" Type="1">RWRpdGluZyBQcm9tbyBCbG9jayBHcm91cA==</PHRASE> <PHRASE Label="la_title_EditingScheduledTask" Module="Core" Type="1">RWRpdGluZyBTY2hlZHVsZWQgVGFzaw==</PHRASE> <PHRASE Label="la_title_EditingSiteDomain" Module="Core" Type="1">RWRpdGluZyBTaXRlIERvbWFpbg==</PHRASE> <PHRASE Label="la_title_EditingSkin" Module="Core" Type="1">RWRpdGluZyBTa2lu</PHRASE> <PHRASE Label="la_title_EditingSpamReport" Module="Core" Type="1">RWRpdGluZyBTUEFNIFJlcG9ydA==</PHRASE> <PHRASE Label="la_title_EditingSpellingDictionary" Module="Core" Type="1">RWRpdGluZyBTcGVsbGluZyBEaWN0aW9uYXJ5</PHRASE> <PHRASE Label="la_title_EditingStopWord" Module="Core" Type="1">RWRpdGluZyBTdG9wIFdvcmQ=</PHRASE> <PHRASE Label="la_title_EditingStyle" Module="Core" Type="1">RWRpdGluZyBTdHlsZQ==</PHRASE> <PHRASE Label="la_title_EditingSystemEventSubscription" Module="Core" Type="1">RWRpdGluZyBTeXN0ZW0gRXZlbnQgU3Vic2NyaXB0aW9u</PHRASE> <PHRASE Label="la_title_EditingSystemSetting" Module="Core" Type="1">RWRpdGluZyBTeXN0ZW0gU2V0dGluZw==</PHRASE> <PHRASE Label="la_title_EditingThemeFile" Module="Core" Type="1">RWRpdGluZyBUaGVtZSBGaWxl</PHRASE> <PHRASE Label="la_title_EditingThesaurus" Module="Core" Type="1">RWRpdGluZyBUaGVzYXVydXM=</PHRASE> <PHRASE Label="la_title_EditingTranslation" Module="Core" Type="1">RWRpdGluZyBUcmFuc2xhdGlvbg==</PHRASE> <PHRASE Label="la_title_Editing_BaseStyle" Module="Core" Type="1">RWRpdGluZyBCYXNlIFN0eWxl</PHRASE> <PHRASE Label="la_title_Editing_BlockStyle" Module="Core" Type="1">RWRpdGluZyBCbG9jayBTdHlsZQ==</PHRASE> <PHRASE Label="la_title_Editing_Category" Module="Core" Type="1">RWRpdGluZyBTZWN0aW9u</PHRASE> <PHRASE Label="la_title_Editing_Content" Module="Core" Type="1">RWRpdGluZyBDTVMgQmxvY2s=</PHRASE> <PHRASE Label="la_title_Editing_CustomField" Module="Core" Type="1">RWRpdGluZyBDdXN0b20gRmllbGQ=</PHRASE> <PHRASE Label="la_title_Editing_Form" Module="Core" Type="1">RWRpdGluZyBGb3Jt</PHRASE> <PHRASE Label="la_title_Editing_FormField" Module="Core" Type="1">RWRpdGluZyBGb3JtIEZpZWxk</PHRASE> <PHRASE Label="la_title_Editing_Group" Module="Core" Type="1">RWRpdGluZyBHcm91cA==</PHRASE> <PHRASE Label="la_title_Editing_Image" Module="Core" Type="1">RWRpdGluZyBJbWFnZQ==</PHRASE> <PHRASE Label="la_title_Editing_Language" Module="Core" Type="1">RWRpdGluZyBMYW5ndWFnZQ==</PHRASE> <PHRASE Label="la_title_Editing_Phrase" Module="Core" Type="1">RWRpdGluZyBQaHJhc2U=</PHRASE> <PHRASE Label="la_title_Editing_RelatedSearch_Keyword" Module="Core" Type="1">RWRpdGluZyBLZXl3b3Jk</PHRASE> <PHRASE Label="la_title_Editing_Relationship" Module="Core" Type="1">RWRpdGluZyBSZWxhdGlvbnNoaXA=</PHRASE> <PHRASE Label="la_title_Editing_Review" Module="Core" Type="1">RWRpdGluZyBDb21tZW50</PHRASE> <PHRASE Label="la_title_Editing_Theme" Module="Core" Type="1">RWRpdGluZyBUaGVtZQ==</PHRASE> <PHRASE Label="la_title_Editing_User" Module="Core" Type="1">RWRpdGluZyBVc2Vy</PHRASE> <PHRASE Label="la_title_EmailCommunication" Module="Core" Type="1">RS1tYWlsIENvbW11bmljYXRpb24=</PHRASE> <PHRASE Label="la_title_EmailSettings" Module="Core" Type="1">RS1tYWlsIFNldHRpbmdz</PHRASE> <PHRASE Label="la_title_EmailTemplates" Module="Core" Type="1">RS1tYWlsIFRlbXBsYXRlcw==</PHRASE> <PHRASE Label="la_title_ExportLanguagePackResults" Module="Core" Type="1">RXhwb3J0IExhbmd1YWdlIFBhY2sgLSBSZXN1bHRz</PHRASE> <PHRASE Label="la_title_ExportLanguagePackStep1" Module="Core" Type="1">RXhwb3J0IExhbmd1YWdlIFBhY2sgLSBTdGVwMQ==</PHRASE> <PHRASE Label="la_title_Fields" Module="Core" Type="1">RmllbGRz</PHRASE> <PHRASE Label="la_title_Files" Module="Core" Type="1">RmlsZXM=</PHRASE> <PHRASE Label="la_title_Forms" Module="Core" Type="1">Rm9ybXM=</PHRASE> <PHRASE Label="la_title_FormSubmissions" Module="Core" Type="1">Rm9ybSBTdWJtaXNzaW9ucw==</PHRASE> <PHRASE Label="la_title_General" Module="Core" Type="1">R2VuZXJhbA==</PHRASE> <PHRASE Label="la_title_Groups" Module="Core" Type="1">R3JvdXBz</PHRASE> <PHRASE Label="la_title_Images" Module="Core" Type="1">SW1hZ2Vz</PHRASE> <PHRASE Label="la_title_InstallLanguagePackStep1" Module="Core" Type="1">SW5zdGFsbCBMYW5ndWFnZSBQYWNrIC0gU3RlcCAx</PHRASE> <PHRASE Label="la_title_InstallLanguagePackStep2" Module="Core" Type="1">SW5zdGFsbCBMYW5ndWFnZSBQYWNrIC0gU3RlcCAy</PHRASE> <PHRASE Label="la_title_ItemFilters" Module="Core" Type="1">SXRlbSBGaWx0ZXJz</PHRASE> <PHRASE Label="la_title_Items" Module="Core" Type="1">SXRlbXM=</PHRASE> <PHRASE Label="la_title_Labels" Module="Core" Type="1">TGFiZWxz</PHRASE> <PHRASE Label="la_title_LangManagement" Module="Core" Type="1">TGFuZy4gTWFuYWdlbWVudA==</PHRASE> <PHRASE Label="la_title_LanguagePacks" Module="Core" Type="1">TGFuZ3VhZ2UgUGFja3M=</PHRASE> <PHRASE Label="la_title_LanguagesManagement" Module="Core" Type="1">TGFuZ3VhZ2VzIE1hbmFnZW1lbnQ=</PHRASE> <PHRASE Label="la_title_Loading" Module="Core" Type="1">TG9hZGluZyAuLi4=</PHRASE> <PHRASE Label="la_title_LogOther" Module="Core" Type="1">T3RoZXI=</PHRASE> <PHRASE Label="la_title_LogRequest" Module="Core" Type="1">UmVxdWVzdA==</PHRASE> <PHRASE Label="la_title_LogSession" Module="Core" Type="1">U2Vzc2lvbg==</PHRASE> <PHRASE Label="la_title_LogSource" Module="Core" Type="1">U291cmNl</PHRASE> <PHRASE Label="la_title_MailingLists" Module="Core" Type="1">TWFpbGluZ3M=</PHRASE> <PHRASE Label="la_title_Messages" Module="Core" Type="1">TWVzc2FnZXM=</PHRASE> <PHRASE Label="la_title_ModuleDeploymentLog" Module="Core" Type="1">TW9kdWxlIERlcGxveW1lbnQgTG9n</PHRASE> <PHRASE Label="la_title_Module_Status" Module="Core" Type="1">TW9kdWxlcw==</PHRASE> <PHRASE Label="la_title_NewEmailTemplate" Module="Core" Type="1">TmV3IEUtbWFpbCBUZW1wbGF0ZQ==</PHRASE> <PHRASE Label="la_title_NewFile" Module="Core" Type="1">TmV3IEZpbGU=</PHRASE> <PHRASE Label="la_title_NewReply" Module="Core" Type="1">TmV3IFJlcGx5</PHRASE> <PHRASE Label="la_title_NewScheduledTask" Module="Core" Type="1">TmV3IFNjaGVkdWxlZCBUYXNr</PHRASE> <PHRASE Label="la_title_NewTheme" Module="Core" Type="1">TmV3IFRoZW1l</PHRASE> <PHRASE Label="la_title_NewThemeFile" Module="Core" Type="1">TmV3IFRoZW1lIFRlbXBsYXRl</PHRASE> <PHRASE Label="la_title_New_BaseStyle" Module="Core" Type="1">TmV3IEJhc2UgU3R5bGU=</PHRASE> <PHRASE Label="la_title_New_BlockStyle" Module="Core" Type="1">TmV3IEJsb2NrIFN0eWxl</PHRASE> <PHRASE Label="la_title_New_Category" Module="Core" Type="1">TmV3IFNlY3Rpb24=</PHRASE> <PHRASE Label="la_title_New_ConfigSearch" Module="Core" Type="1">TmV3IEZpZWxk</PHRASE> <PHRASE Label="la_title_New_Image" Module="Core" Type="1">TmV3IEltYWdl</PHRASE> <PHRASE Label="la_title_New_Relationship" Module="Core" Type="1">TmV3IFJlbGF0aW9uc2hpcA==</PHRASE> <PHRASE Label="la_title_New_Review" Module="Core" Type="1">TmV3IENvbW1lbnQ=</PHRASE> <PHRASE Label="la_title_NoPermissions" Module="Core" Type="1">Tm8gUGVybWlzc2lvbnM=</PHRASE> <PHRASE Label="la_title_Permissions" Module="Core" Type="1">UGVybWlzc2lvbnM=</PHRASE> <PHRASE Label="la_title_Phrases" Module="Core" Type="1">TGFiZWxzICYgUGhyYXNlcw==</PHRASE> <PHRASE Label="la_Title_PleaseWait" Module="Core" Type="1">UGxlYXNlIFdhaXQ=</PHRASE> <PHRASE Label="la_title_PromoBlockGroups" Module="Core" Type="1">UHJvbW8gQmxvY2sgR3JvdXBz</PHRASE> <PHRASE Label="la_title_PromoBlocks" Module="Core" Type="1">UHJvbW8gQmxvY2tz</PHRASE> <PHRASE Label="la_title_Properties" Module="Core" Type="1">UHJvcGVydGllcw==</PHRASE> <PHRASE Label="la_title_RelatedSearches" Module="Core" Type="1">UmVsYXRlZCBTZWFyY2hlcw==</PHRASE> <PHRASE Label="la_title_Relations" Module="Core" Type="1">UmVsYXRpb25z</PHRASE> <PHRASE Label="la_title_ReplySettings" Module="Core" Type="1">UmVwbHkgUE9QMyBTZXJ2ZXIgU2V0dGluZ3M=</PHRASE> <PHRASE Label="la_title_Reviews" Module="Core" Type="1">Q29tbWVudHM=</PHRASE> <PHRASE Label="la_title_RunSchedule" Module="Core" Type="1">UnVuIFNjaGVkdWxl</PHRASE> <PHRASE Label="la_title_RunSettings" Module="Core" Type="1">UnVuIFNldHRpbmdz</PHRASE> <PHRASE Label="la_title_ScheduledTasks" Module="Core" Type="1">U2NoZWR1bGVkIFRhc2tz</PHRASE> <PHRASE Label="la_title_SelectGroup" Module="Core" Type="1">U2VsZWN0IEdyb3VwKHMp</PHRASE> <PHRASE Label="la_title_SelectUser" Module="Core" Type="1">U2VsZWN0IFVzZXI=</PHRASE> <PHRASE Label="LA_TITLE_SENDEMAIL" Module="Core" Type="1">U2VuZCBFLW1haWw=</PHRASE> <PHRASE Label="LA_TITLE_SENDINGPREPAREDEMAILS" Module="Core" Type="1">U2VuZGluZyBQcmVwYXJlZCBFLW1haWxz</PHRASE> <PHRASE Label="la_Title_SendMailComplete" Module="Core" Type="1">TWFpbCBoYXMgYmVlbiBzZW50IFN1Y2Nlc3NmdWxseQ==</PHRASE> <PHRASE Label="la_title_SiteDomains" Module="Core" Type="1">U2l0ZSBEb21haW5z</PHRASE> <PHRASE Label="la_title_SpamReports" Module="Core" Type="1">U1BBTSBSZXBvcnRz</PHRASE> <PHRASE Label="la_title_SpellingDictionary" Module="Core" Type="1">U3BlbGxpbmcgRGljdGlvbmFyeQ==</PHRASE> <PHRASE Label="la_title_StopWords" Module="Core" Type="1">U3RvcCBXb3Jkcw==</PHRASE> <PHRASE Label="la_title_Structure" Module="Core" Type="1">U3RydWN0dXJlICYgRGF0YQ==</PHRASE> <PHRASE Label="la_title_SystemCF" Module="Core" Type="1">U3lzdGVtIEN1c3RvbSBGaWVsZHM=</PHRASE> <PHRASE Label="la_title_SystemEventSubscriptions" Module="Core" Type="1">VXNlciBTdWJzY3JpcHRpb25z</PHRASE> <PHRASE Label="la_title_SystemTools" Module="Core" Type="1">U3lzdGVtIFRvb2xz</PHRASE> <PHRASE Label="la_title_SystemToolsClearTemplatesCache" Module="Core" Type="1" Hint="PHVsPg0KICA8bGk+RGVsZXRlcyBhbGwgY29tcGlsZWQgdGVtcGxhdGVzIGZyb20gQWRtaW4gQ29uc29sZSBhbmQgRnJvbnQtZW5kIHRoZW1lcy48L2xpPg0KICA8bGk+UmVjb21tZW5kZWQgZm9yIHRoZSBtYWludGVuYW5jZSBwdXJwb3Nlcywgc2luY2UgZG9lcyBub3QgcHJvdmlkZSBhbnkgYWR2YW50YWdlcyBleGNlcHQgZm9yIHRlbXBvcmFyeSBsb3dlcmluZyB1c2FnZSBvZiB0aGUgZGlzayBzcGFjZS4gQWxsIHRlbXBsYXRlcyB3aWxsIGJlIGF1dG9tYXRpY2FsbHkgcmVjb21waWxlZCBhdCB0aGUgdGltZSBvZiB2aXNpdC48L2xpPg0KPC91bD4=">Q2xlYXIgVGVtcGxhdGVzIENhY2hl</PHRASE> <PHRASE Label="la_title_SystemToolsCommonlyUsedKeys" Module="Core" Type="1">Q29tbW9ubHkgVXNlZCBLZXlz</PHRASE> <PHRASE Label="la_title_SystemToolsDeploy" Module="Core" Type="1" Hint="PHVsPg0KPGxpPlRoaXMgZGVwbG95IHNjcmlwdCB3aWxsIGFwcGx5IGFsbCBEYXRhYmFzZSBDaGFuZ2VzIHN0b3JlZCBpbiA8Yj48dT5bbW9kdWxlXS9wcm9qZWN0X3VwZ3JhZGVzLnNxbDwvdT48L2I+IHRvIHRoZSBjdXJyZW50IHdlYnNpdGUgYW5kIHNhdmUgYXBwbGllZCBSZXZpc2lvbnMgaW4gQXBwbGllZERCUmV2aXNpb25zLjwvbGk+DQo8bGk+VGhpcyBkZXBsb3kgc2NyaXB0IHdpbGwgY3JlYXRlIGFsbCBuZXcgbGFuZ3VhZ2UgcGhyYXNlcyBieSByZS1pbXBvcnRpbmcgPGI+PHU+W21vZHVsZV0vaW5zdGFsbC9lbmdsaXNoLmxhbmc8L3U+PC9iPiBmaWxlLjwvbGk+DQo8bGk+VGhpcyBkZXBsb3kgc2NyaXB0IHdpbGwgcmVzZXQgYWxsIGNhY2hlcyBhdCBvbmNlLjwvbGk+DQo8L3VsPg==">RGVwbG95IENoYW5nZXM=</PHRASE> <PHRASE Label="la_title_SystemToolsKeyName" Module="Core" Type="1" Hint="PHVsPg0KICA8bGk+TmFtZSBvZiB0aGUgS2V5IHVzZWQgdG8gZ2V0IG9yIHNldCB0aGUgZGF0YSAodmFsdWUpIGluIHRoZSBtZW1vcnkgY2FjaGUgKDxzdHJvbmc+PGk+a0FwcGxpY2F0aW9uOjpzZXRDYWNoZTwvaT48L3N0cm9uZz4gYW5kIDxzdHJvbmc+PGk+a0FwcGxpY2F0aW9uOjpnZXRDYWNoZTwvaT48L3N0cm9uZz4gbWV0aG9kcykuPC9saT4NCjwvdWw+">S2V5IE5hbWU=</PHRASE> <PHRASE Label="la_title_SystemToolsKeyValue" Module="Core" Type="1" Hint="Q3VycmVudCB2YWx1ZSBvciBhIG5ldyB2YWx1ZSAoZm9yIHNldHRpbmcpIG9mIHRoZSBrZXkgbmFtZSBzcGVjaWZpZWQgYWJvdmUu">S2V5IFZhbHVl</PHRASE> <PHRASE Label="la_title_SystemToolsLocateUnitConfigFile" Module="Core" Type="1" Hint="PHVsPg0KICA8bGk+U2hvd3MgdGhlIGxvY2F0aW9uIG9mIDxzdHJvbmc+PGk+VW5pdCBDb25maWc8L2k+PC9zdHJvbmc+IGZpbGUsIGFzc29jaWF0ZWQgd2l0aCB0aGUgZ2l2ZW4gPHN0cm9uZz48aT5Vbml0IENvbmZpZyBQcmVmaXg8L2k+PC9zdHJvbmc+IChpZS4gImFkbSIsICJ1IiwgImxhbmciIGFuZCBvdGhlcnMpLjwvbGk+DQo8L3VsPg==">TG9jYXRlIFVuaXQgQ29uZmlnIEZpbGU=</PHRASE> <PHRASE Label="la_title_SystemToolsRebuildMultilingualFields" Module="Core" Type="1" Hint="PHVsPg0KICA8bGk+U2NhbnMgYW5kIGFkZHMgbWlzc2luZyBkYXRhYmFzZSB0YWJsZSBjb2x1bW5zIChmb3JtYXQgPHN0cm9uZz48aT4ibCZsdDtOJmd0O19GaWVsZE5hbWUiPC9pPjwvc3Ryb25nPiwgd2hlcmUgTiBpcyBhIExhbmd1YWdlSWQpIHRvIHN0b3JlIHRoZSBkYXRhIGZvciB0cmFuc2xhdGFibGUgZmllbGRzLiBUaGlzIGFjdGlvbiBpcyBwZXJmb3JtZWQgYXV0b21hdGljYWxseSB3aGVuZXZlciBhIG5ldyBMYW5ndWFnZSBpcyBjcmVhdGVkIHZpYSBBZG1pbiBDb25zb2xlLjwvbGk+DQogIDxsaT5Vc2UgdGhpcyAiUmVidWlsZCIgb3B0aW9uIG9ubHkgZm9yIHN5bmNocm9uaXphdGlvbiBvZiBkYXRhYmFzZSB0YWJsZSBjb2x1bW5zIHdpdGggbmV3bHkgYWRkZWQgbXVsdGlsaW5ndWFsIGZpZWxkcyAoa011bHRpTGFuZ3VhZ2UgZm9ybWF0dGVyKSBkZWZpbmVkIHRocm91Z2ggPHN0cm9uZz48aT5Vbml0IENvbmZpZzwvaT48L3N0cm9uZz4gZmlsZXMuPC9saT4NCjwvdWw+">UmVidWlsZCBNdWx0aWxpbmd1YWwgRmllbGRz</PHRASE> <PHRASE Label="la_title_SystemToolsRecompileTemplates" Module="Core" Type="1" Hint="PHVsPg0KICA8bGk+Q29tcGxldGVseSByZWNvbXBpbGVzIHRoZSB0ZW1wbGF0ZXMgZm9yIGFsbCBlbmFibGVkIEZyb250LWVuZCB0aGVtZXMgYXMgd2VsbCBhcyBBZG1pbiBDb25zb2xlIHRlbXBsYXRlcyBmb3IgYWxsIGxvYWRlZCBtb2R1bGVzLjwvbGk+DQogIDxsaT5BZGRpdGlvbmFsbHksIGNoZWNrcyBmb3IgdGhlIHN5bnRheCBvZiBhbGwgPHN0cm9uZz48aT4mbHQ7aW5wMjouLi4vJmd0OzwvaT48L3N0cm9uZz4gdGFncyBhY3Jvc3MgdGhlIEluLVBvcnRhbCBpbnN0YWxsYXRpb24uPC9saT4NCiAgPGxpPlRoaXMgYWN0aW9uIGlzIG5ldmVyIHBlcmZvcm1lZCBhdXRvbWF0aWNhbGx5LiBIb3dldmVyLCBhbGwgbmV3bHkgbW9kaWZpZWQgdGVtcGxhdGVzIHdpbGwgYmUgYXV0b21hdGljYWxseSByZWNvbXBpbGVkIGJ5IHRoZSBzeXN0ZW0gYXQgdGhlIHRpbWUgb2YgdmlzaXQuPC9saT4NCjwvdWw+">UmVjb21waWxlIFRlbXBsYXRlcw==</PHRASE> <PHRASE Label="la_title_SystemToolsRefreshThemeFiles" Module="Core" Type="1" Hint="PHVsPg0KICA8bGk+U2NhbnMgZm9yIG5ld2x5IGFkZGVkIEZyb250LWVuZCBUaGVtZSB0ZW1wbGF0ZXMgYWNyb3NzIGFsbCA8c3Ryb25nPjxpPmVuYWJsZWQ8L2k+PC9zdHJvbmc+IHRoZW1lcy4gVGhpcyBhY3Rpb24gaXMgcGVyZm9ybWVkIGF1dG9tYXRpY2FsbHkgd2hlbiBhIG5ldyB0aGVtZSBpcyBhZGRlZCBvciBleGlzdGluZyB0aGVtZSBpcyBlbmFibGVkLjwvbGk+DQogIDxsaT5BZGRpdGlvbmFsbHksIGRlbGV0ZXMgYWxsIGNvbXByZXNzZWQgYW5kIGNhY2hlZCBKYXZhc2NyaXB0L0NTUyBmaWxlcyAoLmpzIC5jc3MpIGxvYWRlZCB1c2luZyA8c3Ryb25nPjxpPiZsdDtpbnAyOm1fQ29tcHJlc3MgLi4uLyZndDs8L2k+PC9zdHJvbmc+IHRhZy48L2xpPg0KICA8bGk+VGhpcyBmdW5jdGlvbiBpcyBhbHNvIGF2YWlsYWJsZSBhcyBhICJSZWZyZXNoIiBidXR0b24gaW4gdGhlIFRoZW1lcyBzZWN0aW9uIHRvb2xiYXIgaW4gQWRtaW4gQ29uc29sZS48L2xpPg0KICA8bGk+VGhpcyBvcHRpb24gc2hvdWxkIGJlIHVzZWQgaW4gY2FzZSB3aGVuICI0MDQgTm90IEZvdW5kIiBwYWdlIGlzIHNob3duIGluc3RlYWQgb2YgZXhwZWN0ZWQgbmV3bHkgYWRkZWQgcGFnZSBvciB0ZW1wbGF0ZS48L2xpPg0KPC91bD4=">UmVmcmVzaCBUaGVtZSBGaWxlcw==</PHRASE> <PHRASE Label="la_title_SystemToolsResetAdminConsoleSections" Module="Core" Type="1" Hint="PHVsPg0KICA8bGk+UmVzZXRzIHRoZSBjYWNoZSBvZiBBZG1pbiBDb25zb2xlIHNlY3Rpb25zIChsZWZ0IG1lbnUpLiBUaGUgZGVmaW5pdGlvbnMgb2Ygc2VjdGlvbnMgYXJlIHJlYWQgYW5kIGNvbGxlY3RlZCBmcm9tIDxzdHJvbmc+PGk+VW5pdCBDb25maWc8L2k+PC9zdHJvbmc+IGZpbGVzIHRoYXQgYWxyZWFkeSBiZWVuIHNjYW5uZWQgYW5kIGNhY2hlZCBieSB0aGUgc3lzdGVtLjwvbGk+DQogIDxsaT5Vc2UgdGhpcyByZXNldCBvcHRpb24gaWYgYSBuZXdseSBhZGRlZCBzZWN0aW9uIGRvZXNuJ3QgYXBwZWFyIGluIHRoZSBsZWZ0IEFkbWluIENvbnNvbGUgbWVudS48L2xpPg0KPC91bD4=">UmVzZXQgQWRtaW4gQ29uc29sZSBTZWN0aW9ucw==</PHRASE> <PHRASE Label="la_title_SystemToolsResetAllKeys" Module="Core" Type="1" Hint="PHVsPg0KICA8bGk+UmVzZXRzIDxzdHJvbmc+PGk+QWxsIERhdGE8L2k+PC9zdHJvbmc+IHN0b3JlZCBpbiB0aGUgTWVtb3J5IENhY2hlLCBpbmNsdWRpbmcgYnV0IG5vdCBsaW1pdGVkIHRvIFN5c3RlbSBEYXRhIGFuZCBEYXRhYmFzZSBJdGVtcy48L2xpPg0KICA8bGk+VXNlIHdpdGggY2F1dGlvbiBkdWUgdG8gcG9zc2liaWxpdHkgb2YgbG9uZyBleGVjdXRpb24gdGltZS48L2xpPg0KPC91bD4=">UmVzZXQgQWxsIEtleXM=</PHRASE> <PHRASE Label="la_title_SystemToolsResetConfigsAndParsedData" Module="Core" Type="1" Hint="PHVsPg0KICA8bGk+U2NhbnMgPHN0cm9uZz48aT4iY29yZSI8L2k+PC9zdHJvbmc+IGFuZCA8c3Ryb25nPjxpPiJtb2R1bGVzIjwvaT48L3N0cm9uZz4gZm9sZGVycyB0byBjYWNoZSB0aGUgbG9jYXRpb24gb2YgYWxsIDxzdHJvbmc+PGk+VW5pdCBDb25maWc8L2k+PC9zdHJvbmc+IGZpbGVzLiBUaGUgZXhlY3V0aW9uIHRpbWUgZGVwZW5kcyBvbiB0aGUgbnVtYmVyIG9mIDxzdHJvbmc+PGk+VW5pdCBDb25maWc8L2k+PC9zdHJvbmc+IGZpbGVzIGZvdW5kLjwvbGk+DQogIDxsaT5SZXNldHMgdmFyaW91cyBjYWNoZWQgc3lzdGVtIGRhdGEgc3VjaCBhcyBkZWZpbmVkIFBIUCBDbGFzc2VzIChtYXBwaW5nIGJldHdlZW4gdGhlIGNsYXNzIG5hbWUgYW5kIHBoeXNpY2FsIGZpbGVuYW1lIGFuZCBsb2NhdGlvbiBvZiB0aGUgY2xhc3MpLCBIb29rcywgU2NoZWR1bGVkIFRhc2tzLCBDYWNoZWQgQ29uZmlndXJhdGlvbiBWYXJpYWJsZXMsIFJlcGxhY2VtZW50IFRlbXBsYXRlcywgUmV3cml0ZSBMaXN0ZW5lcnMgYW5kIExvYWRlZCBNb2R1bGVzLiBEYXRhIGlzIHJlYWQgYW5kIGNvbGxlY3RlZCBmcm9tIDxzdHJvbmc+PGk+VW5pdCBDb25maWc8L2k+PC9zdHJvbmc+IGZpbGVzIHRoYXQgYWxyZWFkeSBiZWVuIHNjYW5uZWQgYW5kIGNhY2hlZCBieSB0aGUgc3lzdGVtLjwvbGk+DQogIDxsaT5EZWxldGVzIGNvbXBpbGVkIHNraW5zIGZvciBBZG1pbiBDb25zb2xlIChjc3MgZmlsZXMpLjwvbGk+DQo8L3VsPg==">UmVzZXQgQ29uZmlncyBGaWxlcyBDYWNoZSBhbmQgUGFyc2VkIFN5c3RlbSBEYXRh</PHRASE> <PHRASE Label="la_title_SystemToolsResetModRewriteCache" Module="Core" Type="1" Hint="PHVsPg0KICA8bGk+RGVsZXRlcyB0aGUgbWFwcGluZyBiZXR3ZWVuIHRoZSBGcm9udC1lbmQgVVJMcyBhbmQgYWN0dWFsIFRoZW1lIFRlbXBsYXRlcy4gVGhpcyBtYXBwaW5nIGlzIHVwZGF0ZWQgYXV0b21hdGljYWxseSwgd2hlbiB0aGUgd2Vic2l0ZSBTdHJ1Y3R1cmUgb3IgU2VjdGlvbnMgYXJlIGNoYW5nZWQuPC9saT4NCiAgPGxpPlVzZSB0aGlzIG9wdGlvbiBvbmx5IGluIGNhc2UgaWYgTW9kUmV3cml0ZSBtb2RlIGlzIGVuYWJsZWQgYW5kIGRpc3BsYXllZCBwYWdlIGRpZmZlcnMgZnJvbSB0aGUgcGFnZSB0aGF0IGl0IHNob3VsZCBiZSwgd2hlbiBnaXZlbiBVUkwgaXMgdmlzaXRlZC48L2xpPg0KPC91bD4=">UmVzZXQgTW9kUmV3cml0ZSBDYWNoZQ==</PHRASE> <PHRASE Label="la_title_SystemToolsResetParsedCachedData" Module="Core" Type="1" Hint="PHVsPg0KICA8bGk+UmVzZXRzIHZhcmlvdXMgY2FjaGVkIHN5c3RlbSBkYXRhIHN1Y2ggYXMgZGVmaW5lZCBQSFAgQ2xhc3NlcyAobWFwcGluZyBiZXR3ZWVuIHRoZSBjbGFzcyBuYW1lIGFuZCBwaHlzaWNhbCBmaWxlbmFtZSBhbmQgbG9jYXRpb24gb2YgdGhlIGNsYXNzKSwgSG9va3MsIFNjaGVkdWxlZCBUYXNrcywgQ2FjaGVkIENvbmZpZ3VyYXRpb24gVmFyaWFibGVzLCBSZXBsYWNlbWVudCBUZW1wbGF0ZXMsIFJld3JpdGUgTGlzdGVuZXJzIGFuZCBMb2FkZWQgTW9kdWxlcy4gRGF0YSBpcyByZWFkIGFuZCBjb2xsZWN0ZWQgZnJvbSA8c3Ryb25nPjxpPlVuaXQgQ29uZmlnPC9pPjwvc3Ryb25nPiBmaWxlcyB0aGF0IGFscmVhZHkgYmVlbiBzY2FubmVkIGFuZCBjYWNoZWQgYnkgdGhlIHN5c3RlbS48L2xpPg0KPC91bD4=">UmVzZXQgUGFyc2VkIGFuZCBDYWNoZWQgU3lzdGVtIERhdGE=</PHRASE> <PHRASE Label="la_title_SystemToolsResetSMSMenuCache" Module="Core" Type="1" Hint="PHVsPg0KICA8bGk+RGVsZXRlcyB0aGUgY2FjaGVkIHZlcnNpb24gb2YgRnJvbnQtZW5kIG1lbnUgKGRpc3BsYXllZCB2aWEgPHN0cm9uZz48aT4mbHQ7aW5wMjpzdF9DYWNoZWRNZW51IC4uLi8mZ3Q7PC9pPjwvc3Ryb25nPiB0YWcpLiBUaGlzIGNhY2hlIGlzIHVwZGF0ZWQgYXV0b21hdGljYWxseSwgd2hlbiB0aGUgd2Vic2l0ZSBzdHJ1Y3R1cmUgb3Igc2VjdGlvbnMgYXJlIGNoYW5nZWQuPC9saT4NCiAgPGxpPlVzZSB0aGlzIG9wdGlvbiBvbmx5IGluIGNhc2UgaWYgZGlzcGxheWVkIG1lbnUgb24gdGhlIEZyb250LWVuZCBkb2Vzbid0IG1hdGNoIHRoZSBtZW51IGRlZmluZWQgaW4gQWRtaW4gQ29uc29sZS48L2xpPg0KPC91bD4=">UmVzZXQgU01TIE1lbnUgQ2FjaGU=</PHRASE> <PHRASE Label="la_title_SystemToolsShowDatabaseTableStructure" Module="Core" Type="1" Hint="PHVsPg0KICA8bGk+U2hvd3MgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YWJhc2UgdGFibGUgbG9hZGluZyBpdCBieSB0aGUgVGFibGUgTmFtZSAodGFibGUgcHJlZml4IGlzIG9wdGlvbmFsKSBvciA8c3Ryb25nPjxpPlVuaXQgQ29uZmlnIFByZWZpeDwvaT48L3N0cm9uZz4gYXNzb2NpYXRlZCB3aXRoIHRoaXMgdGFibGUuPC9saT4NCjwvdWw+">U2hvdyBEYXRhYmFzZSBUYWJsZSBTdHJ1Y3R1cmU=</PHRASE> <PHRASE Label="la_title_SystemToolsSynchronizeDBRevisions" Module="Core" Type="1" Hint="PHVsPg0KPGxpPkFzIGEgcmVzdWx0LCBzY3JpcHQgd2lsbCBWYWxpZGF0ZSBjdXJyZW50IDxiPjx1PnByb2plY3RfdXBncmFkZXMuc3FsPC91PjwvYj4gZmlsZSBhbmQgb3V0bGluZSBhbnkgZXJyb3JzIG9yIGluY29uc2lzdGVuY2llcywgYW5kIGF1dG8tcG9wdWxhdGUgYWxsIG1pc3NpbmcgREIgUmV2aXNpb25zIGZyb20gdGhlIGZpbGUgaW50byBBcHBsaWVkREJSZXZpc2lvbnMuPC9saT4NCjxsaT48YiBzdHlsZT0iY29sb3I6cmVkIj5OT1RFOjwvYj4gRGV2ZWxvcGVycyBzaG91bGQgT05MWSBydW4gdGhpcyBiZWZvcmUgdGhleSBwZXJmb3JtIFJlcG9zaXRvcnkgVXBkYXRlcyBvbiB0aGVpIHdvcmtpbmcgY29weSE8L2xpPg0KPC91bD4=">U3luY2hyb25pemUgRGF0YWJhc2UgUmV2aXNpb25z</PHRASE> <PHRASE Label="la_title_ThemeFiles" Module="Core" Type="1">VGhlbWUgRmlsZXM=</PHRASE> <PHRASE Label="la_title_Thesaurus" Module="Core" Type="1">VGhlc2F1cnVz</PHRASE> <PHRASE Label="la_title_UpdatingCategories" Module="Core" Type="1">VXBkYXRpbmcgU2VjdGlvbnM=</PHRASE> <PHRASE Label="la_title_Users" Module="Core" Type="1">VXNlcnM=</PHRASE> <PHRASE Label="la_title_ViewingEmailLog" Module="Core" Type="1">Vmlld2luZyBFbWFpbCBMb2c=</PHRASE> <PHRASE Label="la_title_ViewingFormSubmission" Module="Core" Type="1">Vmlld2luZyBmb3JtIHN1Ym1pc3Npb24=</PHRASE> <PHRASE Label="la_title_ViewingMailingList" Module="Core" Type="1">Vmlld2luZyBNYWlsaW5nIExpc3Q=</PHRASE> <PHRASE Label="la_title_ViewingModuleDeploymentLog" Module="Core" Type="1">Vmlld2luZyBNb2R1bGUgRGVwbG95bWVudCBMb2c=</PHRASE> <PHRASE Label="la_title_ViewingReply" Module="Core" Type="1">Vmlld2luZyBSZXBseQ==</PHRASE> <PHRASE Label="la_title_ViewingRevision" Module="Core" Type="1">Vmlld2luZyBSZXZpc2lvbiAjJXMgKCVzKQ==</PHRASE> <PHRASE Label="la_title_ViewingSystemLog" Module="Core" Type="1">Vmlld2luZyBTeXN0ZW0gTG9n</PHRASE> <PHRASE Label="la_title_Visits" Module="Core" Type="1">VmlzaXRz</PHRASE> <PHRASE Label="la_title_Website" Module="Core" Type="1">V2Vic2l0ZQ==</PHRASE> <PHRASE Label="la_To" Module="Core" Type="1">dG8=</PHRASE> <PHRASE Label="la_ToolTipShort_Edit_Current_Category" Module="Core" Type="1">Q3Vyci4gU2VjdGlvbg==</PHRASE> <PHRASE Label="la_ToolTipShort_Move_Down" Module="Core" Type="1">RG93bg==</PHRASE> <PHRASE Label="la_ToolTipShort_Move_Up" Module="Core" Type="1">VXA=</PHRASE> <PHRASE Label="la_ToolTip_Add" Module="Core" Type="1">QWRk</PHRASE> <PHRASE Label="la_ToolTip_AddToGroup" Module="Core" Type="1">QWRkIFVzZXIgdG8gR3JvdXA=</PHRASE> <PHRASE Label="la_ToolTip_AddUserToGroup" Module="Core" Type="1">QWRkIFVzZXIgVG8gR3JvdXA=</PHRASE> <PHRASE Label="la_ToolTip_Approve" Module="Core" Type="1">QXBwcm92ZQ==</PHRASE> <PHRASE Label="la_ToolTip_Back" Module="Core" Type="1">QmFjaw==</PHRASE> <PHRASE Label="la_ToolTip_cancel" Module="Core" Type="1">Q2FuY2Vs</PHRASE> <PHRASE Label="la_ToolTip_ClearClipboard" Module="Core" Type="1">Q2xlYXIgQ2xpcGJvYXJk</PHRASE> <PHRASE Label="la_ToolTip_Clone" Module="Core" Type="1">Q2xvbmU=</PHRASE> <PHRASE Label="la_ToolTip_CloneUser" Module="Core" Type="1">Q2xvbmUgVXNlcnM=</PHRASE> <PHRASE Label="la_ToolTip_close" Module="Core" Type="1">Q2xvc2U=</PHRASE> <PHRASE Label="la_ToolTip_Copy" Module="Core" Type="1">Q29weQ==</PHRASE> <PHRASE Label="la_ToolTip_Cut" Module="Core" Type="1">Q3V0</PHRASE> <PHRASE Label="la_ToolTip_Decline" Module="Core" Type="1">RGVjbGluZQ==</PHRASE> <PHRASE Label="la_ToolTip_Delete" Module="Core" Type="1">RGVsZXRl</PHRASE> <PHRASE Label="la_ToolTip_DeleteAll" Module="Core" Type="1">RGVsZXRlIEFsbA==</PHRASE> <PHRASE Label="la_ToolTip_DeleteReview" Module="Core" Type="1">RGVsZXRlIFJldmlldw==</PHRASE> <PHRASE Label="la_ToolTip_DeleteSpamReportOnly" Module="Core" Type="1">RGVsZXRlIFJlcG9ydCBPbmx5</PHRASE> <PHRASE Label="la_ToolTip_Deny" Module="Core" Type="1">RGVueQ==</PHRASE> <PHRASE Label="la_ToolTip_Details" Module="Core" Type="1">RGV0YWlscw==</PHRASE> <PHRASE Label="la_ToolTip_Disable" Module="Core" Type="1">RGlzYWJsZQ==</PHRASE> <PHRASE Label="la_ToolTip_Discard" Module="Core" Type="1">RGlzY2FyZA==</PHRASE> <PHRASE Label="la_ToolTip_Edit" Module="Core" Type="1">RWRpdA==</PHRASE> <PHRASE Label="la_ToolTip_Edit_Current_Category" Module="Core" Type="1">RWRpdCBDdXJyZW50IFNlY3Rpb24=</PHRASE> <PHRASE Label="la_ToolTip_Email_FrontOnly" Module="Core" Type="1">RnJvbnQtRW5kIE9ubHk=</PHRASE> <PHRASE Label="la_ToolTip_Enable" Module="Core" Type="1">RW5hYmxl</PHRASE> <PHRASE Label="la_ToolTip_Export" Module="Core" Type="1">RXhwb3J0</PHRASE> <PHRASE Label="la_ToolTip_ExportLanguage" Module="Core" Type="1">RXhwb3J0IExhbmd1YWdl</PHRASE> <PHRASE Label="la_ToolTip_HideMenu" Module="Core" Type="1">SGlkZSBNZW51</PHRASE> <PHRASE Label="la_ToolTip_History" Module="Core" Type="1">SGlzdG9yeQ==</PHRASE> <PHRASE Label="la_ToolTip_Home" Module="Core" Type="1">SG9tZQ==</PHRASE> <PHRASE Label="la_ToolTip_Import" Module="Core" Type="1">SW1wb3J0</PHRASE> <PHRASE Label="la_ToolTip_ImportLanguage" Module="Core" Type="1">SW1wb3J0IExhbmd1YWdl</PHRASE> <PHRASE Label="la_ToolTip_LoginAs" Module="Core" Type="1">TG9naW4gQXM=</PHRASE> <PHRASE Label="la_ToolTip_MoveDown" Module="Core" Type="1">TW92ZSBEb3du</PHRASE> <PHRASE Label="la_ToolTip_MoveUp" Module="Core" Type="1">TW92ZSBVcA==</PHRASE> <PHRASE Label="la_ToolTip_NewBaseStyle" Module="Core" Type="1">TmV3IEJhc2UgU3R5bGU=</PHRASE> <PHRASE Label="la_ToolTip_NewBlockStyle" Module="Core" Type="1">TmV3IEJsb2NrIFN0eWxl</PHRASE> <PHRASE Label="la_ToolTip_NewCountryState" Module="Core" Type="1">TmV3IENvdW50cnkvU3RhdGU=</PHRASE> <PHRASE Label="la_ToolTip_NewGroup" Module="Core" Type="1">TmV3IEdyb3Vw</PHRASE> <PHRASE Label="la_ToolTip_newlabel" Module="Core" Type="1">TmV3IGxhYmVs</PHRASE> <PHRASE Label="la_ToolTip_NewLanguage" Module="Core" Type="1">TmV3IExhbmd1YWdl</PHRASE> <PHRASE Label="la_ToolTip_NewPermission" Module="Core" Type="1">TmV3IFBlcm1pc3Npb24=</PHRASE> <PHRASE Label="la_ToolTip_NewPhrase" Module="Core" Type="1">TmV3IFBocmFzZQ==</PHRASE> <PHRASE Label="la_ToolTip_NewReview" Module="Core" Type="1">TmV3IENvbW1lbnQ=</PHRASE> <PHRASE Label="la_ToolTip_NewScheduledTask" Module="Core" Type="1">TmV3IFNjaGVkdWxlZCBUYXNr</PHRASE> <PHRASE Label="la_ToolTip_NewSearchConfig" Module="Core" Type="1">TmV3IFNlYXJjaCBGaWVsZA==</PHRASE> <PHRASE Label="la_ToolTip_NewSiteDomain" Module="Core" Type="1">TmV3IFNpdGUgRG9tYWlu</PHRASE> <PHRASE Label="la_ToolTip_NewStopWord" Module="Core" Type="1">TmV3IFN0b3AgV29yZA==</PHRASE> <PHRASE Label="la_ToolTip_NewSystemSetting" Module="Core" Type="1">TmV3IFN5c3RlbSBTZXR0aW5n</PHRASE> <PHRASE Label="la_ToolTip_NewTerm" Module="Core" Type="1">TmV3IFRlcm0=</PHRASE> <PHRASE Label="la_ToolTip_newtheme" Module="Core" Type="1">TmV3IFRoZW1l</PHRASE> <PHRASE Label="la_ToolTip_NewUser" Module="Core" Type="1">TmV3IFVzZXI=</PHRASE> <PHRASE Label="la_ToolTip_New_Category" Module="Core" Type="1">TmV3IFNlY3Rpb24=</PHRASE> <PHRASE Label="la_ToolTip_New_CustomField" Module="Core" Type="1">TmV3IEN1c3RvbSBGaWVsZA==</PHRASE> <PHRASE Label="la_ToolTip_New_Form" Module="Core" Type="1">TmV3IEZvcm0=</PHRASE> <PHRASE Label="la_ToolTip_New_FormField" Module="Core" Type="1">TmV3IEZvcm0gRmllbGQ=</PHRASE> <PHRASE Label="la_ToolTip_new_images" Module="Core" Type="1">TmV3IEltYWdlcw==</PHRASE> <PHRASE Label="la_ToolTip_New_Keyword" Module="Core" Type="1">QWRkIEtleXdvcmQ=</PHRASE> <PHRASE Label="la_ToolTip_New_Relation" Module="Core" Type="1">TmV3IFJlbGF0aW9u</PHRASE> <PHRASE Label="la_ToolTip_New_Template" Module="Core" Type="1">TmV3IFRlbXBsYXRl</PHRASE> <PHRASE Label="la_ToolTip_Next" Module="Core" Type="1">TmV4dA==</PHRASE> <PHRASE Label="la_ToolTip_Paste" Module="Core" Type="1">UGFzdGU=</PHRASE> <PHRASE Label="la_ToolTip_Prev" Module="Core" Type="1">UHJldmlvdXM=</PHRASE> <PHRASE Label="la_ToolTip_Preview" Module="Core" Type="1">UHJldmlldw==</PHRASE> <PHRASE Label="la_ToolTip_PrimaryGroup" Module="Core" Type="1">U2V0IFByaW1hcnkgR3JvdXA=</PHRASE> <PHRASE Label="la_ToolTip_Print" Module="Core" Type="1">UHJpbnQ=</PHRASE> <PHRASE Label="la_ToolTip_ProcessQueue" Module="Core" Type="1">UHJvY2VzcyBRdWV1ZQ==</PHRASE> <PHRASE Label="la_ToolTip_Publish" Module="Core" Type="1">UHVibGlzaA==</PHRASE> <PHRASE Label="la_ToolTip_RebuildCategoryCache" Module="Core" Type="1">UmVidWlsZCBTZWN0aW9uIENhY2hl</PHRASE> <PHRASE Label="la_ToolTip_RecalculatePriorities" Module="Core" Type="1">UmVjYWxjdWxhdGUgUHJpb3JpdGllcw==</PHRASE> <PHRASE Label="la_ToolTip_Refresh" Module="Core" Type="1">UmVmcmVzaA==</PHRASE> <PHRASE Label="la_ToolTip_Reply" Module="Core" Type="1">UmVwbHk=</PHRASE> <PHRASE Label="la_ToolTip_RescanThemes" Module="Core" Type="1">UmVzY2FuIFRoZW1lcw==</PHRASE> <PHRASE Label="la_ToolTip_Resend" Module="Core" Type="1">UmVzZW5k</PHRASE> <PHRASE Label="la_ToolTip_Reset" Module="Core" Type="1">UmVzZXQ=</PHRASE> <PHRASE Label="la_ToolTip_ResetCounters" Module="Core" Type="1">UmVzZXQgQ291bnRlcnM=</PHRASE> <PHRASE Label="la_ToolTip_ResetSettings" Module="Core" Type="1">UmVzZXQgUGVyc2lzdGVudCBTZXR0aW5ncw==</PHRASE> <PHRASE Label="la_ToolTip_ResetToBase" Module="Core" Type="1">UmVzZXQgVG8gQmFzZQ==</PHRASE> <PHRASE Label="la_ToolTip_Run" Module="Core" Type="1">UnVu</PHRASE> <PHRASE Label="la_ToolTip_RunSQL" Module="Core" Type="1">UnVuIFNRTA==</PHRASE> <PHRASE Label="la_ToolTip_save" Module="Core" Type="1">U2F2ZQ==</PHRASE> <PHRASE Label="la_ToolTip_SaveAsDraft" Module="Core" Type="1">U2F2ZSBhcyBEcmFmdA==</PHRASE> <PHRASE Label="la_ToolTip_Search" Module="Core" Type="1">U2VhcmNo</PHRASE> <PHRASE Label="la_ToolTip_SearchReset" Module="Core" Type="1">UmVzZXQ=</PHRASE> <PHRASE Label="la_ToolTip_SelectUser" Module="Core" Type="1">U2VsZWN0IFVzZXI=</PHRASE> <PHRASE Label="la_ToolTip_Send" Module="Core" Type="1">U2VuZA==</PHRASE> <PHRASE Label="la_ToolTip_SendEmail" Module="Core" Type="1">U2VuZCBFLW1haWw=</PHRASE> <PHRASE Label="la_ToolTip_SendMail" Module="Core" Type="1">U2VuZCBFLW1haWw=</PHRASE> <PHRASE Label="la_ToolTip_setPrimary" Module="Core" Type="1">U2V0IFByaW1hcnk=</PHRASE> <PHRASE Label="la_ToolTip_setprimarycategory" Module="Core" Type="1">U2V0IFByaW1hcnkgU2VjdGlvbg==</PHRASE> <PHRASE Label="la_ToolTip_SetPrimaryLanguage" Module="Core" Type="1">U2V0IFByaW1hcnkgTGFuZ3VhZ2U=</PHRASE> <PHRASE Label="la_ToolTip_SetPrimaryTranslation" Module="Core" Type="1" Hint="VXNlIHRoaXMgdHJhbnNsYXRpb24gYXMgcHJpbWFyeSBmb3Igb3RoZXIgbGFuZ3VhZ2Vz">VXNlIGFzIFByaW1hcnk=</PHRASE> <PHRASE Label="la_ToolTip_SetSticky" Module="Core" Type="1">U2V0IFN0aWNreQ==</PHRASE> <PHRASE Label="la_ToolTip_Settings" Module="Core" Type="1">U2V0dGluZ3M=</PHRASE> <PHRASE Label="la_ToolTip_ShowMenu" Module="Core" Type="1">U2hvdyBNZW51</PHRASE> <PHRASE Label="la_ToolTip_SynchronizeLanguages" Module="Core" Type="1">U3luY2hyb25pemUgTGFuZ3VhZ2Vz</PHRASE> <PHRASE Label="la_ToolTip_Tools" Module="Core" Type="1">VG9vbHM=</PHRASE> <PHRASE Label="la_ToolTip_Up" Module="Core" Type="1">VXAgYSBTZWN0aW9u</PHRASE> <PHRASE Label="la_ToolTip_ValidateSelected" Module="Core" Type="1">VmFsaWRhdGU=</PHRASE> <PHRASE Label="la_ToolTip_View" Module="Core" Type="1">Vmlldw==</PHRASE> <PHRASE Label="la_ToolTip_ViewDetails" Module="Core" Type="1">VmlldyBEZXRhaWxz</PHRASE> <PHRASE Label="la_ToolTip_ViewItem" Module="Core" Type="1">Vmlldw==</PHRASE> <PHRASE Label="la_to_date" Module="Core" Type="1">VG8gRGF0ZQ==</PHRASE> <PHRASE Label="la_translate" Module="Core" Type="1">VHJhbnNsYXRl</PHRASE> <PHRASE Label="la_Translated" Module="Core" Type="1">VHJhbnNsYXRlZA==</PHRASE> <PHRASE Label="la_Trees" Module="Core" Type="1">VHJlZQ==</PHRASE> <PHRASE Label="la_type_checkbox" Module="Core" Type="1">Q2hlY2tib3hlcw==</PHRASE> <PHRASE Label="la_type_date" Module="Core" Type="1">RGF0ZQ==</PHRASE> <PHRASE Label="la_type_datetime" Module="Core" Type="1">RGF0ZSAmIFRpbWU=</PHRASE> <PHRASE Label="la_type_label" Module="Core" Type="1">TGFiZWw=</PHRASE> <PHRASE Label="la_type_multiselect" Module="Core" Type="1">TXVsdGlwbGUgU2VsZWN0</PHRASE> <PHRASE Label="la_type_password" Module="Core" Type="1">UGFzc3dvcmQgZmllbGQ=</PHRASE> <PHRASE Label="la_type_radio" Module="Core" Type="1">UmFkaW8gYnV0dG9ucw==</PHRASE> <PHRASE Label="la_type_RangeSlider" Module="Core" Type="1">UmFuZ2UgU2xpZGVy</PHRASE> <PHRASE Label="la_type_select" Module="Core" Type="1">RHJvcCBkb3duIGZpZWxk</PHRASE> <PHRASE Label="la_type_SingleCheckbox" Module="Core" Type="1">Q2hlY2tib3g=</PHRASE> <PHRASE Label="la_type_text" Module="Core" Type="1">VGV4dCBmaWVsZA==</PHRASE> <PHRASE Label="la_type_textarea" Module="Core" Type="1">VGV4dCBhcmVh</PHRASE> <PHRASE Label="la_type_Upload" Module="Core" Type="1">RmlsZSBVcGxvYWQ=</PHRASE> <PHRASE Label="la_Unchanged" Module="Core" Type="1">VW5jaGFuZ2Vk</PHRASE> <PHRASE Label="la_Unicode" Module="Core" Type="1">VW5pY29kZQ==</PHRASE> <PHRASE Label="la_updating_config" Module="Core" Type="1">VXBkYXRpbmcgQ29uZmlndXJhdGlvbg==</PHRASE> <PHRASE Label="la_Upload" Module="Core" Type="1">VXBsb2Fk</PHRASE> <PHRASE Label="la_UseCronForRegularEvent" Module="Core" Type="1">VXNlIENyb24gdG8gcnVuIFNjaGVkdWxlZCBUYXNrcw==</PHRASE> <PHRASE Label="la_users_admin_group" Module="Core" Type="1">QXNzaWduIGFkbWluaXN0cmF0b3JzIHRvIGdyb3Vw</PHRASE> <PHRASE Label="la_users_allow_new" Module="Core" Type="1">QWxsb3cgbmV3IHVzZXIgcmVnaXN0cmF0aW9u</PHRASE> <PHRASE Label="la_users_assign_all_to" Module="Core" Type="1">QXNzaWduIEFsbCBVc2VycyBUbyBHcm91cA==</PHRASE> <PHRASE Label="la_users_guest_group" Module="Core" Type="1">QXNzaWduIHVzZXJzIG5vdCBsb2dnZWQgaW4gdG8gZ3JvdXA=</PHRASE> <PHRASE Label="la_users_new_group" Module="Core" Type="1">QXNzaWduIHJlZ2lzdGVyZWQgdXNlcnMgdG8gZ3JvdXA=</PHRASE> <PHRASE Label="la_users_password_auto" Module="Core" Type="1">QXNzaWduIHBhc3N3b3JkIGF1dG9tYXRpY2FsbHk=</PHRASE> <PHRASE Label="la_users_subscriber_group" Module="Core" Type="1">QXNzaWduIG1haWxpbmcgbGlzdCBzdWJzY3JpYmVycyB0byBncm91cA==</PHRASE> <PHRASE Label="la_US_UK" Module="Core" Type="1">VVMvVUs=</PHRASE> <PHRASE Label="la_ValidationEmail" Module="Core" Type="1">RS1tYWlsIGFkZHJlc3M=</PHRASE> <PHRASE Label="la_Value" Module="Core" Type="1">VmFsdWU=</PHRASE> <PHRASE Label="la_visit_DirectReferer" Module="Core" Type="1">RGlyZWN0IGFjY2VzcyBvciBib29rbWFyaw==</PHRASE> <PHRASE Label="la_Warning_Enable_HTML" Module="Core" Type="1">V2FybmluZzogRW5hYmxpbmcgSFRNTCBpcyBhIHNlY3VyaXR5IHJpc2sgYW5kIGNvdWxkIGRhbWFnZSB0aGUgc3lzdGVtIGlmIHVzZWQgaW1wcm9wZXJseSE=</PHRASE> <PHRASE Label="la_Warning_Filter" Module="Core" Type="1">QSBzZWFyY2ggb3IgYSBmaWx0ZXIgaXMgaW4gZWZmZWN0LiBZb3UgbWF5IG5vdCBiZSBzZWVpbmcgYWxsIG9mIHRoZSBkYXRhLg==</PHRASE> <PHRASE Label="la_Warning_NewFormError" Module="Core" Type="1">T25lIG9yIG1vcmUgZmllbGRzIG9uIHRoaXMgZm9ybSBoYXMgYW4gZXJyb3IuPGJyLz4NCjxzbWFsbD5QbGVhc2UgbW92ZSB5b3VyIG1vdXNlIG92ZXIgdGhlIGZpZWxkcyBtYXJrZWQgd2l0aCByZWQgdG8gc2VlIHRoZSBlcnJvciBkZXRhaWxzLjwvc21hbGw+</PHRASE> <PHRASE Label="la_Warning_Save_Item" Module="Core" Type="1">TW9kaWZpY2F0aW9ucyB3aWxsIG5vdCB0YWtlIGVmZmVjdCB1bnRpbCB5b3UgY2xpY2sgdGhlIFNhdmUgYnV0dG9uIQ==</PHRASE> <PHRASE Label="la_week" Module="Core" Type="1">d2Vlaw==</PHRASE> <PHRASE Label="la_Windows" Module="Core" Type="1">V2luZG93cw==</PHRASE> <PHRASE Label="la_year" Module="Core" Type="1">eWVhcg==</PHRASE> <PHRASE Label="la_Yes" Module="Core" Type="1">WWVz</PHRASE> <PHRASE Label="lc_field_CachedDescendantCatsQty" Module="Core" Type="2">U3ViLXNlY3Rpb25zIFF1YW50aXR5</PHRASE> <PHRASE Label="lc_field_CachedNavBar" Module="Core" Type="2">TmF2aWdhdGlvbiBCYXI=</PHRASE> <PHRASE Label="lc_field_cachedrating" Module="Core" Type="2">UmF0aW5n</PHRASE> <PHRASE Label="lc_field_cachedreviewsqty" Module="Core" Type="2">TnVtYmVyIG9mIFJldmlld3M=</PHRASE> <PHRASE Label="lc_field_cachedvotesqty" Module="Core" Type="2">TnVtYmVyIG9mIFJhdGluZyBWb3Rlcw==</PHRASE> <PHRASE Label="lc_field_CategoryId" Module="Core" Type="2">U2VjdGlvbiBJRA==</PHRASE> <PHRASE Label="lc_field_createdbyid" Module="Core" Type="2">Q3JlYXRlZCBCeSBVc2VyIElE</PHRASE> <PHRASE Label="lc_field_createdon" Module="Core" Type="2">RGF0ZSBDcmVhdGVk</PHRASE> <PHRASE Label="lc_field_description" Module="Core" Type="2">RGVzY3JpcHRpb24=</PHRASE> <PHRASE Label="lc_field_EditorsPick" Module="Core" Type="2">RWRpdG9ycyBQaWNr</PHRASE> <PHRASE Label="lc_field_hits" Module="Core" Type="2">SGl0cw==</PHRASE> <PHRASE Label="lc_field_hotitem" Module="Core" Type="2">SXRlbSBJcyBIb3Q=</PHRASE> <PHRASE Label="lc_field_linkid" Module="Core" Type="2">TGluayBJRA==</PHRASE> <PHRASE Label="lc_field_MetaDescription" Module="Core" Type="2">TWV0YSBEZXNjcmlwdGlvbg==</PHRASE> <PHRASE Label="lc_field_MetaKeywords" Module="Core" Type="2">TWV0YSBLZXl3b3Jkcw==</PHRASE> <PHRASE Label="lc_field_modified" Module="Core" Type="2">TGFzdCBNb2RpZmllZCBEYXRl</PHRASE> <PHRASE Label="lc_field_modifiedbyid" Module="Core" Type="2">TW9kaWZpZWQgQnkgVXNlciBJRA==</PHRASE> <PHRASE Label="lc_field_name" Module="Core" Type="2">TmFtZQ==</PHRASE> <PHRASE Label="lc_field_newitem" Module="Core" Type="2">SXRlbSBJcyBOZXc=</PHRASE> <PHRASE Label="lc_field_notifyowneronchanges" Module="Core" Type="2">Tm90aWZ5IE93bmVyIG9mIENoYW5nZXM=</PHRASE> <PHRASE Label="lc_field_orgid" Module="Core" Type="2">T3JpZ2luYWwgSXRlbSBJRA==</PHRASE> <PHRASE Label="lc_field_ownerid" Module="Core" Type="2">T3duZXIgVXNlciBJRA==</PHRASE> <PHRASE Label="lc_field_ParentId" Module="Core" Type="2">UGFyZW50IElE</PHRASE> <PHRASE Label="lc_field_ParentPath" Module="Core" Type="2">UGFyZW50IFBhdGg=</PHRASE> <PHRASE Label="lc_field_popitem" Module="Core" Type="2">SXRlbSBJcyBQb3B1bGFy</PHRASE> <PHRASE Label="lc_field_priority" Module="Core" Type="2">UHJpb3JpdHk=</PHRASE> <PHRASE Label="lc_field_qtysold" Module="Core" Type="2">UXR5IFNvbGQ=</PHRASE> <PHRASE Label="lc_field_resourceid" Module="Core" Type="2">UmVzb3VyY2UgSUQ=</PHRASE> <PHRASE Label="lc_field_status" Module="Core" Type="2">U3RhdHVz</PHRASE> <PHRASE Label="lc_field_topseller" Module="Core" Type="2">SXRlbSBJcyBhIFRvcCBTZWxsZXI=</PHRASE> <PHRASE Label="lc_field_url" Module="Core" Type="2">VVJM</PHRASE> <PHRASE Label="lc_of" Module="Core" Type="2">b2Y=</PHRASE> <PHRASE Label="lc_Text_Invalid" Module="Core" Type="2">SW52YWxpZA==</PHRASE> <PHRASE Label="lc_Text_Not_Validated" Module="Core" Type="2">Tm90IFZhbGlkYXRlZA==</PHRASE> <PHRASE Label="lc_Text_Valid" Module="Core" Type="2">VmFsaWQ=</PHRASE> </PHRASES> <EVENTS> <EVENT Event="CATEGORY.ADD" Type="0"> <SUBJECT>TmV3IENhdGVnb3J5ICI8aW5wMjpjX0ZpZWxkIG5hbWU9Ik5hbWUiLz4iIC0gQWRkZWQ=</SUBJECT> <HTMLBODY>WW91ciBzdWdnZXN0ZWQgY2F0ZWdvcnkgIjxpbnAyOmNfRmllbGQgbmFtZT0iTmFtZSIvPiIgaGFzIGJlZW4gYWRkZWQu</HTMLBODY> </EVENT> <EVENT Event="CATEGORY.ADD" Type="1"> <SUBJECT>TmV3IENhdGVnb3J5ICI8aW5wMjpjX0ZpZWxkIG5hbWU9Ik5hbWUiLz4iIFN1Ym1pdHRlZCBieSBVc2Vycw==</SUBJECT> <HTMLBODY>QSBjYXRlZ29yeSAiPGlucDI6Y19GaWVsZCBuYW1lPSJOYW1lIi8+IiBoYXMgYmVlbiBhZGRlZC4=</HTMLBODY> </EVENT> <EVENT Event="CATEGORY.ADD.PENDING" Type="0"> <SUBJECT>U3VnZ2VzdGVkIENhdGVnb3J5ICI8aW5wMjpjX0ZpZWxkIG5hbWU9Ik5hbWUiLz4iIGlzIFBlbmRpbmc=</SUBJECT> <HTMLBODY>VGhlIGNhdGVnb3J5IHlvdSBzdWdnZXN0ZWQgIjxpbnAyOmNfRmllbGQgbmFtZT0iTmFtZSIvPiIgaXMgcGVuZGluZyBmb3IgYWRtaW5pc3RyYXRpdmUgYXBwcm92YWwuDQoNClRoYW5rIHlvdSE=</HTMLBODY> </EVENT> <EVENT Event="CATEGORY.ADD.PENDING" Type="1"> <SUBJECT>U3VnZ2VzdGVkIENhdGVnb3J5ICI8aW5wMjpjX0ZpZWxkIG5hbWU9Ik5hbWUiLz4iIGlzIFBlbmRpbmc=</SUBJECT> <HTMLBODY>QSBjYXRlZ29yeSAiPGlucDI6Y19GaWVsZCBuYW1lPSJOYW1lIi8+IiBoYXMgYmVlbiBhZGRlZCwgcGVuZGluZyB5b3VyIGNvbmZpcm1hdGlvbi4gIFBsZWFzZSByZXZpZXcgdGhlIGNhdGVnb3J5IGFuZCBhcHByb3ZlIG9yIGRlbnkgaXQu</HTMLBODY> </EVENT> <EVENT Event="CATEGORY.APPROVE" Type="0"> <SUBJECT>QSBjYXRlZ29yeSBoYXMgYmVlbiBhcHByb3ZlZA==</SUBJECT> <HTMLBODY>WW91ciBzdWdnZXN0ZWQgY2F0ZWdvcnkgIjxpbnAyOmNfRmllbGQgbmFtZT0iTmFtZSIvPiIgaGFzIGJlZW4gYXBwcm92ZWQu</HTMLBODY> </EVENT> <EVENT Event="CATEGORY.DENY" Type="0"> <SUBJECT>WW91ciBDYXRlZ29yeSAiPGlucDI6Y19GaWVsZCBuYW1lPSJOYW1lIi8+IiBoYXMgYmVlbiBEZW5pZWQ=</SUBJECT> <HTMLBODY>WW91ciBjYXRlZ29yeSBzdWdnZXN0aW9uICI8aW5wMjpjX0ZpZWxkIG5hbWU9Ik5hbWUiLz4iIGhhcyBiZWVuIGRlbmllZC4=</HTMLBODY> </EVENT> <EVENT Event="FORM.SUBMISSION.REPLY.FROM.USER" Type="1"> <SUBJECT>TmV3IEVtYWlsIFJFUExZIFJlY2VpdmVkIGluICJGZWVkYmFjayBNYW5hZ2VyIiAoPGlucDI6Zm9ybXN1YnMuLWl0ZW1fRmllbGQgbmFtZT0iRm9ybVN1Ym1pc3Npb25JZCIvPik=</SUBJECT> <HTMLBODY>TmV3IEVtYWlsIFJFUExZIFJlY2VpdmVkIGluICZxdW90O0ZlZWRiYWNrIE1hbmFnZXImcXVvdDsuPGJyIC8+DQo8YnIgLz4NCk9yaWdpbmFsIEZlZWRiYWNrSWQ6IDxpbnAyOmZvcm1zdWJzLi1pdGVtX0ZpZWxkIG5hbWU9IkZvcm1TdWJtaXNzaW9uSWQiLz4gPGJyIC8+DQpPcmlnaW5hbCBTdWJqZWN0OiA8aW5wMjpmb3Jtc3Vicy4taXRlbV9Gb3JtRmllbGQgcm9sZT0ic3ViamVjdCIvPiA8YnIgLz4NCjxiciAvPg0KUGxlYXNlIHByb2NlZWQgdG8gdGhlIEFkbWluIENvbnNvbGUgaW4gb3JkZXIgdG8gcmV2aWV3IGFuZCByZXBseSB0byB0aGUgdXNlci4=</HTMLBODY> </EVENT> <EVENT Event="FORM.SUBMISSION.REPLY.FROM.USER.BOUNCED" Type="1"> <SUBJECT>TmV3IEVtYWlsIC0gRGVsaXZlcnkgRmFpbHVyZSBSZWNlaXZlZCBpbiAiRmVlZGJhY2sgTWFuYWdlciIgKDxpbnAyOmZvcm1zdWJzLi1pdGVtX0ZpZWxkIG5hbWU9IkZvcm1TdWJtaXNzaW9uSWQiLz4p</SUBJECT> <HTMLBODY>TmV3IEVtYWlsIERlbGl2ZXJ5IEZhaWx1cmUgUmVjZWl2ZWQgaW4gJnF1b3Q7RmVlZGJhY2sgTWFuYWdlciZxdW90Oy48YnIgLz4NCjxiciAvPg0KT3JpZ2luYWwgRmVlZGJhY2tJZDogPGlucDI6Zm9ybXN1YnMuLWl0ZW1fRmllbGQgbmFtZT0iRm9ybVN1Ym1pc3Npb25JZCIvPiA8YnIgLz4NCk9yaWdpbmFsIFN1YmplY3Q6IDxpbnAyOmZvcm1zdWJzLi1pdGVtX0Zvcm1GaWVsZCByb2xlPSJzdWJqZWN0Ii8+IDxiciAvPg0KPGJyIC8+DQpQbGVhc2UgcHJvY2VlZCB0byB0aGUgQWRtaW4gQ29uc29sZSBpbiBvcmRlciB0byByZXZpZXcgYW5kIHJlcGx5IHRvIHRoZSB1c2VyLg==</HTMLBODY> </EVENT> <EVENT Event="FORM.SUBMISSION.REPLY.TO.USER" Type="1"> <SUBJECT>PGlucDI6bV9QYXJhbSBuYW1lPSJzdWJqZWN0Ii8+ICN2ZXJpZnk8aW5wMjpzdWJtaXNzaW9uLWxvZ19GaWVsZCBuYW1lPSJWZXJpZnlDb2RlIi8+</SUBJECT> <PLAINTEXTBODY>PGlucDI6bV9QYXJhbSBuYW1lPSJtZXNzYWdlIi8+</PLAINTEXTBODY> </EVENT> <EVENT Event="FORM.SUBMITTED" Type="0"> <SUBJECT>VGhhbmsgWW91IGZvciBDb250YWN0aW5nIFVzIQ==</SUBJECT> <HTMLBODY>PHA+VGhhbmsgeW91IGZvciBjb250YWN0aW5nIHVzLiBXZSdsbCBiZSBpbiB0b3VjaCB3aXRoIHlvdSBzaG9ydGx5ITwvcD4=</HTMLBODY> </EVENT> <EVENT Event="FORM.SUBMITTED" Type="1"> <SUBJECT>TmV3IGZvcm0gc3VibWlzc2lvbg==</SUBJECT> <HTMLBODY>PHA+Rm9ybSBoYXMgYmVlbiBzdWJtaXR0ZWQuIFBsZWFzZSBwcm9jZWVkIHRvIHRoZSBBZG1pbiBDb25zb2xlIHRvIHJldmlldyB0aGUgc3VibWlzc2lvbiE8L3A+</HTMLBODY> </EVENT> <EVENT Event="ROOT.RESET.PASSWORD" Type="1"> <SUBJECT>Um9vdCBSZXNldCBQYXNzd29yZA==</SUBJECT> <HTMLBODY>WW91ciBuZXcgcGFzc3dvcmQgaXM6IDxpbnAyOm1fUGFyYW0gbmFtZT0icGFzc3dvcmQiLz4=</HTMLBODY> </EVENT> <EVENT Event="SYSTEM.LOG.NOTIFY" Type="1"> <SUBJECT>U3lzdGVtIExvZyBOb3RpZmljYXRpb25zICg8aW5wMjpzeXN0ZW0tbG9nLmVtYWlsX1RvdGFsUmVjb3Jkcy8+KQ==</SUBJECT> <HTMLBODY>PGlucDI6bV9EZWZpbmVFbGVtZW50IG5hbWU9ImJhY2t0cmFjZV9lbGVtZW50Ij4NCgk8bGk+PGlucDI6bV9QaHJhc2UgbmFtZT0ibGFfTG9nQmFja3RyYWNlRnVuY3Rpb24iLz46IDxpbnAyOm1fUGFyYW0gbmFtZT0iZmlsZV9pbmZvIi8+PC9saT4NCjwvaW5wMjptX0RlZmluZUVsZW1lbnQ+DQoNCjxpbnAyOm1fRGVmaW5lRWxlbWVudCBuYW1lPSJzeXN0ZW1fbG9nX2VsZW1lbnQiPg0KCTxoND48aW5wMjpGaWVsZCBuYW1lPSJMb2dUaW1lc3RhbXAiIGZvcm1hdD0iTSBkIEg6aTpzIi8+IDxpbnAyOkZpZWxkIG5hbWU9IkxvZ0hvc3RuYW1lIi8+IDxpbnAyOlJlcXVlc3RVUkkgaHRtbF9lc2NhcGU9IjEiLz5bUElEPTxpbnAyOkZpZWxkIG5hbWU9IkxvZ1Byb2Nlc3NJZCIvPixVSUQ9PGlucDI6RmllbGQgbmFtZT0iTG9nVW5pcXVlSWQiLz5dPC9oND4NCglbPGlucDI6RmllbGQgbmFtZT0iTG9nTGV2ZWwiLz5dICM8aW5wMjpGaWVsZCBuYW1lPSJMb2dDb2RlIi8+OiA8aW5wMjpGaWVsZCBuYW1lPSJMb2dNZXNzYWdlIiBub19zcGVjaWFsPSIxIi8+IGluIDxpbnAyOkZpbGVuYW1lLz4gb24gbGluZSA8aW5wMjpGaWVsZCBuYW1lPSJMb2dTb3VyY2VGaWxlTGluZSIvPjxici8+DQoNCgk8aW5wMjptX2lmIGNoZWNrPSJGaWVsZCIgbmFtZT0iTG9nQmFja3RyYWNlIiBkYj0iZGIiPg0KCQk8YnIvPkJhY2t0cmFjZToNCg0KCQk8b2wgc3R5bGU9Im1hcmdpbjogMDsgcGFkZGluZy1sZWZ0OiAyNXB4OyBmb250LXNpemU6IDEycHg7Ij4NCgkJCTxpbnAyOlByaW50QmFja3RyYWNlIHJlbmRlcl9hcz0iYmFja3RyYWNlX2VsZW1lbnQiLz4NCgkJPC9vbD4NCgk8L2lucDI6bV9pZj4NCg0KCTxpbnAyOm1faWZub3QgY2hlY2s9Im1fUGFyYW0iIG5hbWU9ImlzX2xhc3QiPjxoci8+PC9pbnAyOm1faWZub3Q+DQo8L2lucDI6bV9EZWZpbmVFbGVtZW50Pg0KDQo8aW5wMjpzeXN0ZW0tbG9nLmVtYWlsX1ByaW50TGlzdCByZW5kZXJfYXM9InN5c3RlbV9sb2dfZWxlbWVudCIvPg==</HTMLBODY> <PLAINTEXTBODY>PGlucDI6bV9EZWZpbmVFbGVtZW50IG5hbWU9ImJhY2t0cmFjZV9wbGFpbl9lbGVtZW50Ij4NCjxpbnAyOkJhY2t0cmFjZUluZGV4Lz4uIDxpbnAyOm1fUGhyYXNlIG5hbWU9ImxhX0xvZ0JhY2t0cmFjZUZ1bmN0aW9uIi8+OiA8aW5wMjptX1BhcmFtIG5hbWU9ImZpbGVfaW5mbyIvPg0KPGlucDI6bV9pZm5vdCBjaGVjaz0ibV9QYXJhbSIgbmFtZT0iaXNfbGFzdCI+DQoNCjwvaW5wMjptX2lmbm90Pg0KPC9pbnAyOm1fRGVmaW5lRWxlbWVudD4NCjxpbnAyOm1fRGVmaW5lRWxlbWVudCBuYW1lPSJzeXN0ZW1fbG9nX3BsYWluX2VsZW1lbnQiPg0KPGlucDI6RmllbGQgbmFtZT0iTG9nVGltZXN0YW1wIiBmb3JtYXQ9Ik0gZCBIOmk6cyIvPiA8aW5wMjpGaWVsZCBuYW1lPSJMb2dIb3N0bmFtZSIvPiA8aW5wMjpSZXF1ZXN0VVJJLz5bUElEPTxpbnAyOkZpZWxkIG5hbWU9IkxvZ1Byb2Nlc3NJZCIvPixVSUQ9PGlucDI6RmllbGQgbmFtZT0iTG9nVW5pcXVlSWQiLz5dDQpbPGlucDI6RmllbGQgbmFtZT0iTG9nTGV2ZWwiLz5dICM8aW5wMjpGaWVsZCBuYW1lPSJMb2dDb2RlIi8+OiA8aW5wMjpGaWVsZCBuYW1lPSJMb2dNZXNzYWdlIiBub19zcGVjaWFsPSIxIi8+IGluIDxpbnAyOkZpbGVuYW1lLz4gb24gbGluZSA8aW5wMjpGaWVsZCBuYW1lPSJMb2dTb3VyY2VGaWxlTGluZSIvPg0KPGlucDI6bV9pZiBjaGVjaz0iRmllbGQiIG5hbWU9IkxvZ0JhY2t0cmFjZSIgZGI9ImRiIj4NCg0KQmFja3RyYWNlOg0KPGlucDI6UHJpbnRCYWNrdHJhY2UgcmVuZGVyX2FzPSJiYWNrdHJhY2VfcGxhaW5fZWxlbWVudCIgc3RyaXBfdGFncz0iMSIvPjwvaW5wMjptX2lmPg0KPGlucDI6bV9pZm5vdCBjaGVjaz0ibV9QYXJhbSIgbmFtZT0iaXNfbGFzdCI+DQotLS0tLS0tLS0tLS0tDQoNCjwvaW5wMjptX2lmbm90Pg0KPC9pbnAyOm1fRGVmaW5lRWxlbWVudD4NCjxpbnAyOnN5c3RlbS1sb2cuZW1haWxfUHJpbnRMaXN0IHJlbmRlcl9hcz0ic3lzdGVtX2xvZ19wbGFpbl9lbGVtZW50Ii8+</PLAINTEXTBODY> </EVENT> <EVENT Event="USER.ADD" Type="0"> <SUBJECT>SW4tcG9ydGFsIHJlZ2lzdHJhdGlvbg==</SUBJECT> <HTMLBODY>RGVhciA8aW5wMjp1LnJlZ2lzdGVyX0ZpZWxkIG5hbWU9IkZpcnN0TmFtZSIgLz4gPGlucDI6dS5yZWdpc3Rlcl9GaWVsZCBuYW1lPSJMYXN0TmFtZSIgLz4sDQoNClRoYW5rIHlvdSBmb3IgcmVnaXN0ZXJpbmcgb24gPGlucDI6bV9MaW5rIHRlbXBsYXRlPSJpbmRleCIvPi4gWW91ciByZWdpc3RyYXRpb24gaXMgbm93IGFjdGl2ZS4NCjxpbnAyOm1faWYgY2hlY2s9InUucmVnaXN0ZXJfRmllbGQiIG5hbWU9IkVtYWlsIj4NCjxici8+PGJyLz4NClBsZWFzZSBjbGljayBoZXJlIHRvIHZlcmlmeSB5b3VyIEUtbWFpbCBhZGRyZXNzOg0KPGEgaHJlZj0iPGlucDI6dS5yZWdpc3Rlcl9Db25maXJtUGFzc3dvcmRMaW5rIHQ9InBsYXRmb3JtL215X2FjY291bnQvdmVyaWZ5X2VtYWlsIiBub19hbXA9IjEiLz4iPjxpbnAyOnUucmVnaXN0ZXJfQ29uZmlybVBhc3N3b3JkTGluayB0PSJwbGF0Zm9ybS9teV9hY2NvdW50L3ZlcmlmeV9lbWFpbCIgbm9fYW1wPSIxIi8+PC9hPjxici8+PGJyLz4NCjwvaW5wMjptX2lmPg==</HTMLBODY> </EVENT> <EVENT Event="USER.ADD" Type="1"> - <SUBJECT>TmV3IFVzZXIgUmVnaXN0cmF0aW9uICg8aW5wMjp1LnJlZ2lzdGVyX0ZpZWxkIG5hbWU9IlVzZXJuYW1lIi8+KQ==</SUBJECT> - <HTMLBODY>QSBuZXcgdXNlciAiPGlucDI6dS5yZWdpc3Rlcl9GaWVsZCBuYW1lPSdVc2VybmFtZScvPiIgaGFzIGJlZW4gYWRkZWQu</HTMLBODY> + <SUBJECT>TmV3IFVzZXIgUmVnaXN0cmF0aW9uICg8aW5wMjp1LnJlZ2lzdGVyX1VzZXJUaXRsZS8+KQ==</SUBJECT> + <HTMLBODY>QSBuZXcgdXNlciAiPGlucDI6dS5yZWdpc3Rlcl9Vc2VyVGl0bGUvPiIgaGFzIGJlZW4gYWRkZWQu</HTMLBODY> </EVENT> <EVENT Event="USER.ADD.BYADMIN" Type="0"> <SUBJECT>TmV3IHVzZXIgaGFzIGJlZW4gY3JlYXRlZA==</SUBJECT> <PLAINTEXTBODY>RGVhciA8aW5wMjp1X0ZpZWxkIG5hbWU9IkZpcnN0TmFtZSIvPiwNCg0KQSBuZXcgdXNlciBoYXMgYmVlbiBjcmVhdGVkIGFuZCBhc3NpZ25lZCB0byB5b3UNCg0KTm93IHlvdSBjYW4gbG9naW4gdXNpbmcgdGhlIGZvbGxvd2luZyBjcmVkZW50aWFsczoNCg0KPGlucDI6bV9pZiBjaGVjaz0idV9GaWVsZCIgbmFtZT0iVXNlcm5hbWUiPlVzZXJuYW1lOiA8aW5wMjp1X0ZpZWxkIG5hbWU9IlVzZXJuYW1lIi8+PGlucDI6bV9lbHNlLz5FLW1haWw6IDxpbnAyOnVfRmllbGQgbmFtZT0iRW1haWwiLz48L2lucDI6bV9pZj4gDQpQYXNzd29yZDogPGlucDI6dV9GaWVsZCBuYW1lPSJQYXNzd29yZF9wbGFpbiIvPiANCg==</PLAINTEXTBODY> </EVENT> <EVENT Event="USER.ADD.PENDING" Type="0"> - <SUBJECT>TmV3IFVzZXIgUmVnaXN0cmF0aW9uICg8aW5wMjp1LnJlZ2lzdGVyX0ZpZWxkIG5hbWU9IlVzZXJuYW1lIi8+PGlucDI6bV9pZiBjaGVjaz0ibV9HZXRDb25maWciIG5hbWU9IlVzZXJfQWxsb3dfTmV3IiBlcXVhbHNfdG89IjQiPiAtIEFjdGl2YXRpb24gRW1haWw8L2lucDI6bV9pZj4p</SUBJECT> + <SUBJECT>TmV3IFVzZXIgUmVnaXN0cmF0aW9uICg8aW5wMjp1LnJlZ2lzdGVyX1VzZXJUaXRsZS8+PGlucDI6bV9pZiBjaGVjaz0ibV9HZXRDb25maWciIG5hbWU9IlVzZXJfQWxsb3dfTmV3IiBlcXVhbHNfdG89IjQiPiAtIEFjdGl2YXRpb24gRW1haWw8L2lucDI6bV9pZj4p</SUBJECT> <HTMLBODY>RGVhciA8aW5wMjp1LnJlZ2lzdGVyX0ZpZWxkIG5hbWU9IkZpcnN0TmFtZSIgLz4gPGlucDI6dS5yZWdpc3Rlcl9GaWVsZCBuYW1lPSJMYXN0TmFtZSIgLz4sPGJyIC8+DQo8YnIgLz4NCjxpbnAyOm1faWYgY2hlY2s9Im1fR2V0Q29uZmlnIiBuYW1lPSJVc2VyX0FsbG93X05ldyIgZXF1YWxzX3RvPSI0Ij4NCglUaGFuayB5b3UgZm9yIHJlZ2lzdGVyaW5nIG9uIDxpbnAyOm1fTGluayB0ZW1wbGF0ZT0iaW5kZXgiLz4gd2Vic2l0ZS4gVG8gYWN0aXZhdGUgeW91ciByZWdpc3RyYXRpb24gcGxlYXNlIGZvbGxvdyBsaW5rIGJlbG93LiA8aW5wMjp1LnJlZ2lzdGVyX0FjdGl2YXRpb25MaW5rIHRlbXBsYXRlPSJwbGF0Zm9ybS9sb2dpbi9hY3RpdmF0ZV9jb25maXJtIi8+DQo8aW5wMjptX2Vsc2UvPg0KCVRoYW5rIHlvdSBmb3IgcmVnaXN0ZXJpbmcgb24gPGlucDI6bV9MaW5rIHRlbXBsYXRlPSJpbmRleCIvPiB3ZWJzaXRlLiBZb3VyIHJlZ2lzdHJhdGlvbiB3aWxsIGJlIGFjdGl2ZSBhZnRlciBhcHByb3ZhbC4gDQoJDQoJPGlucDI6bV9pZiBjaGVjaz0idS5yZWdpc3Rlcl9GaWVsZCIgbmFtZT0iRW1haWwiPg0KCQk8YnIvPjxici8+DQoJCVBsZWFzZSBjbGljayBoZXJlIHRvIHZlcmlmeSB5b3VyIEUtbWFpbCBhZGRyZXNzOg0KCQk8YSBocmVmPSI8aW5wMjp1LnJlZ2lzdGVyX0NvbmZpcm1QYXNzd29yZExpbmsgdD0icGxhdGZvcm0vbXlfYWNjb3VudC92ZXJpZnlfZW1haWwiIG5vX2FtcD0iMSIvPiI+PGlucDI6dS5yZWdpc3Rlcl9Db25maXJtUGFzc3dvcmRMaW5rIHQ9InBsYXRmb3JtL215X2FjY291bnQvdmVyaWZ5X2VtYWlsIiBub19hbXA9IjEiLz48L2E+PGJyLz48YnIvPg0KCTwvaW5wMjptX2lmPg0KPC9pbnAyOm1faWY+</HTMLBODY> </EVENT> <EVENT Event="USER.ADD.PENDING" Type="1"> <SUBJECT>TmV3IFVzZXIgUmVnaXN0ZXJlZA==</SUBJECT> - <HTMLBODY>QSBuZXcgdXNlciAiPGlucDI6dS5yZWdpc3Rlcl9GaWVsZCBuYW1lPSJVc2VybmFtZSIvPiIgaGFzIHJlZ2lzdGVyZWQgYW5kIGlzIHBlbmRpbmcgYWRtaW5pc3RyYXRpdmUgYXBwcm92YWwu</HTMLBODY> + <HTMLBODY>QSBuZXcgdXNlciAiPGlucDI6dS5yZWdpc3Rlcl9Vc2VyVGl0bGUvPiIgaGFzIHJlZ2lzdGVyZWQgYW5kIGlzIHBlbmRpbmcgYWRtaW5pc3RyYXRpdmUgYXBwcm92YWwu</HTMLBODY> </EVENT> <EVENT Event="USER.APPROVE" Type="0"> <SUBJECT>WW91ciBBY2NvdW50IGlzIEFjdGl2ZQ==</SUBJECT> - <HTMLBODY>V2VsY29tZSB0byA8aW5wMjptX0xpbmsgdGVtcGxhdGU9ImluZGV4Ii8+IQ0KDQpZb3VyIHVzZXIgcmVnaXN0cmF0aW9uIGhhcyBiZWVuIGFwcHJvdmVkLiBZb3VyIHVzZXIgbmFtZSBpczogIjxpbnAyOnVfRmllbGQgbmFtZT0iVXNlcm5hbWUiLz4iLg==</HTMLBODY> + <HTMLBODY>V2VsY29tZSB0byA8aW5wMjptX0xpbmsgdGVtcGxhdGU9ImluZGV4Ii8+IQ0KDQpZb3VyIHVzZXIgcmVnaXN0cmF0aW9uIGhhcyBiZWVuIGFwcHJvdmVkLiBZb3VyIHVzZXIgbmFtZSBpczogIjxpbnAyOnVfVXNlclRpdGxlLz4iLg==</HTMLBODY> </EVENT> <EVENT Event="USER.APPROVE" Type="1"> - <SUBJECT>TmV3IFVzZXIgQWNjb3VudCAiPGlucDI6dV9GaWVsZCBuYW1lPSJVc2VybmFtZSIvPiIgd2FzIEFwcHJvdmVk</SUBJECT> - <HTMLBODY>VXNlciAiPGlucDI6dV9GaWVsZCBuYW1lPSJVc2VybmFtZSIvPiIgaGFzIGJlZW4gYXBwcm92ZWQu</HTMLBODY> + <SUBJECT>TmV3IFVzZXIgQWNjb3VudCAiPGlucDI6dV9Vc2VyVGl0bGUvPiIgd2FzIEFwcHJvdmVk</SUBJECT> + <HTMLBODY>VXNlciAiPGlucDI6dV9Vc2VyVGl0bGUvPiIgaGFzIGJlZW4gYXBwcm92ZWQu</HTMLBODY> </EVENT> <EVENT Event="USER.DENY" Type="0"> <SUBJECT>WW91ciBSZWdpc3RyYXRpb24gaGFzIGJlZW4gRGVuaWVk</SUBJECT> <HTMLBODY>WW91ciByZWdpc3RyYXRpb24gb24gPGEgaHJlZj0iPGlucDI6bV9MaW5rIHRlbXBsYXRlPSJpbmRleCIvPiI+PGlucDI6bV9MaW5rIHRlbXBsYXRlPSJpbmRleCIvPjwvYT4gd2Vic2l0ZSBoYXMgYmVlbiBkZW5pZWQu</HTMLBODY> </EVENT> <EVENT Event="USER.DENY" Type="1"> - <SUBJECT>VXNlciBSZWdpc3RyYXRpb24gZm9yICAiPGlucDI6dV9GaWVsZCBuYW1lPSJVc2VybmFtZSIvPiIgaGFzIGJlZW4gRGVuaWVk</SUBJECT> - <HTMLBODY>VXNlciAiPGlucDI6dV9GaWVsZCBuYW1lPSJVc2VybmFtZSIvPiIgaGFzIGJlZW4gZGVuaWVkLg==</HTMLBODY> + <SUBJECT>VXNlciBSZWdpc3RyYXRpb24gZm9yICAiPGlucDI6dV9Vc2VyVGl0bGUvPiIgaGFzIGJlZW4gRGVuaWVk</SUBJECT> + <HTMLBODY>VXNlciAiPGlucDI6dV9Vc2VyVGl0bGUvPiIgaGFzIGJlZW4gZGVuaWVkLg==</HTMLBODY> </EVENT> <EVENT Event="USER.EMAIL.CHANGE.UNDO" Type="0"> <SUBJECT>Q2hhbmdlZCBFLW1haWwgUm9sbGJhY2s=</SUBJECT> <HTMLBODY>SGVsbG8sPGJyLz48YnIvPg0KDQpJdCBzZWVtcyB0aGF0IHlvdSBoYXZlIGNoYW5nZWQgZS1tYWlsIGluIHlvdXIgSW4tcG9ydGFsIGFjY291bnQuIFlvdSBtYXkgdW5kbyB0aGlzIGNoYW5nZSBieSBjbGlja2luZyBvbiB0aGUgbGluayBiZWxvdzo8YnIvPjxici8+DQoNCjxhIGhyZWY9IjxpbnAyOnVfVW5kb0VtYWlsQ2hhbmdlTGluayB0ZW1wbGF0ZT0icGxhdGZvcm0vbXlfYWNjb3VudC9yZXN0b3JlX2VtYWlsIi8+Ij48aW5wMjp1X1VuZG9FbWFpbENoYW5nZUxpbmsgdGVtcGxhdGU9InBsYXRmb3JtL215X2FjY291bnQvcmVzdG9yZV9lbWFpbCIvPjwvYT48YnIvPjxici8+DQoNCklmIHlvdSBiZWxpZXZlIHlvdSBoYXZlIHJlY2VpdmVkIHRoaXMgZW1haWwgaW4gZXJyb3IsIHBsZWFzZSBpZ25vcmUgdGhpcyBlbWFpbC4gWW91ciBhY2NvdW50IHdpbGwgYmUgbGlua2VkIHRvIGFub3RoZXIgZS1tYWlsIHVubGVzcyB5b3UgaGF2ZSBjbGlja2VkIG9uIHRoZSBhYm92ZSBsaW5rLg==</HTMLBODY> </EVENT> <EVENT Event="USER.EMAIL.CHANGE.VERIFY" Type="0"> <SUBJECT>Q2hhbmdlZCBFLW1haWwgVmVyaWZpY2F0aW9u</SUBJECT> <HTMLBODY>SGVsbG8sPGJyLz48YnIvPg0KDQpJdCBzZWVtcyB0aGF0IHlvdSBoYXZlIGNoYW5nZWQgZS1tYWlsIGluIHlvdXIgSW4tcG9ydGFsIGFjY291bnQuIFBsZWFzZSB2ZXJpZnkgdGhpcyBuZXcgZS1tYWlsIGJ5IGNsaWNraW5nIG9uIHRoZSBsaW5rIGJlbG93Ojxici8+PGJyLz4NCg0KPGEgaHJlZj0iPGlucDI6dV9Db25maXJtUGFzc3dvcmRMaW5rIHQ9InBsYXRmb3JtL215X2FjY291bnQvdmVyaWZ5X2VtYWlsIiBub19hbXA9IjEiLz4iPjxpbnAyOnVfQ29uZmlybVBhc3N3b3JkTGluayB0PSJwbGF0Zm9ybS9teV9hY2NvdW50L3ZlcmlmeV9lbWFpbCIgbm9fYW1wPSIxIi8+PC9hPjxici8+PGJyLz4NCg0KSWYgeW91IGJlbGlldmUgeW91IGhhdmUgcmVjZWl2ZWQgdGhpcyBlbWFpbCBpbiBlcnJvciwgcGxlYXNlIGlnbm9yZSB0aGlzIGVtYWlsLiBZb3VyIGVtYWlsIHdpbGwgbm90IGdldCB2ZXJpZmllZCBzdGF0dXMgdW5sZXNzIHlvdSBoYXZlIGNsaWNrZWQgb24gdGhlIGFib3ZlIGxpbmsuDQo=</HTMLBODY> </EVENT> <EVENT Event="USER.MEMBERSHIP.EXPIRATION.NOTICE" Type="0"> <SUBJECT>TWVtYmVyc2hpcCBFeHBpcmF0aW9uIE5vdGljZQ==</SUBJECT> <HTMLBODY>WW91ciBtZW1iZXJzaGlwIG9uIDxpbnAyOm1fTGluayB0ZW1wbGF0ZT0iaW5kZXgiLz4gd2Vic2l0ZSB3aWxsIHNvb24gZXhwaXJlLg==</HTMLBODY> </EVENT> <EVENT Event="USER.MEMBERSHIP.EXPIRATION.NOTICE" Type="1"> - <SUBJECT>TWVtYmVyc2hpcCBFeHBpcmF0aW9uIE5vdGljZSBmb3IgIjxpbnAyOkZpZWxkIG5hbWU9IlVzZXJuYW1lIi8+IiBTZW50</SUBJECT> - <HTMLBODY>VXNlciA8aW5wMjpGaWVsZCBuYW1lPSJVc2VybmFtZSIvPiBtZW1iZXJzaGlwIHdpbGwgZXhwaXJlIHNvb24u</HTMLBODY> + <SUBJECT>TWVtYmVyc2hpcCBFeHBpcmF0aW9uIE5vdGljZSBmb3IgIjxpbnAyOlVzZXJUaXRsZS8+IiBTZW50</SUBJECT> + <HTMLBODY>VXNlciA8aW5wMjpVc2VyVGl0bGUvPiBtZW1iZXJzaGlwIHdpbGwgZXhwaXJlIHNvb24u</HTMLBODY> </EVENT> <EVENT Event="USER.MEMBERSHIP.EXPIRED" Type="0"> <SUBJECT>WW91ciBNZW1iZXJzaGlwIEV4cGlyZWQ=</SUBJECT> <HTMLBODY>WW91ciBtZW1iZXJzaGlwIG9uIDxpbnAyOm1fTGluayB0ZW1wbGF0ZT0iaW5kZXgiLz4gd2Vic2l0ZSBoYXMgZXhwaXJlZC4=</HTMLBODY> </EVENT> <EVENT Event="USER.MEMBERSHIP.EXPIRED" Type="1"> - <SUBJECT>VXNlcidzIE1lbWJlcnNoaXAgRXhwaXJlZCAgKCA8aW5wMjpGaWVsZCBuYW1lPSJVc2VybmFtZSIvPik=</SUBJECT> - <HTMLBODY>VXNlcidzICg8aW5wMjpGaWVsZCBuYW1lPSJVc2VybmFtZSIvPikgbWVtYmVyc2hpcCBvbiA8aW5wMjptX0xpbmsgdGVtcGxhdGU9ImluZGV4Ii8+IHdlYnNpdGUgaGFzIGV4cGlyZWQu</HTMLBODY> + <SUBJECT>VXNlcidzIE1lbWJlcnNoaXAgRXhwaXJlZCAoIDxpbnAyOlVzZXJUaXRsZS8+KQ==</SUBJECT> + <HTMLBODY>VXNlcidzICg8aW5wMjpVc2VyVGl0bGUvPikgbWVtYmVyc2hpcCBvbiA8aW5wMjptX0xpbmsgdGVtcGxhdGU9ImluZGV4Ii8+IHdlYnNpdGUgaGFzIGV4cGlyZWQu</HTMLBODY> </EVENT> <EVENT Event="USER.NEW.PASSWORD" Type="0"> <SUBJECT>TmV3IHBhc3N3b3JkIGdlbmVyYXRlZA==</SUBJECT> <PLAINTEXTBODY>RGVhciA8aW5wMjp1X0ZpZWxkIG5hbWU9IkZpcnN0TmFtZSIvPiwNCg0KQSBuZXcgcGFzc3dvcmQgaGFzIGJlZW4gZ2VuZXJhdGVkIGZvciB5b3VyIHVzZXIuDQoNCk5vdyB5b3UgY2FuIGxvZ2luIHVzaW5nIHRoZSBmb2xsb3dpbmcgY3JlZGVudGlhbHM6DQoNCjxpbnAyOm1faWYgY2hlY2s9InVfRmllbGQiIG5hbWU9IlVzZXJuYW1lIj5Vc2VybmFtZTogPGlucDI6dV9GaWVsZCBuYW1lPSJVc2VybmFtZSIvPjxpbnAyOm1fZWxzZS8+RS1tYWlsOiA8aW5wMjp1X0ZpZWxkIG5hbWU9IkVtYWlsIi8+PC9pbnAyOm1faWY+IA0KUGFzc3dvcmQ6IDxpbnAyOnVfRmllbGQgbmFtZT0iUGFzc3dvcmRfcGxhaW4iLz4g</PLAINTEXTBODY> </EVENT> <EVENT Event="USER.PSWDC" Type="0"> <SUBJECT>UmVzZXQgUGFzc3dvcmQgQ29uZmlybWF0aW9u</SUBJECT> <HTMLBODY>SGVsbG8sPGJyLz48YnIvPg0KDQpJdCBzZWVtcyB0aGF0IHlvdSBoYXZlIHJlcXVlc3RlZCBhIHBhc3N3b3JkIHJlc2V0IGZvciB5b3VyIEluLXBvcnRhbCBhY2NvdW50LiBJZiB5b3Ugd291bGQgbGlrZSB0byBwcm9jZWVkIGFuZCBjaGFuZ2UgdGhlIHBhc3N3b3JkLCBwbGVhc2UgY2xpY2sgb24gdGhlIGxpbmsgYmVsb3c6PGJyLz48YnIvPg0KDQo8YSBocmVmPSI8aW5wMjp1X0NvbmZpcm1QYXNzd29yZExpbmsgbm9fYW1wPSIxIi8+Ij48aW5wMjp1X0NvbmZpcm1QYXNzd29yZExpbmsgbm9fYW1wPSIxIi8+PC9hPjxici8+PGJyLz4NCg0KWW91IHdpbGwgcmVjZWl2ZSBhIHNlY29uZCBlbWFpbCB3aXRoIHlvdXIgbmV3IHBhc3N3b3JkIHNob3J0bHkuPGJyLz48YnIvPg0KDQpJZiB5b3UgYmVsaWV2ZSB5b3UgaGF2ZSByZWNlaXZlZCB0aGlzIGVtYWlsIGluIGVycm9yLCBwbGVhc2UgaWdub3JlIHRoaXMgZW1haWwuIFlvdXIgcGFzc3dvcmQgd2lsbCBub3QgYmUgY2hhbmdlZCB1bmxlc3MgeW91IGhhdmUgY2xpY2tlZCBvbiB0aGUgYWJvdmUgbGluay4NCg==</HTMLBODY> </EVENT> <EVENT Event="USER.SUBSCRIBE" Type="0"> <SUBJECT>U3Vic2NyaWJlZCB0byBhIE1haWxpbmcgTGlzdCBvbiA8aW5wMjptX0xpbmsgdGVtcGxhdGU9ImluZGV4Ii8+</SUBJECT> <HTMLBODY>WW91IGhhdmUgc3Vic2NyaWJlZCB0byBhIG1haWxpbmcgbGlzdCBvbiA8aW5wMjptX0xpbmsgdGVtcGxhdGU9ImluZGV4Ii8+IHdlYnNpdGUu</HTMLBODY> </EVENT> <EVENT Event="USER.SUBSCRIBE" Type="1"> <SUBJECT>TmV3IFVzZXIgaGFzIFN1YnNjcmliZWQgdG8gYSBNYWxsaW5nIExpc3Q=</SUBJECT> <HTMLBODY>TmV3IHVzZXIgPGlucDI6RmllbGQgbmFtZT0iRW1haWwiLz4gaGFzIHN1YnNjcmliZWQgdG8gYSBtYWlsaW5nIGxpc3Qgb24gPGEgaHJlZj0iPGlucDI6bV9MaW5rIHRlbXBsYXRlPSJpbmRleCIvPiI+PGlucDI6bV9MaW5rIHRlbXBsYXRlPSJpbmRleCIvPjwvYT4gd2Vic2l0ZS4=</HTMLBODY> </EVENT> <EVENT Event="USER.SUGGEST" Type="0"> <SUBJECT>Q2hlY2sgb3V0IHRoaXMgV2Vic2l0ZQ==</SUBJECT> <HTMLBODY>SGVsbG8sPC9icj48L2JyPg0KDQpUaGlzIG1lc3NhZ2UgaGFzIGJlZW4gc2VudCB0byB5b3UgZnJvbSBvbmUgb2YgeW91ciBmcmllbmRzLjwvYnI+PC9icj4NCkNoZWNrIG91dCB0aGlzIHNpdGU6IDxhIGhyZWY9IjxpbnAyOm1fTGluayB0ZW1wbGF0ZT0iaW5kZXgiLz4iPjxpbnAyOm1fTGluayB0ZW1wbGF0ZT0iaW5kZXgiLz48L2E+IQ==</HTMLBODY> </EVENT> <EVENT Event="USER.SUGGEST" Type="1"> <SUBJECT>V2Vic2l0ZSBTdWdnZXN0ZWQgdG8gYSBGcmllbmQ=</SUBJECT> <HTMLBODY>QSB2aXNpdG9yIHN1Z2dlc3RlZCA8YSBocmVmPSI8aW5wMjptX0xpbmsgdGVtcGxhdGU9ImluZGV4Ii8+Ij48aW5wMjptX0xpbmsgdGVtcGxhdGU9ImluZGV4Ii8+PC9hPiB3ZWJzaXRlIHRvIGEgZnJpZW5kLg==</HTMLBODY> </EVENT> <EVENT Event="USER.UNSUBSCRIBE" Type="0"> <SUBJECT>WW91IGhhdmUgYmVlbiB1bnN1YnNjcmliZWQ=</SUBJECT> <HTMLBODY>WW91IGhhdmUgc3VjY2Vzc2Z1bGx5IHVuc3Vic2NyaWJlZCBmcm9tIHRoZSBtYWlsaW5nIGxpc3Qgb24gPGEgaHJlZj0iPGlucDI6bV9CYXNlVXJsIC8+Ij48aW5wMjptX0Jhc2VVcmwgLz48L2E+IHdlYnNpdGUu</HTMLBODY> </EVENT> <EVENT Event="USER.UNSUBSCRIBE" Type="1"> <SUBJECT>VXNlciBVbnN1YnNyaWJlZCBmcm9tIE1haWxpbmcgTGlzdA==</SUBJECT> <HTMLBODY>QSB1c2VyICI8aW5wMjpGaWVsZCBuYW1lPSJFbWFpbCIvPiIgaGFzIHVuc3Vic2NyaWJlZCBmcm9tIHRoZSBtYWlsaW5nIGxpc3Qgb24gPGEgaHJlZj0iPGlucDI6bV9MaW5rIHRlbXBsYXRlPSJpbmRleCIvPiI+PGlucDI6bV9MaW5rIHRlbXBsYXRlPSJpbmRleCIvPjwvYT4u</HTMLBODY> </EVENT> <EVENT Event="USER.VALIDATE" Type="0"> <SUBJECT>VXNlciBSZWdpc3RyYXRpb24gaXMgVmFsaWRhdGVk</SUBJECT> - <HTMLBODY>V2VsY29tZSB0byBJbi1wb3J0YWwhPGJyLz48YnIvPg0KDQpZb3VyIHVzZXIgcmVnaXN0cmF0aW9uIGhhcyBiZWVuIGFwcHJvdmVkLiBZb3UgY2FuIGxvZ2luIG5vdyA8YSBocmVmPSI8aW5wMjptX0xpbmsgdGVtcGxhdGU9ImluZGV4Ii8+Ij48aW5wMjptX0xpbmsgdGVtcGxhdGU9ImluZGV4Ii8+PC9hPiB1c2luZyB0aGUgZm9sbG93aW5nIGluZm9ybWF0aW9uOjxici8+PGJyLz4NCg0KPT09PT09PT09PT09PT09PT09PGJyLz4NClVzZXJuYW1lOiAiPGlucDI6dV9GaWVsZCBuYW1lPSJVc2VybmFtZSIvPiI8YnIvPg0KUGFzc3dvcmQ6ICI8aW5wMjp1X0ZpZWxkIG5hbWU9IlBhc3N3b3JkX3BsYWluIi8+Ijxici8+DQo9PT09PT09PT09PT09PT09PT08YnIvPjxici8+DQo=</HTMLBODY> + <HTMLBODY>V2VsY29tZSB0byBJbi1wb3J0YWwhPGJyLz48YnIvPg0KDQpZb3VyIHVzZXIgcmVnaXN0cmF0aW9uIGhhcyBiZWVuIGFwcHJvdmVkLiBZb3UgY2FuIGxvZ2luIG5vdyA8YSBocmVmPSI8aW5wMjptX0xpbmsgdGVtcGxhdGU9ImluZGV4Ii8+Ij48aW5wMjptX0xpbmsgdGVtcGxhdGU9ImluZGV4Ii8+PC9hPiB1c2luZyB0aGUgZm9sbG93aW5nIGluZm9ybWF0aW9uOjxici8+PGJyLz4NCg0KPT09PT09PT09PT09PT09PT09PGJyLz4NClVzZXJuYW1lOiAiPGlucDI6dV9Vc2VyVGl0bGUvPiI8YnIvPg0KUGFzc3dvcmQ6ICI8aW5wMjp1X0ZpZWxkIG5hbWU9IlBhc3N3b3JkX3BsYWluIi8+Ijxici8+DQo9PT09PT09PT09PT09PT09PT08YnIvPjxici8+DQo=</HTMLBODY> </EVENT> <EVENT Event="USER.VALIDATE" Type="1"> <SUBJECT>TmV3IFVzZXIgUmVnaXN0cmF0aW9uIGlzIFZhbGlkYXRlZA==</SUBJECT> - <HTMLBODY>VXNlciAiPGlucDI6dV9GaWVsZCBuYW1lPSJVc2VybmFtZSIvPiIgaGFzIGJlZW4gdmFsaWRhdGVkLg==</HTMLBODY> + <HTMLBODY>VXNlciAiPGlucDI6dV9Vc2VyVGl0bGUvPiIgaGFzIGJlZW4gdmFsaWRhdGVkLg==</HTMLBODY> </EVENT> </EVENTS> <COUNTRIES> <COUNTRY Iso="ABW" Translation="QXJ1YmE="/> <COUNTRY Iso="AFG" Translation="QWZnaGFuaXN0YW4="/> <COUNTRY Iso="AGO" Translation="QW5nb2xh"/> <COUNTRY Iso="AIA" Translation="QW5ndWlsbGE="/> <COUNTRY Iso="ALB" Translation="QWxiYW5pYQ=="/> <COUNTRY Iso="AND" Translation="QW5kb3JyYQ=="/> <COUNTRY Iso="ANT" Translation="TmV0aGVybGFuZHMgQW50aWxsZXM="/> <COUNTRY Iso="ARE" Translation="VW5pdGVkIEFyYWIgRW1pcmF0ZXM="/> <COUNTRY Iso="ARG" Translation="QXJnZW50aW5h"/> <COUNTRY Iso="ARM" Translation="QXJtZW5pYQ=="/> <COUNTRY Iso="ASM" Translation="QW1lcmljYW4gc2Ftb2E="/> <COUNTRY Iso="ATA" Translation="QW50YXJjdGljYQ=="/> <COUNTRY Iso="ATF" Translation="RnJlbmNoIFNvdXRoZXJuIFRlcnJpdG9yaWVz"/> <COUNTRY Iso="ATG" Translation="QW50aWd1YSBhbmQgYmFyYnVkYQ=="/> <COUNTRY Iso="AUS" Translation="QXVzdHJhbGlh"/> <COUNTRY Iso="AUT" Translation="QXVzdHJpYQ=="/> <COUNTRY Iso="AZE" Translation="QXplcmJhaWphbg=="/> <COUNTRY Iso="BDI" Translation="QnVydW5kaQ=="/> <COUNTRY Iso="BEL" Translation="QmVsZ2l1bQ=="/> <COUNTRY Iso="BEN" Translation="QmVuaW4="/> <COUNTRY Iso="BFA" Translation="QnVya2luYSBGYXNv"/> <COUNTRY Iso="BGD" Translation="QmFuZ2xhZGVzaA=="/> <COUNTRY Iso="BGR" Translation="QnVsZ2FyaWE="/> <COUNTRY Iso="BHR" Translation="QmFocmFpbg=="/> <COUNTRY Iso="BHS" Translation="QmFoYW1hcw=="/> <COUNTRY Iso="BIH" Translation="Qm9zbmlhIGFuZCBIZXJ6ZWdvd2luYQ=="/> <COUNTRY Iso="BLR" Translation="QmVsYXJ1cw=="/> <COUNTRY Iso="BLZ" Translation="QmVsaXpl"/> <COUNTRY Iso="BMU" Translation="QmVybXVkYQ=="/> <COUNTRY Iso="BOL" Translation="Qm9saXZpYQ=="/> <COUNTRY Iso="BRA" Translation="QnJhemls"/> <COUNTRY Iso="BRB" Translation="QmFyYmFkb3M="/> <COUNTRY Iso="BRN" Translation="QnJ1bmVpIERhcnVzc2FsYW0="/> <COUNTRY Iso="BTN" Translation="Qmh1dGFu"/> <COUNTRY Iso="BVT" Translation="Qm91dmV0IElzbGFuZA=="/> <COUNTRY Iso="BWA" Translation="Qm90c3dhbmE="/> <COUNTRY Iso="CAF" Translation="Q2VudHJhbCBBZnJpY2FuIFJlcHVibGlj"/> <COUNTRY Iso="CAN" Translation="Q2FuYWRh"> <STATE Iso="AB" Translation="QWxiZXJ0YQ=="/> <STATE Iso="BC" Translation="QnJpdGlzaCBDb2x1bWJpYQ=="/> <STATE Iso="MB" Translation="TWFuaXRvYmE="/> <STATE Iso="NB" Translation="TmV3IEJydW5zd2ljaw=="/> <STATE Iso="NL" Translation="TmV3Zm91bmRsYW5kIGFuZCBMYWJyYWRvcg=="/> <STATE Iso="NS" Translation="Tm92YSBTY290aWE="/> <STATE Iso="NT" Translation="Tm9ydGh3ZXN0IFRlcnJpdG9yaWVz"/> <STATE Iso="NU" Translation="TnVuYXZ1dA=="/> <STATE Iso="ON" Translation="T250YXJpbw=="/> <STATE Iso="PE" Translation="UHJpbmNlIEVkd2FyZCBJc2xhbmQ="/> <STATE Iso="QC" Translation="UXVlYmVj"/> <STATE Iso="SK" Translation="U2Fza2F0Y2hld2Fu"/> <STATE Iso="YT" Translation="WXVrb24="/> </COUNTRY> <COUNTRY Iso="CCK" Translation="Q29jb3MgKEtlZWxpbmcpIElzbGFuZHM="/> <COUNTRY Iso="CHE" Translation="U3dpdHplcmxhbmQ="/> <COUNTRY Iso="CHL" Translation="Q2hpbGU="/> <COUNTRY Iso="CHN" Translation="Q2hpbmE="/> <COUNTRY Iso="CIV" Translation="Q290ZSBkJ0l2b2lyZQ=="/> <COUNTRY Iso="CMR" Translation="Q2FtZXJvb24="/> <COUNTRY Iso="COD" Translation="Q29uZ28sIERlbW9jcmF0aWMgUmVwdWJsaWMgb2YgKFdhcyBaYWlyZSk="/> <COUNTRY Iso="COG" Translation="Q29uZ28sIFBlb3BsZSdzIFJlcHVibGljIG9m"/> <COUNTRY Iso="COK" Translation="Q29vayBJc2xhbmRz"/> <COUNTRY Iso="COL" Translation="Q29sb21iaWE="/> <COUNTRY Iso="COM" Translation="Q29tb3Jvcw=="/> <COUNTRY Iso="CPV" Translation="Q2FwZSBWZXJkZQ=="/> <COUNTRY Iso="CRI" Translation="Q29zdGEgUmljYQ=="/> <COUNTRY Iso="CUB" Translation="Q3ViYQ=="/> <COUNTRY Iso="CXR" Translation="Q2hyaXN0bWFzIElzbGFuZA=="/> <COUNTRY Iso="CYM" Translation="Q2F5bWFuIElzbGFuZHM="/> <COUNTRY Iso="CYP" Translation="Q3lwcnVz"/> <COUNTRY Iso="CZE" Translation="Q3plY2ggUmVwdWJsaWM="/> <COUNTRY Iso="DEU" Translation="R2VybWFueQ=="/> <COUNTRY Iso="DJI" Translation="RGppYm91dGk="/> <COUNTRY Iso="DMA" Translation="RG9taW5pY2E="/> <COUNTRY Iso="DNK" Translation="RGVubWFyaw=="/> <COUNTRY Iso="DOM" Translation="RG9taW5pY2FuIFJlcHVibGlj"/> <COUNTRY Iso="DZA" Translation="QWxnZXJpYQ=="/> <COUNTRY Iso="ECU" Translation="RWN1YWRvcg=="/> <COUNTRY Iso="EGY" Translation="RWd5cHQ="/> <COUNTRY Iso="ERI" Translation="RXJpdHJlYQ=="/> <COUNTRY Iso="ESH" Translation="V2VzdGVybiBTYWhhcmE="/> <COUNTRY Iso="ESP" Translation="U3BhaW4="/> <COUNTRY Iso="EST" Translation="RXN0b25pYQ=="/> <COUNTRY Iso="ETH" Translation="RXRoaW9waWE="/> <COUNTRY Iso="FIN" Translation="RmlubGFuZA=="/> <COUNTRY Iso="FJI" Translation="RmlqaQ=="/> <COUNTRY Iso="FLK" Translation="RmFsa2xhbmQgSXNsYW5kcyAoTWFsdmluYXMp"/> <COUNTRY Iso="FRA" Translation="RnJhbmNl"/> <COUNTRY Iso="FRO" Translation="RmFyb2UgSXNsYW5kcw=="/> <COUNTRY Iso="FSM" Translation="TWljcm9uZXNpYSwgRmVkZXJhdGVkIFN0YXRlcyBvZg=="/> <COUNTRY Iso="FXX" Translation="RnJhbmNlLCBNZXRyb3BvbGl0YW4="/> <COUNTRY Iso="GAB" Translation="R2Fib24="/> <COUNTRY Iso="GBR" Translation="VW5pdGVkIEtpbmdkb20="/> <COUNTRY Iso="GEO" Translation="R2VvcmdpYQ=="/> <COUNTRY Iso="GHA" Translation="R2hhbmE="/> <COUNTRY Iso="GIB" Translation="R2licmFsdGFy"/> <COUNTRY Iso="GIN" Translation="R3VpbmVh"/> <COUNTRY Iso="GLP" Translation="R3VhZGVsb3VwZQ=="/> <COUNTRY Iso="GMB" Translation="R2FtYmlh"/> <COUNTRY Iso="GNB" Translation="R3VpbmVhLUJpc3NhdQ=="/> <COUNTRY Iso="GNQ" Translation="RXF1YXRvcmlhbCBHdWluZWE="/> <COUNTRY Iso="GRC" Translation="R3JlZWNl"/> <COUNTRY Iso="GRD" Translation="R3JlbmFkYQ=="/> <COUNTRY Iso="GRL" Translation="R3JlZW5sYW5k"/> <COUNTRY Iso="GTM" Translation="R3VhdGVtYWxh"/> <COUNTRY Iso="GUF" Translation="RnJlbmNoIEd1aWFuYQ=="/> <COUNTRY Iso="GUM" Translation="R3VhbQ=="/> <COUNTRY Iso="GUY" Translation="R3V5YW5h"/> <COUNTRY Iso="HKG" Translation="SG9uZyBrb25n"/> <COUNTRY Iso="HMD" Translation="SGVhcmQgYW5kIE1jIERvbmFsZCBJc2xhbmRz"/> <COUNTRY Iso="HND" Translation="SG9uZHVyYXM="/> <COUNTRY Iso="HRV" Translation="Q3JvYXRpYSAobG9jYWwgbmFtZTogSHJ2YXRza2Ep"/> <COUNTRY Iso="HTI" Translation="SGFpdGk="/> <COUNTRY Iso="HUN" Translation="SHVuZ2FyeQ=="/> <COUNTRY Iso="IDN" Translation="SW5kb25lc2lh"/> <COUNTRY Iso="IND" Translation="SW5kaWE="/> <COUNTRY Iso="IOT" Translation="QnJpdGlzaCBJbmRpYW4gT2NlYW4gVGVycml0b3J5"/> <COUNTRY Iso="IRL" Translation="SXJlbGFuZA=="/> <COUNTRY Iso="IRN" Translation="SXJhbiAoSXNsYW1pYyBSZXB1YmxpYyBvZik="/> <COUNTRY Iso="IRQ" Translation="SXJhcQ=="/> <COUNTRY Iso="ISL" Translation="SWNlbGFuZA=="/> <COUNTRY Iso="ISR" Translation="SXNyYWVs"/> <COUNTRY Iso="ITA" Translation="SXRhbHk="/> <COUNTRY Iso="JAM" Translation="SmFtYWljYQ=="/> <COUNTRY Iso="JOR" Translation="Sm9yZGFu"/> <COUNTRY Iso="JPN" Translation="SmFwYW4="/> <COUNTRY Iso="KAZ" Translation="S2F6YWtoc3Rhbg=="/> <COUNTRY Iso="KEN" Translation="S2VueWE="/> <COUNTRY Iso="KGZ" Translation="S3lyZ3l6c3Rhbg=="/> <COUNTRY Iso="KHM" Translation="Q2FtYm9kaWE="/> <COUNTRY Iso="KIR" Translation="S2lyaWJhdGk="/> <COUNTRY Iso="KNA" Translation="U2FpbnQgS2l0dHMgYW5kIE5ldmlz"/> <COUNTRY Iso="KOR" Translation="S29yZWEsIFJlcHVibGljIG9m"/> <COUNTRY Iso="KWT" Translation="S3V3YWl0"/> <COUNTRY Iso="LAO" Translation="TGFvIFBlb3BsZSdzIERlbW9jcmF0aWMgUmVwdWJsaWM="/> <COUNTRY Iso="LBN" Translation="TGViYW5vbg=="/> <COUNTRY Iso="LBR" Translation="TGliZXJpYQ=="/> <COUNTRY Iso="LBY" Translation="TGlieWFuIEFyYWIgSmFtYWhpcml5YQ=="/> <COUNTRY Iso="LCA" Translation="U2FpbnQgTHVjaWE="/> <COUNTRY Iso="LIE" Translation="TGllY2h0ZW5zdGVpbg=="/> <COUNTRY Iso="LKA" Translation="U3JpIGxhbmth"/> <COUNTRY Iso="LSO" Translation="TGVzb3Robw=="/> <COUNTRY Iso="LTU" Translation="TGl0aHVhbmlh"/> <COUNTRY Iso="LUX" Translation="THV4ZW1ib3VyZw=="/> <COUNTRY Iso="LVA" Translation="TGF0dmlh"/> <COUNTRY Iso="MAC" Translation="TWFjYXU="/> <COUNTRY Iso="MAR" Translation="TW9yb2Njbw=="/> <COUNTRY Iso="MCO" Translation="TW9uYWNv"/> <COUNTRY Iso="MDA" Translation="TW9sZG92YSwgUmVwdWJsaWMgb2Y="/> <COUNTRY Iso="MDG" Translation="TWFkYWdhc2Nhcg=="/> <COUNTRY Iso="MDV" Translation="TWFsZGl2ZXM="/> <COUNTRY Iso="MEX" Translation="TWV4aWNv"/> <COUNTRY Iso="MHL" Translation="TWFyc2hhbGwgSXNsYW5kcw=="/> <COUNTRY Iso="MKD" Translation="TWFjZWRvbmlh"/> <COUNTRY Iso="MLI" Translation="TWFsaQ=="/> <COUNTRY Iso="MLT" Translation="TWFsdGE="/> <COUNTRY Iso="MMR" Translation="TXlhbm1hcg=="/> <COUNTRY Iso="MNG" Translation="TW9uZ29saWE="/> <COUNTRY Iso="MNP" Translation="Tm9ydGhlcm4gTWFyaWFuYSBJc2xhbmRz"/> <COUNTRY Iso="MOZ" Translation="TW96YW1iaXF1ZQ=="/> <COUNTRY Iso="MRT" Translation="TWF1cml0YW5pYQ=="/> <COUNTRY Iso="MSR" Translation="TW9udHNlcnJhdA=="/> <COUNTRY Iso="MTQ" Translation="TWFydGluaXF1ZQ=="/> <COUNTRY Iso="MUS" Translation="TWF1cml0aXVz"/> <COUNTRY Iso="MWI" Translation="TWFsYXdp"/> <COUNTRY Iso="MYS" Translation="TWFsYXlzaWE="/> <COUNTRY Iso="MYT" Translation="TWF5b3R0ZQ=="/> <COUNTRY Iso="NAM" Translation="TmFtaWJpYQ=="/> <COUNTRY Iso="NCL" Translation="TmV3IENhbGVkb25pYQ=="/> <COUNTRY Iso="NER" Translation="TmlnZXI="/> <COUNTRY Iso="NFK" Translation="Tm9yZm9sayBJc2xhbmQ="/> <COUNTRY Iso="NGA" Translation="TmlnZXJpYQ=="/> <COUNTRY Iso="NIC" Translation="TmljYXJhZ3Vh"/> <COUNTRY Iso="NIU" Translation="Tml1ZQ=="/> <COUNTRY Iso="NLD" Translation="TmV0aGVybGFuZHM="/> <COUNTRY Iso="NOR" Translation="Tm9yd2F5"/> <COUNTRY Iso="NPL" Translation="TmVwYWw="/> <COUNTRY Iso="NRU" Translation="TmF1cnU="/> <COUNTRY Iso="NZL" Translation="TmV3IFplYWxhbmQ="/> <COUNTRY Iso="OMN" Translation="T21hbg=="/> <COUNTRY Iso="PAK" Translation="UGFraXN0YW4="/> <COUNTRY Iso="PAN" Translation="UGFuYW1h"/> <COUNTRY Iso="PCN" Translation="UGl0Y2Fpcm4="/> <COUNTRY Iso="PER" Translation="UGVydQ=="/> <COUNTRY Iso="PHL" Translation="UGhpbGlwcGluZXM="/> <COUNTRY Iso="PLW" Translation="UGFsYXU="/> <COUNTRY Iso="PNG" Translation="UGFwdWEgTmV3IEd1aW5lYQ=="/> <COUNTRY Iso="POL" Translation="UG9sYW5k"/> <COUNTRY Iso="PRI" Translation="UHVlcnRvIFJpY28="/> <COUNTRY Iso="PRK" Translation="S29yZWEsIERlbW9jcmF0aWMgUGVvcGxlJ3MgUmVwdWJsaWMgb2Y="/> <COUNTRY Iso="PRT" Translation="UG9ydHVnYWw="/> <COUNTRY Iso="PRY" Translation="UGFyYWd1YXk="/> <COUNTRY Iso="PSE" Translation="UGFsZXN0aW5pYW4gVGVycml0b3J5LCBPY2N1cGllZA=="/> <COUNTRY Iso="PYF" Translation="RnJlbmNoIFBvbHluZXNpYQ=="/> <COUNTRY Iso="QAT" Translation="UWF0YXI="/> <COUNTRY Iso="REU" Translation="UmV1bmlvbg=="/> <COUNTRY Iso="ROU" Translation="Um9tYW5pYQ=="/> <COUNTRY Iso="RUS" Translation="UnVzc2lhbiBGZWRlcmF0aW9u"/> <COUNTRY Iso="RWA" Translation="UndhbmRh"/> <COUNTRY Iso="SAU" Translation="U2F1ZGkgQXJhYmlh"/> <COUNTRY Iso="SDN" Translation="U3VkYW4="/> <COUNTRY Iso="SEN" Translation="U2VuZWdhbA=="/> <COUNTRY Iso="SGP" Translation="U2luZ2Fwb3Jl"/> <COUNTRY Iso="SGS" Translation="U291dGggR2VvcmdpYSBhbmQgVGhlIFNvdXRoIFNhbmR3aWNoIElzbGFuZHM="/> <COUNTRY Iso="SHN" Translation="U3QuIGhlbGVuYQ=="/> <COUNTRY Iso="SJM" Translation="U3ZhbGJhcmQgYW5kIEphbiBNYXllbiBJc2xhbmRz"/> <COUNTRY Iso="SLB" Translation="U29sb21vbiBJc2xhbmRz"/> <COUNTRY Iso="SLE" Translation="U2llcnJhIExlb25l"/> <COUNTRY Iso="SLV" Translation="RWwgU2FsdmFkb3I="/> <COUNTRY Iso="SMR" Translation="U2FuIE1hcmlubw=="/> <COUNTRY Iso="SOM" Translation="U29tYWxpYQ=="/> <COUNTRY Iso="SPM" Translation="U3QuIFBpZXJyZSBhbmQgTWlxdWVsb24="/> <COUNTRY Iso="STP" Translation="U2FvIFRvbWUgYW5kIFByaW5jaXBl"/> <COUNTRY Iso="SUR" Translation="U3VyaW5hbWU="/> <COUNTRY Iso="SVK" Translation="U2xvdmFraWEgKFNsb3ZhayBSZXB1YmxpYyk="/> <COUNTRY Iso="SVN" Translation="U2xvdmVuaWE="/> <COUNTRY Iso="SWE" Translation="U3dlZGVu"/> <COUNTRY Iso="SWZ" Translation="U3dhemlsYW5k"/> <COUNTRY Iso="SYC" Translation="U2V5Y2hlbGxlcw=="/> <COUNTRY Iso="SYR" Translation="U3lyaWFuIEFyYWIgUmVwdWJsaWM="/> <COUNTRY Iso="TCA" Translation="VHVya3MgYW5kIENhaWNvcyBJc2xhbmRz"/> <COUNTRY Iso="TCD" Translation="Q2hhZA=="/> <COUNTRY Iso="TGO" Translation="VG9nbw=="/> <COUNTRY Iso="THA" Translation="VGhhaWxhbmQ="/> <COUNTRY Iso="TJK" Translation="VGFqaWtpc3Rhbg=="/> <COUNTRY Iso="TKL" Translation="VG9rZWxhdQ=="/> <COUNTRY Iso="TKM" Translation="VHVya21lbmlzdGFu"/> <COUNTRY Iso="TLS" Translation="RWFzdCBUaW1vcg=="/> <COUNTRY Iso="TON" Translation="VG9uZ2E="/> <COUNTRY Iso="TTO" Translation="VHJpbmlkYWQgYW5kIFRvYmFnbw=="/> <COUNTRY Iso="TUN" Translation="VHVuaXNpYQ=="/> <COUNTRY Iso="TUR" Translation="VHVya2V5"/> <COUNTRY Iso="TUV" Translation="VHV2YWx1"/> <COUNTRY Iso="TWN" Translation="VGFpd2Fu"/> <COUNTRY Iso="TZA" Translation="VGFuemFuaWEsIFVuaXRlZCBSZXB1YmxpYyBvZg=="/> <COUNTRY Iso="UGA" Translation="VWdhbmRh"/> <COUNTRY Iso="UKR" Translation="VWtyYWluZQ=="/> <COUNTRY Iso="UMI" Translation="VW5pdGVkIFN0YXRlcyBNaW5vciBPdXRseWluZyBJc2xhbmRz"/> <COUNTRY Iso="URY" Translation="VXJ1Z3VheQ=="/> <COUNTRY Iso="USA" Translation="VW5pdGVkIFN0YXRlcw=="> <STATE Iso="AK" Translation="QWxhc2th"/> <STATE Iso="AL" Translation="QWxhYmFtYQ=="/> <STATE Iso="AR" Translation="QXJrYW5zYXM="/> <STATE Iso="AZ" Translation="QXJpem9uYQ=="/> <STATE Iso="CA" Translation="Q2FsaWZvcm5pYQ=="/> <STATE Iso="CO" Translation="Q29sb3JhZG8="/> <STATE Iso="CT" Translation="Q29ubmVjdGljdXQ="/> <STATE Iso="DC" Translation="RGlzdHJpY3Qgb2YgQ29sdW1iaWE="/> <STATE Iso="DE" Translation="RGVsYXdhcmU="/> <STATE Iso="FL" Translation="RmxvcmlkYQ=="/> <STATE Iso="GA" Translation="R2VvcmdpYQ=="/> <STATE Iso="HI" Translation="SGF3YWlp"/> <STATE Iso="IA" Translation="SW93YQ=="/> <STATE Iso="ID" Translation="SWRhaG8="/> <STATE Iso="IL" Translation="SWxsaW5vaXM="/> <STATE Iso="IN" Translation="SW5kaWFuYQ=="/> <STATE Iso="KS" Translation="S2Fuc2Fz"/> <STATE Iso="KY" Translation="S2VudHVja3k="/> <STATE Iso="LA" Translation="TG91aXNpYW5h"/> <STATE Iso="MA" Translation="TWFzc2FjaHVzZXR0cw=="/> <STATE Iso="MD" Translation="TWFyeWxhbmQ="/> <STATE Iso="ME" Translation="TWFpbmU="/> <STATE Iso="MI" Translation="TWljaGlnYW4="/> <STATE Iso="MN" Translation="TWlubmVzb3Rh"/> <STATE Iso="MO" Translation="TWlzc291cmk="/> <STATE Iso="MS" Translation="TWlzc2lzc2lwcGk="/> <STATE Iso="MT" Translation="TW9udGFuYQ=="/> <STATE Iso="NC" Translation="Tm9ydGggQ2Fyb2xpbmE="/> <STATE Iso="ND" Translation="Tm9ydGggRGFrb3Rh"/> <STATE Iso="NE" Translation="TmVicmFza2E="/> <STATE Iso="NH" Translation="TmV3IEhhbXBzaGlyZQ=="/> <STATE Iso="NJ" Translation="TmV3IEplcnNleQ=="/> <STATE Iso="NM" Translation="TmV3IE1leGljbw=="/> <STATE Iso="NV" Translation="TmV2YWRh"/> <STATE Iso="NY" Translation="TmV3IFlvcms="/> <STATE Iso="OH" Translation="T2hpbw=="/> <STATE Iso="OK" Translation="T2tsYWhvbWE="/> <STATE Iso="OR" Translation="T3JlZ29u"/> <STATE Iso="PA" Translation="UGVubnN5bHZhbmlh"/> <STATE Iso="PR" Translation="UHVlcnRvIFJpY28="/> <STATE Iso="RI" Translation="UmhvZGUgSXNsYW5k"/> <STATE Iso="SC" Translation="U291dGggQ2Fyb2xpbmE="/> <STATE Iso="SD" Translation="U291dGggRGFrb3Rh"/> <STATE Iso="TN" Translation="VGVubmVzc2Vl"/> <STATE Iso="TX" Translation="VGV4YXM="/> <STATE Iso="UT" Translation="VXRhaA=="/> <STATE Iso="VA" Translation="VmlyZ2luaWE="/> <STATE Iso="VT" Translation="VmVybW9udA=="/> <STATE Iso="WA" Translation="V2FzaGluZ3Rvbg=="/> <STATE Iso="WI" Translation="V2lzY29uc2lu"/> <STATE Iso="WV" Translation="V2VzdCBWaXJnaW5pYQ=="/> <STATE Iso="WY" Translation="V3lvbWluZw=="/> </COUNTRY> <COUNTRY Iso="UZB" Translation="VXpiZWtpc3Rhbg=="/> <COUNTRY Iso="VAT" Translation="VmF0aWNhbiBDaXR5IFN0YXRlIChIb2x5IFNlZSk="/> <COUNTRY Iso="VCT" Translation="U2FpbnQgVmluY2VudCBhbmQgVGhlIEdyZW5hZGluZXM="/> <COUNTRY Iso="VEN" Translation="VmVuZXp1ZWxh"/> <COUNTRY Iso="VGB" Translation="VmlyZ2luIElzbGFuZHMgKEJyaXRpc2gp"/> <COUNTRY Iso="VIR" Translation="VmlyZ2luIElzbGFuZHMgKFUuUy4p"/> <COUNTRY Iso="VNM" Translation="VmlldG5hbQ=="/> <COUNTRY Iso="VUT" Translation="VmFudWF0dQ=="/> <COUNTRY Iso="WLF" Translation="V2FsbGlzIGFuZCBGdXR1bmEgSXNsYW5kcw=="/> <COUNTRY Iso="WSM" Translation="U2Ftb2E="/> <COUNTRY Iso="YEM" Translation="WWVtZW4="/> <COUNTRY Iso="YUG" Translation="WXVnb3NsYXZpYQ=="/> <COUNTRY Iso="ZAF" Translation="U291dGggQWZyaWNh"/> <COUNTRY Iso="ZMB" Translation="WmFtYmlh"/> <COUNTRY Iso="ZWE" Translation="WmltYmFid2U="/> </COUNTRIES> </LANGUAGE> </LANGUAGES> Index: branches/5.3.x/index.php =================================================================== --- branches/5.3.x/index.php (revision 16394) +++ branches/5.3.x/index.php (revision 16395) Property changes on: branches/5.3.x/index.php ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,2 ## Merged /in-portal/branches/5.1.x/index.php:r15851 Merged /in-portal/branches/5.2.x/index.php:r16196,16198-16199,16201,16203-16205,16212,16231-16233,16235-16236,16241,16243,16245-16247,16249-16251,16259-16264,16266-16268,16270-16274,16276-16279,16286,16288,16290-16291,16293-16300,16304-16309,16313-16314,16317-16318,16338-16339,16347-16348,16350,16352-16359,16362-16363,16367,16373-16374,16376-16377,16379,16388 Index: branches/5.3.x/CREDITS =================================================================== --- branches/5.3.x/CREDITS (revision 16394) +++ branches/5.3.x/CREDITS (revision 16395) Property changes on: branches/5.3.x/CREDITS ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,2 ## Merged /in-portal/branches/5.1.x/CREDITS:r15851 Merged /in-portal/branches/5.2.x/CREDITS:r16196,16198-16199,16201,16203-16205,16212,16231-16233,16235-16236,16241,16243,16245-16247,16249-16251,16259-16264,16266-16268,16270-16274,16276-16279,16286,16288,16290-16291,16293-16300,16304-16309,16313-16314,16317-16318,16338-16339,16347-16348,16350,16352-16359,16362-16363,16367,16373-16374,16376-16377,16379,16388 Index: branches/5.3.x/README =================================================================== --- branches/5.3.x/README (revision 16394) +++ branches/5.3.x/README (revision 16395) Property changes on: branches/5.3.x/README ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,2 ## Merged /in-portal/branches/5.1.x/README:r15851 Merged /in-portal/branches/5.2.x/README:r16196,16198-16199,16201,16203-16205,16212,16231-16233,16235-16236,16241,16243,16245-16247,16249-16251,16259-16264,16266-16268,16270-16274,16276-16279,16286,16288,16290-16291,16293-16300,16304-16309,16313-16314,16317-16318,16338-16339,16347-16348,16350,16352-16359,16362-16363,16367,16373-16374,16376-16377,16379,16388 Index: branches/5.3.x/composer.json =================================================================== --- branches/5.3.x/composer.json (revision 16394) +++ branches/5.3.x/composer.json (revision 16395) @@ -1,18 +1,17 @@ { "name": "In-Portal", "require": { "symfony/console": "~2.6", "stecman/symfony-console-completion": "~0.5", "symfony/process": "^2.7" }, "require-dev": { "aik099/phpunit-mink": "~2.0", "qa-tools/qa-tools": "~1.0", "aik099/coding-standard": "dev-in-portal", "nikic/php-parser": "~1.2", "mockery/mockery": "~0.9", - "mindplay/annotations": "~1.2@dev", "behat/mink": "~1.6" } } Index: branches/5.3.x/tools/build/targets/folders.xml =================================================================== --- branches/5.3.x/tools/build/targets/folders.xml (revision 16394) +++ branches/5.3.x/tools/build/targets/folders.xml (revision 16395) @@ -1,70 +1,70 @@ <?xml version="1.0" encoding="UTF-8"?> <project name="inc_folders" default="build"> <target name="folders-create" description="Creates folders, to store build artifacts" depends="create-aliases,folders-delete"> <!--<mkdir dir="${build.api.dir}"/>--> <!--<mkdir dir="${build.coverage.dir}"/>--> <mkdir dir="${build.logs.dir}"/> <!--<mkdir dir="${build.pdepend.dir}"/>--> <mkdir dir="${build.cache.dir}"/> <mkdir dir="${build.dir}/test_data"/> <mkdir dir="${build.dir}/system/remote_code_coverage"/> - <copy todir="${base.dir}/system" overwrite="true"> - <fileset dir="${build.dir}/test_data"> - <include name="**/*"/> - </fileset> - </copy> - <if> <isfalse value="${local.build}"/> <then> + <copy todir="${base.dir}/system" overwrite="true"> + <fileset dir="${build.dir}/test_data"> + <include name="**/*"/> + </fileset> + </copy> + <echo message="Setting 'RewriteBase /' to solve problems with 'VirtualDocumentRoot' option"/> <echo file="${base.dir}/.htaccess" append="true"> RewriteBase / </echo> </then> </if> </target> <target name="folders-delete" description="Deletes folders, where build artifacts are stored" depends="create-aliases"> <!--<delete dir="${build.api.dir}"/>--> <!--<delete dir="${build.coverage.dir}"/>--> <delete dir="${build.logs.dir}"/> <!--<delete dir="${build.pdepend.dir}"/>--> </target> <target name="config-create" description="Creates '/system/config.php' file" depends="create-aliases,config-delete"> <touch file="${base.dir}/system/config.php" /> <echo file="${base.dir}/system/config.php"><?php $_CONFIG['Misc']['WriteablePath'] = '/system'; $_CONFIG['Misc']['RestrictedPath'] = '/system/.restricted'; $_CONFIG['Misc']['WebsitePath'] = '${website.path}'; $_CONFIG['Misc']['Domain'] = '${website.domain}'; $_CONFIG['Misc']['AdminDirectory'] = '/admin'; $_CONFIG['Misc']['AdminPresetsDirectory'] = '/admin'; $_CONFIG['Misc']['ApplicationClass'] = 'kApplication'; $_CONFIG['Misc']['ApplicationPath'] = '/core/kernel/application.php'; $_CONFIG['Misc']['CacheHandler'] = 'Fake'; $_CONFIG['Misc']['MemcacheServers'] = 'localhost:11211'; $_CONFIG['Misc']['CompressionEngine'] = 'php'; $_CONFIG['Misc']['WebsiteCharset'] = 'utf-8'; $_CONFIG['Misc']['EnableSystemLog'] = '1'; $_CONFIG['Misc']['SystemLogMaxLevel'] = '5'; $_CONFIG['Misc']['TrustProxy'] = '0'; $_CONFIG['Database']['DBType'] = 'mysql'; $_CONFIG['Database']['DBHost'] = '${db.host}'; $_CONFIG['Database']['DBName'] = '${db.name}'; $_CONFIG['Database']['DBUser'] = '${db.user}'; $_CONFIG['Database']['DBUserPassword'] = '${db.password}'; $_CONFIG['Database']['DBCollation'] = 'utf8_general_ci'; $_CONFIG['Database']['TablePrefix'] = '${db.tableprefix}'; $_CONFIG['Database']['DBCharset'] = 'utf8'; </echo> </target> <target name="config-delete" description="Deletes '/system/config.php' file" depends="create-aliases"> <delete file="${base.dir}/system/config.php"/> </target> </project> Index: branches/5.3.x/tools/configure_profile.php =================================================================== --- branches/5.3.x/tools/configure_profile.php (revision 16394) +++ branches/5.3.x/tools/configure_profile.php (revision 16395) Property changes on: branches/5.3.x/tools/configure_profile.php ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +Id \ No newline at end of property Index: branches/5.3.x/tools/class_locator.php =================================================================== --- branches/5.3.x/tools/class_locator.php (nonexistent) +++ branches/5.3.x/tools/class_locator.php (revision 16395) @@ -0,0 +1,37 @@ +<?php +/** +* @version $Id$ +* @package In-Portal +* @copyright Copyright (C) 1997 - 2016 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. +*/ + +$start = microtime(true); + +define('DBG_ZEND_PRESENT', 1); +define('INDEX_FILE', 'index.php'); +define('FULL_PATH', realpath(dirname(__FILE__) . '/..')); + +include_once(FULL_PATH . '/core/kernel/startup.php'); + +$application = kApplication::Instance(); +$application->Init(); + +/** @var \Composer\Autoload\ClassLoader $composer_class_loader */ +$composer_class_loader = require FULL_PATH . '/vendor/autoload.php'; + +return function ($class) use ($application, $composer_class_loader) { + $file = $application->findClassFile($class); + + if ( $file !== false ) { + return $file; + } + + return $composer_class_loader->findFile($class); +}; Property changes on: branches/5.3.x/tools/class_locator.php ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +LF \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +Id \ No newline at end of property Index: branches/5.3.x/LICENSES =================================================================== --- branches/5.3.x/LICENSES (revision 16394) +++ branches/5.3.x/LICENSES (revision 16395) Property changes on: branches/5.3.x/LICENSES ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,2 ## Merged /in-portal/branches/5.1.x/LICENSES:r15851 Merged /in-portal/branches/5.2.x/LICENSES:r16196,16198-16199,16201,16203-16205,16212,16231-16233,16235-16236,16241,16243,16245-16247,16249-16251,16259-16264,16266-16268,16270-16274,16276-16279,16286,16288,16290-16291,16293-16300,16304-16309,16313-16314,16317-16318,16338-16339,16347-16348,16350,16352-16359,16362-16363,16367,16373-16374,16376-16377,16379,16388 Index: branches/5.3.x/INSTALL =================================================================== --- branches/5.3.x/INSTALL (revision 16394) +++ branches/5.3.x/INSTALL (revision 16395) Property changes on: branches/5.3.x/INSTALL ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,2 ## Merged /in-portal/branches/5.1.x/INSTALL:r15851 Merged /in-portal/branches/5.2.x/INSTALL:r16196,16198-16199,16201,16203-16205,16212,16231-16233,16235-16236,16241,16243,16245-16247,16249-16251,16259-16264,16266-16268,16270-16274,16276-16279,16286,16288,16290-16291,16293-16300,16304-16309,16313-16314,16317-16318,16338-16339,16347-16348,16350,16352-16359,16362-16363,16367,16373-16374,16376-16377,16379,16388 Index: branches/5.3.x/COPYRIGHT =================================================================== --- branches/5.3.x/COPYRIGHT (revision 16394) +++ branches/5.3.x/COPYRIGHT (revision 16395) Property changes on: branches/5.3.x/COPYRIGHT ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,2 ## Merged /in-portal/branches/5.1.x/COPYRIGHT:r15851 Merged /in-portal/branches/5.2.x/COPYRIGHT:r16196,16198-16199,16201,16203-16205,16212,16231-16233,16235-16236,16241,16243,16245-16247,16249-16251,16259-16264,16266-16268,16270-16274,16276-16279,16286,16288,16290-16291,16293-16300,16304-16309,16313-16314,16317-16318,16338-16339,16347-16348,16350,16352-16359,16362-16363,16367,16373-16374,16376-16377,16379,16388 Index: branches/5.3.x/.htaccess =================================================================== --- branches/5.3.x/.htaccess (revision 16394) +++ branches/5.3.x/.htaccess (revision 16395) @@ -1,71 +1,71 @@ ### File security # Exclude direct access to tpl, tpl.xml, inc.php, sql extensions # <FilesMatch "\.(tpl|tpl.xml|inc.php|sql)$"> order allow,deny deny from all satisfy all </FilesMatch> <IfModule mod_expires.c> ExpiresActive on ExpiresByType text/css "access plus 1 month" ExpiresByType application/x-javascript "access plus 1 month" ExpiresByType application/javascript "access plus 1 month" ExpiresByType image/gif "access plus 1 month" ExpiresByType image/jpeg "access plus 1 month" ExpiresByType image/png "access plus 1 month" ExpiresByType image/x-icon "access plus 1 month" ExpiresByType image/icon "access plus 1 month" </IfModule> <IfModule mod_rewrite.c> ## Tell PHP that the mod_rewrite module is ENABLED. SetEnv HTTP_MOD_REWRITE On ## Enable mod-rewrite RewriteEngine On ###### Rewrite rule to force 'www.' prefix. Use only if needed # If your site can be accessed both with and without the 'www.' prefix, # use the following setting to redirect all users to access the site with the 'www.' # when they access without 'www.'. Uncomment and MAKE sure to adapt for your domain name # # RewriteCond %{HTTP_HOST} ^example\.com$ [NC] # RewriteRule ^(.*)$ http://www.example.com/$1 [L,R=301] ###### Rewrite rules to block common hacks ## If you experience problems comment out the operations listed below ## Block out any script trying to base64_encode crap to send via URL RewriteCond %{QUERY_STRING} base64_encode.*\(.*\) [OR] ## Block out any script that includes a <script> tag in URL RewriteCond %{QUERY_STRING} (<|%3C).*script.*(>|%3E) [NC,OR] ## Block out any script trying to set a PHP GLOBALS variable via URL RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2}) [OR] ## Block out any script trying to modify a _REQUEST variable via URL RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2}) ## Send all blocked request to homepage with 403 Forbidden error! RewriteRule ^(.*)$ index.php [F,L] ## Uncomment line below if FollowSymLinks option is not enabled ## by default in server configuration # # Options +FollowSymLinks ## Uncomment following line if your webserver's URL ## is not directly related to physical file paths. ## Update Your In-Portal Directory (just / for root) # # RewriteBase / ## In-Portal SEF URLs # RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME}/ !-f RewriteCond %{REQUEST_FILENAME}/index.php !-f RewriteCond %{REQUEST_FILENAME}/index.html !-f - RewriteCond %{REQUEST_URI} !\.(gif|jpg|png|bmp|js|css|ico|swf)$ [NC] + RewriteCond %{REQUEST_URI} !\.(gif|jpg|png|bmp|js|css|ico|swf|map)$ [NC] RewriteRule ^(.*) index.php?rewrite=on&_mod_rw_url_=$1 [QSA] </IfModule> -RedirectMatch 404 /(\.svn|CVS)(/|$) \ No newline at end of file +RedirectMatch 404 /(\.svn|CVS)(/|$) Property changes on: branches/5.3.x/.htaccess ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,2 ## Merged /in-portal/branches/5.1.x/.htaccess:r15851 Merged /in-portal/branches/5.2.x/.htaccess:r16196,16198-16199,16201,16203-16205,16212,16231-16233,16235-16236,16241,16243,16245-16247,16249-16251,16259-16264,16266-16268,16270-16274,16276-16279,16286,16288,16290-16291,16293-16300,16304-16309,16313-16314,16317-16318,16338-16339,16347-16348,16350,16352-16359,16362-16363,16367,16373-16374,16376-16377,16379,16388 Index: branches/5.3.x/composer.lock =================================================================== --- branches/5.3.x/composer.lock (revision 16394) +++ branches/5.3.x/composer.lock (revision 16395) @@ -1,1839 +1,1839 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "e57cf4dcb89e749c1707598be9560ba2", + "hash": "d1845e3801289eda5979369163949615", "packages": [ { "name": "stecman/symfony-console-completion", "version": "0.5.1", "source": { "type": "git", "url": "https://github.com/stecman/symfony-console-completion.git", "reference": "1a9fc7ab4820cd1aabbdc584c6b25d221e7b6cb5" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/stecman/symfony-console-completion/zipball/1a9fc7ab4820cd1aabbdc584c6b25d221e7b6cb5", "reference": "1a9fc7ab4820cd1aabbdc584c6b25d221e7b6cb5", "shasum": "" }, "require": { "php": ">=5.3.2", "symfony/console": "~2.2" }, "require-dev": { "phpunit/phpunit": "~4.1" }, "type": "library", "extra": { "branch-alias": { "dev-master": "0.5.x-dev" } }, "autoload": { "psr-4": { "Stecman\\Component\\Symfony\\Console\\BashCompletion\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Stephen Holdaway", "email": "stephen@stecman.co.nz" } ], "description": "Automatic BASH completion for Symfony Console Component based applications.", "time": "2015-05-07 12:21:50" }, { "name": "symfony/console", "version": "v2.6.7", "target-dir": "Symfony/Component/Console", "source": { "type": "git", "url": "https://github.com/symfony/Console.git", "reference": "ebc5679854aa24ed7d65062e9e3ab0b18a917272" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/Console/zipball/ebc5679854aa24ed7d65062e9e3ab0b18a917272", "reference": "ebc5679854aa24ed7d65062e9e3ab0b18a917272", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { "psr/log": "~1.0", "symfony/event-dispatcher": "~2.1", "symfony/phpunit-bridge": "~2.7", "symfony/process": "~2.1" }, "suggest": { "psr/log": "For using the console logger", "symfony/event-dispatcher": "", "symfony/process": "" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.6-dev" } }, "autoload": { "psr-0": { "Symfony\\Component\\Console\\": "" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Console Component", "homepage": "https://symfony.com", "time": "2015-05-02 15:18:45" }, { "name": "symfony/process", "version": "v2.7.4", "source": { "type": "git", "url": "https://github.com/symfony/Process.git", "reference": "f7b3f73f70a7f8f49a1c838dc3debbf054732d8e" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/Process/zipball/f7b3f73f70a7f8f49a1c838dc3debbf054732d8e", "reference": "f7b3f73f70a7f8f49a1c838dc3debbf054732d8e", "shasum": "" }, "require": { "php": ">=5.3.9" }, "require-dev": { "symfony/phpunit-bridge": "~2.7" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.7-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Process\\": "" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Process Component", "homepage": "https://symfony.com", "time": "2015-08-27 06:45:45" } ], "packages-dev": [ { "name": "aik099/coding-standard", "version": "dev-in-portal", "source": { "type": "git", "url": "https://github.com/aik099/CodingStandard.git", "reference": "987a6781521c9293b3e36ff3877cdff63010b291" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/aik099/CodingStandard/zipball/b2d0a7b7e4b69f8a7024c462d511c5a09f46ccb6", "reference": "987a6781521c9293b3e36ff3877cdff63010b291", "shasum": "" }, "require-dev": { "squizlabs/php_codesniffer": "~2.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Alexander Obuhovich", "email": "aik.bold@gmail.com" } ], "description": "The PHP_CodeSniffer coding standard I'm using on all of my projects.", "keywords": [ "PHP_CodeSniffer", "codesniffer" ], "time": "2015-05-17 06:49:37" }, { "name": "aik099/phpunit-mink", "version": "v2.1.0", "source": { "type": "git", "url": "https://github.com/minkphp/phpunit-mink.git", "reference": "62889c5b065d69b695c87df6218b22147d78b147" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/minkphp/phpunit-mink/zipball/62889c5b065d69b695c87df6218b22147d78b147", "reference": "62889c5b065d69b695c87df6218b22147d78b147", "shasum": "" }, "require": { "behat/mink": "~1.6@dev", "behat/mink-selenium2-driver": "~1.2", "php": ">=5.3.2", "phpunit/phpunit": ">=3.7.8", "pimple/pimple": "~2.0|~3.0", "symfony/event-dispatcher": "~2.4" }, "require-dev": { "aik099/coding-standard": "dev-master", "mockery/mockery": "~0.9" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0.x-dev" } }, "autoload": { "psr-0": { "aik099\\": "./library/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Alexander Obuhovich", "email": "aik.bold@gmail.com" } ], "description": "Library for using Mink in PHPUnit tests. Supports session sharing between tests in a test case.", "homepage": "http://github.com/minkphp/phpunit-mink", "keywords": [ "BrowserStack", "Mink", "Sauce", "SauceLabs", "phpunit", "selenium", "tests" ], "time": "2015-05-06 13:33:55" }, { "name": "behat/mink", "version": "v1.6.1", "source": { "type": "git", "url": "https://github.com/minkphp/Mink.git", "reference": "8b68523a339ec991bcd638b39dc8f04f808da88a" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/minkphp/Mink/zipball/8b68523a339ec991bcd638b39dc8f04f808da88a", "reference": "8b68523a339ec991bcd638b39dc8f04f808da88a", "shasum": "" }, "require": { "php": ">=5.3.1", "symfony/css-selector": "~2.0" }, "suggest": { "behat/mink-browserkit-driver": "extremely fast headless driver for Symfony\\Kernel-based apps (Sf2, Silex)", "behat/mink-goutte-driver": "fast headless driver for any app without JS emulation", "behat/mink-selenium2-driver": "slow, but JS-enabled driver for any app (requires Selenium2)", "behat/mink-zombie-driver": "fast and JS-enabled headless driver for any app (requires node.js)" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.6.x-dev" } }, "autoload": { "psr-4": { "Behat\\Mink\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Konstantin Kudryashov", "email": "ever.zet@gmail.com", "homepage": "http://everzet.com" } ], "description": "Browser controller/emulator abstraction for PHP", "homepage": "http://mink.behat.org/", "keywords": [ "browser", "testing", "web" ], "time": "2015-02-04 17:02:06" }, { "name": "behat/mink-selenium2-driver", "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/minkphp/MinkSelenium2Driver.git", "reference": "8018fee80bf6573f909ece3e0dfc07d0eb352210" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/minkphp/MinkSelenium2Driver/zipball/8018fee80bf6573f909ece3e0dfc07d0eb352210", "reference": "8018fee80bf6573f909ece3e0dfc07d0eb352210", "shasum": "" }, "require": { "behat/mink": "~1.6@dev", "instaclick/php-webdriver": "~1.1", "php": ">=5.3.1" }, "type": "mink-driver", "extra": { "branch-alias": { "dev-master": "1.2.x-dev" } }, "autoload": { "psr-0": { "Behat\\Mink\\Driver": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Konstantin Kudryashov", "email": "ever.zet@gmail.com", "homepage": "http://everzet.com" }, { "name": "Pete Otaqui", "email": "pete@otaqui.com", "homepage": "https://github.com/pete-otaqui" } ], "description": "Selenium2 (WebDriver) driver for Mink framework", "homepage": "http://mink.behat.org/", "keywords": [ "ajax", "browser", "javascript", "selenium", "testing", "webdriver" ], "time": "2014-09-29 13:12:12" }, { "name": "doctrine/instantiator", "version": "1.0.4", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f976e5de371104877ebc89bd8fecb0019ed9c119", "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119", "shasum": "" }, "require": { "php": ">=5.3,<8.0-DEV" }, "require-dev": { "athletic/athletic": "~0.1.8", "ext-pdo": "*", "ext-phar": "*", "phpunit/phpunit": "~4.0", "squizlabs/php_codesniffer": "2.0.*@ALPHA" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-0": { "Doctrine\\Instantiator\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Marco Pivetta", "email": "ocramius@gmail.com", "homepage": "http://ocramius.github.com/" } ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", "homepage": "https://github.com/doctrine/instantiator", "keywords": [ "constructor", "instantiate" ], "time": "2014-10-13 12:58:55" }, { "name": "hamcrest/hamcrest-php", "version": "v1.2.2", "source": { "type": "git", "url": "https://github.com/hamcrest/hamcrest-php.git", "reference": "b37020aa976fa52d3de9aa904aa2522dc518f79c" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/b37020aa976fa52d3de9aa904aa2522dc518f79c", "reference": "b37020aa976fa52d3de9aa904aa2522dc518f79c", "shasum": "" }, "require": { "php": ">=5.3.2" }, "replace": { "cordoval/hamcrest-php": "*", "davedevelopment/hamcrest-php": "*", "kodova/hamcrest-php": "*" }, "require-dev": { "phpunit/php-file-iterator": "1.3.3", "satooshi/php-coveralls": "dev-master" }, "type": "library", "autoload": { "classmap": [ "hamcrest" ], "files": [ "hamcrest/Hamcrest.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD" ], "description": "This is the PHP port of Hamcrest Matchers", "keywords": [ "test" ], "time": "2015-05-11 14:41:42" }, { "name": "instaclick/php-webdriver", "version": "1.4.2", "source": { "type": "git", "url": "https://github.com/instaclick/php-webdriver.git", "reference": "6aa16bbc02a5897200ab70316e0d2a01664afc51" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/instaclick/php-webdriver/zipball/6aa16bbc02a5897200ab70316e0d2a01664afc51", "reference": "6aa16bbc02a5897200ab70316e0d2a01664afc51", "shasum": "" }, "require": { "ext-curl": "*", "php": ">=5.3.2" }, "require-dev": { "satooshi/php-coveralls": "dev-master" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.4.x-dev" } }, "autoload": { "psr-0": { "WebDriver": "lib/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "Apache-2.0" ], "authors": [ { "name": "Justin Bishop", "email": "jubishop@gmail.com", "role": "Developer" }, { "name": "Anthon Pang", "email": "apang@softwaredevelopment.ca", "role": "Fork Maintainer" } ], "description": "PHP WebDriver for Selenium 2", "homepage": "http://instaclick.com/", "keywords": [ "browser", "selenium", "webdriver", "webtest" ], "time": "2015-04-05 19:52:55" }, { "name": "mindplay/annotations", - "version": "dev-master", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/php-annotations/php-annotations.git", "reference": "3e6d5ea14fcc032e18dd0f81b014aa7ca47a1881" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-annotations/php-annotations/zipball/3e6d5ea14fcc032e18dd0f81b014aa7ca47a1881", "reference": "3e6d5ea14fcc032e18dd0f81b014aa7ca47a1881", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { "phpunit/php-code-coverage": "~1.2.1", "phpunit/php-file-iterator": ">=1.3.0@stable" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.2.x-dev" } }, "autoload": { "psr-4": { "mindplay\\annotations\\": "src\\annotations" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "LGPL-3.0+" ], "authors": [ { "name": "Rasmus Schultz", "email": "rasmus@mindplay.dk" } ], "description": "Industrial-strength annotations for PHP", "homepage": "http://blog.mindplay.dk/", "keywords": [ "annotations", "framework" ], "time": "2015-04-15 12:54:35" }, { "name": "mockery/mockery", "version": "0.9.4", "source": { "type": "git", "url": "https://github.com/padraic/mockery.git", "reference": "70bba85e4aabc9449626651f48b9018ede04f86b" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/padraic/mockery/zipball/70bba85e4aabc9449626651f48b9018ede04f86b", "reference": "70bba85e4aabc9449626651f48b9018ede04f86b", "shasum": "" }, "require": { "hamcrest/hamcrest-php": "~1.1", "lib-pcre": ">=7.0", "php": ">=5.3.2" }, "require-dev": { "phpunit/phpunit": "~4.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "0.9.x-dev" } }, "autoload": { "psr-0": { "Mockery": "library/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Pádraic Brady", "email": "padraic.brady@gmail.com", "homepage": "http://blog.astrumfutura.com" }, { "name": "Dave Marshall", "email": "dave.marshall@atstsolutions.co.uk", "homepage": "http://davedevelopment.co.uk" } ], "description": "Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succinct API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending.", "homepage": "http://github.com/padraic/mockery", "keywords": [ "BDD", "TDD", "library", "mock", "mock objects", "mockery", "stub", "test", "test double", "testing" ], "time": "2015-04-02 19:54:00" }, { "name": "nikic/php-parser", "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", "reference": "dff239267fd1befa1cd40430c9ed12591aa720ca" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dff239267fd1befa1cd40430c9ed12591aa720ca", "reference": "dff239267fd1befa1cd40430c9ed12591aa720ca", "shasum": "" }, "require": { "ext-tokenizer": "*", "php": ">=5.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.3-dev" } }, "autoload": { "files": [ "lib/bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Nikita Popov" } ], "description": "A PHP parser written in PHP", "keywords": [ "parser", "php" ], "time": "2015-05-02 15:40:40" }, { "name": "phpdocumentor/reflection-docblock", "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { "phpunit/phpunit": "~4.0" }, "suggest": { "dflydev/markdown": "~1.0", "erusev/parsedown": "~1.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0.x-dev" } }, "autoload": { "psr-0": { "phpDocumentor": [ "src/" ] } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Mike van Riel", "email": "mike.vanriel@naenius.com" } ], "time": "2015-02-03 12:10:50" }, { "name": "phpspec/prophecy", "version": "v1.4.1", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", "reference": "3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/phpspec/prophecy/zipball/3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373", "reference": "3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "phpdocumentor/reflection-docblock": "~2.0", "sebastian/comparator": "~1.1" }, "require-dev": { "phpspec/phpspec": "~2.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.4.x-dev" } }, "autoload": { "psr-0": { "Prophecy\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Konstantin Kudryashov", "email": "ever.zet@gmail.com", "homepage": "http://everzet.com" }, { "name": "Marcello Duarte", "email": "marcello.duarte@gmail.com" } ], "description": "Highly opinionated mocking framework for PHP 5.3+", "homepage": "https://github.com/phpspec/prophecy", "keywords": [ "Double", "Dummy", "fake", "mock", "spy", "stub" ], "time": "2015-04-27 22:15:08" }, { "name": "phpunit/php-code-coverage", "version": "2.0.16", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", "reference": "934fd03eb6840508231a7f73eb8940cf32c3b66c" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/934fd03eb6840508231a7f73eb8940cf32c3b66c", "reference": "934fd03eb6840508231a7f73eb8940cf32c3b66c", "shasum": "" }, "require": { "php": ">=5.3.3", "phpunit/php-file-iterator": "~1.3", "phpunit/php-text-template": "~1.2", "phpunit/php-token-stream": "~1.3", "sebastian/environment": "~1.0", "sebastian/version": "~1.0" }, "require-dev": { "ext-xdebug": ">=2.1.4", "phpunit/phpunit": "~4" }, "suggest": { "ext-dom": "*", "ext-xdebug": ">=2.2.1", "ext-xmlwriter": "*" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.0.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sb@sebastian-bergmann.de", "role": "lead" } ], "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", "homepage": "https://github.com/sebastianbergmann/php-code-coverage", "keywords": [ "coverage", "testing", "xunit" ], "time": "2015-04-11 04:35:00" }, { "name": "phpunit/php-file-iterator", "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", "reference": "a923bb15680d0089e2316f7a4af8f437046e96bb" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a923bb15680d0089e2316f7a4af8f437046e96bb", "reference": "a923bb15680d0089e2316f7a4af8f437046e96bb", "shasum": "" }, "require": { "php": ">=5.3.3" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.4.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sb@sebastian-bergmann.de", "role": "lead" } ], "description": "FilterIterator implementation that filters files based on a list of suffixes.", "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", "keywords": [ "filesystem", "iterator" ], "time": "2015-04-02 05:19:05" }, { "name": "phpunit/php-text-template", "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", "shasum": "" }, "require": { "php": ">=5.3.3" }, "type": "library", "autoload": { "classmap": [ "Text/" ] }, "notification-url": "https://packagist.org/downloads/", "include-path": [ "" ], "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sb@sebastian-bergmann.de", "role": "lead" } ], "description": "Simple template engine.", "homepage": "https://github.com/sebastianbergmann/php-text-template/", "keywords": [ "template" ], "time": "2014-01-30 17:20:04" }, { "name": "phpunit/php-timer", "version": "1.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c", "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c", "shasum": "" }, "require": { "php": ">=5.3.3" }, "type": "library", "autoload": { "classmap": [ "PHP/" ] }, "notification-url": "https://packagist.org/downloads/", "include-path": [ "" ], "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sb@sebastian-bergmann.de", "role": "lead" } ], "description": "Utility class for timing", "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ "timer" ], "time": "2013-08-02 07:42:54" }, { "name": "phpunit/php-token-stream", "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", "reference": "eab81d02569310739373308137284e0158424330" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/eab81d02569310739373308137284e0158424330", "reference": "eab81d02569310739373308137284e0158424330", "shasum": "" }, "require": { "ext-tokenizer": "*", "php": ">=5.3.3" }, "require-dev": { "phpunit/phpunit": "~4.2" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.4-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Wrapper around PHP's tokenizer extension.", "homepage": "https://github.com/sebastianbergmann/php-token-stream/", "keywords": [ "tokenizer" ], "time": "2015-04-08 04:46:07" }, { "name": "phpunit/phpunit", "version": "4.6.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", "reference": "3afe303d873a4d64c62ef84de491b97b006fbdac" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3afe303d873a4d64c62ef84de491b97b006fbdac", "reference": "3afe303d873a4d64c62ef84de491b97b006fbdac", "shasum": "" }, "require": { "ext-dom": "*", "ext-json": "*", "ext-pcre": "*", "ext-reflection": "*", "ext-spl": "*", "php": ">=5.3.3", "phpspec/prophecy": "~1.3,>=1.3.1", "phpunit/php-code-coverage": "~2.0,>=2.0.11", "phpunit/php-file-iterator": "~1.4", "phpunit/php-text-template": "~1.2", "phpunit/php-timer": "~1.0", "phpunit/phpunit-mock-objects": "~2.3", "sebastian/comparator": "~1.1", "sebastian/diff": "~1.2", "sebastian/environment": "~1.2", "sebastian/exporter": "~1.2", "sebastian/global-state": "~1.0", "sebastian/version": "~1.0", "symfony/yaml": "~2.1|~3.0" }, "suggest": { "phpunit/php-invoker": "~1.1" }, "bin": [ "phpunit" ], "type": "library", "extra": { "branch-alias": { "dev-master": "4.6.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "The PHP Unit Testing framework.", "homepage": "https://phpunit.de/", "keywords": [ "phpunit", "testing", "xunit" ], "time": "2015-04-29 15:18:52" }, { "name": "phpunit/phpunit-mock-objects", "version": "2.3.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", "reference": "74ffb87f527f24616f72460e54b595f508dccb5c" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/74ffb87f527f24616f72460e54b595f508dccb5c", "reference": "74ffb87f527f24616f72460e54b595f508dccb5c", "shasum": "" }, "require": { "doctrine/instantiator": "~1.0,>=1.0.2", "php": ">=5.3.3", "phpunit/php-text-template": "~1.2" }, "require-dev": { "phpunit/phpunit": "~4.4" }, "suggest": { "ext-soap": "*" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.3.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sb@sebastian-bergmann.de", "role": "lead" } ], "description": "Mock Object library for PHPUnit", "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", "keywords": [ "mock", "xunit" ], "time": "2015-04-02 05:36:41" }, { "name": "pimple/pimple", "version": "v3.0.0", "source": { "type": "git", "url": "https://github.com/silexphp/Pimple.git", "reference": "876bf0899d01feacd2a2e83f04641e51350099ef" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/silexphp/Pimple/zipball/876bf0899d01feacd2a2e83f04641e51350099ef", "reference": "876bf0899d01feacd2a2e83f04641e51350099ef", "shasum": "" }, "require": { "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.0.x-dev" } }, "autoload": { "psr-0": { "Pimple": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" } ], "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", "homepage": "http://pimple.sensiolabs.org", "keywords": [ "container", "dependency injection" ], "time": "2014-07-24 09:48:15" }, { "name": "qa-tools/qa-tools", "version": "v1.0.1", "source": { "type": "git", "url": "https://github.com/qa-tools/qa-tools.git", "reference": "5456f64af5b94c5655f21a592b8817690c2096c7" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/qa-tools/qa-tools/zipball/5456f64af5b94c5655f21a592b8817690c2096c7", "reference": "5456f64af5b94c5655f21a592b8817690c2096c7", "shasum": "" }, "require": { "behat/mink": "~1.6", "mindplay/annotations": "~1.2@dev", "php": ">=5.3.2" }, "replace": { "aik099/qa-tools": "self.version" }, "require-dev": { "aik099/coding-standard": "dev-master", "behat/mink-selenium2-driver": "~1.2@dev", "brianium/paratest": "~0.7", "mockery/mockery": "~0.9" }, "suggest": { "aik099/phpunit-mink": "Allows to use PageObject inside PHPUnit tests and remotely collect code coverage information" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-0": { "QATools\\QATools\\": "./library/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Alexander Obuhovich", "email": "aik.bold@gmail.com" }, { "name": "Dmitry Kushnikov", "email": "dkushnikov@gmail.com" }, { "name": "Michael Geppert", "email": "evangelion1204@aol.com" } ], "description": "Library that provides easy-to-use way of interaction with web-page elements in functional tests using PageObject pattern.", "keywords": [ "BEM", "HtmlElements", "Mink", "PageObject", "acceptance", "functional", "phpunit", "tests" ], "time": "2014-09-26 15:15:51" }, { "name": "sebastian/comparator", "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", "reference": "1dd8869519a225f7f2b9eb663e225298fade819e" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1dd8869519a225f7f2b9eb663e225298fade819e", "reference": "1dd8869519a225f7f2b9eb663e225298fade819e", "shasum": "" }, "require": { "php": ">=5.3.3", "sebastian/diff": "~1.2", "sebastian/exporter": "~1.2" }, "require-dev": { "phpunit/phpunit": "~4.4" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.1.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, { "name": "Volker Dusch", "email": "github@wallbash.com" }, { "name": "Bernhard Schussek", "email": "bschussek@2bepublished.at" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Provides the functionality to compare PHP values for equality", "homepage": "http://www.github.com/sebastianbergmann/comparator", "keywords": [ "comparator", "compare", "equality" ], "time": "2015-01-29 16:28:08" }, { "name": "sebastian/diff", "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/863df9687835c62aa423a22412d26fa2ebde3fd3", "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { "phpunit/phpunit": "~4.2" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.3-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Kore Nordmann", "email": "mail@kore-nordmann.de" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Diff implementation", "homepage": "http://www.github.com/sebastianbergmann/diff", "keywords": [ "diff" ], "time": "2015-02-22 15:13:53" }, { "name": "sebastian/environment", "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", "reference": "5a8c7d31914337b69923db26c4221b81ff5a196e" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5a8c7d31914337b69923db26c4221b81ff5a196e", "reference": "5a8c7d31914337b69923db26c4221b81ff5a196e", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { "phpunit/phpunit": "~4.4" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.3.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Provides functionality to handle HHVM/PHP environments", "homepage": "http://www.github.com/sebastianbergmann/environment", "keywords": [ "Xdebug", "environment", "hhvm" ], "time": "2015-01-01 10:01:08" }, { "name": "sebastian/exporter", "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", "reference": "84839970d05254c73cde183a721c7af13aede943" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/84839970d05254c73cde183a721c7af13aede943", "reference": "84839970d05254c73cde183a721c7af13aede943", "shasum": "" }, "require": { "php": ">=5.3.3", "sebastian/recursion-context": "~1.0" }, "require-dev": { "phpunit/phpunit": "~4.4" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.2.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, { "name": "Volker Dusch", "email": "github@wallbash.com" }, { "name": "Bernhard Schussek", "email": "bschussek@2bepublished.at" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Adam Harvey", "email": "aharvey@php.net" } ], "description": "Provides the functionality to export PHP variables for visualization", "homepage": "http://www.github.com/sebastianbergmann/exporter", "keywords": [ "export", "exporter" ], "time": "2015-01-27 07:23:06" }, { "name": "sebastian/global-state", "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/c7428acdb62ece0a45e6306f1ae85e1c05b09c01", "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { "phpunit/phpunit": "~4.2" }, "suggest": { "ext-uopz": "*" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" } ], "description": "Snapshotting of global state", "homepage": "http://www.github.com/sebastianbergmann/global-state", "keywords": [ "global state" ], "time": "2014-10-06 09:23:50" }, { "name": "sebastian/recursion-context", "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", "reference": "3989662bbb30a29d20d9faa04a846af79b276252" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/3989662bbb30a29d20d9faa04a846af79b276252", "reference": "3989662bbb30a29d20d9faa04a846af79b276252", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { "phpunit/phpunit": "~4.4" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, { "name": "Adam Harvey", "email": "aharvey@php.net" } ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", "time": "2015-01-24 09:48:32" }, { "name": "sebastian/version", "version": "1.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", "reference": "ab931d46cd0d3204a91e1b9a40c4bc13032b58e4" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/ab931d46cd0d3204a91e1b9a40c4bc13032b58e4", "reference": "ab931d46cd0d3204a91e1b9a40c4bc13032b58e4", "shasum": "" }, "type": "library", "autoload": { "classmap": [ "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", "time": "2015-02-24 06:35:25" }, { "name": "symfony/css-selector", "version": "v2.6.7", "target-dir": "Symfony/Component/CssSelector", "source": { "type": "git", "url": "https://github.com/symfony/CssSelector.git", "reference": "189cf0f7f56d7c4be3b778df15a7f16a29f3680d" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/CssSelector/zipball/189cf0f7f56d7c4be3b778df15a7f16a29f3680d", "reference": "189cf0f7f56d7c4be3b778df15a7f16a29f3680d", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { "symfony/phpunit-bridge": "~2.7" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.6-dev" } }, "autoload": { "psr-0": { "Symfony\\Component\\CssSelector\\": "" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Jean-François Simon", "email": "jeanfrancois.simon@sensiolabs.com" }, { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", "time": "2015-05-02 15:18:45" }, { "name": "symfony/event-dispatcher", "version": "v2.6.7", "target-dir": "Symfony/Component/EventDispatcher", "source": { "type": "git", "url": "https://github.com/symfony/EventDispatcher.git", "reference": "672593bc4b0043a0acf91903bb75a1c82d8f2e02" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/672593bc4b0043a0acf91903bb75a1c82d8f2e02", "reference": "672593bc4b0043a0acf91903bb75a1c82d8f2e02", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { "psr/log": "~1.0", "symfony/config": "~2.0,>=2.0.5", "symfony/dependency-injection": "~2.6", "symfony/expression-language": "~2.6", "symfony/phpunit-bridge": "~2.7", "symfony/stopwatch": "~2.3" }, "suggest": { "symfony/dependency-injection": "", "symfony/http-kernel": "" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.6-dev" } }, "autoload": { "psr-0": { "Symfony\\Component\\EventDispatcher\\": "" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", "time": "2015-05-02 15:18:45" }, { "name": "symfony/yaml", "version": "v2.6.7", "target-dir": "Symfony/Component/Yaml", "source": { "type": "git", "url": "https://github.com/symfony/Yaml.git", "reference": "f157ab074e453ecd4c0fa775f721f6e67a99d9e2" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/Yaml/zipball/f157ab074e453ecd4c0fa775f721f6e67a99d9e2", "reference": "f157ab074e453ecd4c0fa775f721f6e67a99d9e2", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { "symfony/phpunit-bridge": "~2.7" }, "type": "library", "extra": { "branch-alias": { "dev-master": "2.6-dev" } }, "autoload": { "psr-0": { "Symfony\\Component\\Yaml\\": "" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", "time": "2015-05-02 15:18:45" } ], "aliases": [], "minimum-stability": "stable", "stability-flags": { "aik099/coding-standard": 20, "mindplay/annotations": 20 }, "prefer-stable": false, "prefer-lowest": false, "platform": [], "platform-dev": [] } Index: branches/5.3.x =================================================================== --- branches/5.3.x (revision 16394) +++ branches/5.3.x (revision 16395) Property changes on: branches/5.3.x ___________________________________________________________________ Modified: bugtraq:logregex ## -1,2 +1 ## -(?:[Bb]ugs?|[Ii]ssues?|[Rr]eports?|[Ff]ixe?s?|[Rr]esolves?)+\s+(?:#?(?:\d+)[,\.\s]*)+ -(\d+) \ No newline at end of property +([A-Z]+\-\d+) \ No newline at end of property Modified: svn:mergeinfo ## -0,0 +0,2 ## Merged /in-portal/branches/5.1.x:r15851 Merged /in-portal/branches/5.2.x:r16196,16198-16199,16201,16203-16205,16212,16231-16233,16235-16236,16241,16243,16245-16247,16249-16251,16259-16264,16266-16268,16270-16274,16276-16279,16286,16288,16290-16291,16293-16300,16304-16309,16313-16314,16317-16318,16338-16339,16347-16348,16350,16352-16359,16362-16363,16367,16373-16374,16376-16377,16379,16388