Index: branches/5.2.x/core/units/helpers/menu_helper.php =================================================================== diff -u -N -r14095 -r14244 --- branches/5.2.x/core/units/helpers/menu_helper.php (.../menu_helper.php) (revision 14095) +++ branches/5.2.x/core/units/helpers/menu_helper.php (.../menu_helper.php) (revision 14244) @@ -1,383 +1,383 @@ -_prepareMenu(); - $cat = $this->_getCategoryId($params); - - $parent_path = array_key_exists($cat, $this->parentPaths) ? $this->parentPaths[$cat] : ''; - $parent_path = str_replace($root_path, '', $parent_path); // menu starts from module path - - $levels = explode('|', trim($parent_path, '|')); - - if ($levels[0] === '') { - $levels = Array (); - } - - if (array_key_exists('level', $params) && $params['level'] > count($levels)) { - // current level is deeper, then requested level - return ; - } - - $level = max(array_key_exists('level', $params) ? $params['level'] - 1 : count($levels) - 1, 0); - $parent = array_key_exists($level, $levels) ? $levels[$level] : 0; - - $cur_menu =& $menu; - $menu_path = array_slice($levels, 0, $level + 1); - - foreach ($menu_path as $elem) { - $cur_menu =& $cur_menu['c' . $elem]['sub_items']; - } - - $block_params = $this->prepareTagParams($prefix_special, $params); - $block_params['name'] = $params['render_as']; - - $this->Application->SetVar('cur_parent_path', $parent_path); - $real_cat_id = $this->Application->GetVar('m_cat_id'); - - if (!is_array($cur_menu) || !$cur_menu) { - // no menus on this level - return ''; - } - - $ret = ''; - $cur_item = 1; - $cur_menu = $this->_removeNonMenuItems($cur_menu); - $block_params['total_items'] = count($cur_menu); - - foreach ($cur_menu as $page) { - $block_params = array_merge( - $block_params, - $this->_prepareMenuItem($page, $real_cat_id, $root_path) - ); - - $block_params['is_last'] = $cur_item == $block_params['total_items']; - $block_params['is_first'] = $cur_item == 1; - - // bug #1: this breaks active section highlighting when 2 menu levels are printed on same page (both visible) - // bug #2: people doesn't pass cat_id parameter to m_Link tags in their blocks, so this line helps them; when removed their links will lead to nowhere - $this->Application->SetVar('m_cat_id', $page['CategoryId']); - - $ret .= $this->Application->ParseBlock($block_params); - $cur_item++; - } - - $this->Application->SetVar('m_cat_id', $real_cat_id); - - return $ret; - } - - /** - * Builds cached menu version - * - * @return Array - */ - function _prepareMenu() - { - static $root_cat = null; - static $root_path = null; - - if (!$root_cat) { - $root_cat = $this->Application->getBaseCategory(); - $cache_key = 'parent_paths[%CIDSerial:' . $root_cat . '%]'; - $root_path = $this->Application->getCache($cache_key); - - if ($root_path === false) { - $this->Conn->nextQueryCachable = true; - $sql = 'SELECT ParentPath - FROM ' . TABLE_PREFIX . 'Category - WHERE CategoryId = ' . $root_cat; - $root_path = $this->Conn->GetOne($sql); - $this->Application->setCache($cache_key, $root_path); - } - } - - if (!$this->Menu) { - if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) { - $menu = $this->Application->getCache('master:cms_menu', false); - } - else { - $menu = $this->Application->getDBCache('cms_menu'); - } - - if ($menu) { - $menu = unserialize($menu); - $this->parentPaths = $menu['parentPaths']; - } - else { - $menu = $this->_altBuildMenuStructure(Array ('CategoryId' => $root_cat, 'ParentPath' => $root_path)); - $menu['parentPaths'] = $this->parentPaths; - - if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) { - $this->Application->setCache('master:cms_menu', serialize($menu)); - } - else { - $this->Application->setDBCache('cms_menu', serialize($menu)); - } - } - - unset($menu['parentPaths']); - $this->Menu = $menu; - } - - return Array ($this->Menu, $root_path); - } - - /** - * Returns category id based tag parameters - * - * @param Array $params - * @return int - */ - function _getCategoryId($params) - { - $cat = isset($params['category_id']) && $params['category_id'] != '' ? $params['category_id'] : $this->Application->GetVar('m_cat_id'); - if ("$cat" == 'parent') { - $this_category =& $this->Application->recallObject('c'); - /* @var $this_category kDBItem */ - - $cat = $this_category->GetDBField('ParentId'); - } - elseif ($cat == 0) { - $cat = $this->Application->getBaseCategory(); - } - - return $cat; - } - - /** - * Prepares cms menu item block parameters - * - * @param Array $page - * @param int $real_cat_id - * @param string $root_path - * @return Array - */ - function _prepareMenuItem($page, $real_cat_id, $root_path) - { - static $language_id = null; - static $primary_language_id = null; - static $template = null; - - if (!isset($language_id)) { - $language_id = $this->Application->GetVar('m_lang'); - $primary_language_id = $this->Application->GetDefaultLanguageId(); - $template = $this->Application->GetVar('t'); - } - - $active = $category_active = false; - $title = $page['l' . $language_id . '_ItemName'] ? $page['l' . $language_id . '_ItemName'] : $page['l' . $primary_language_id . '_ItemName']; - - if ($page['ItemType'] == 'cat') { - if (array_key_exists($real_cat_id, $this->parentPaths)) { - $active = strpos($this->parentPaths[$real_cat_id], $page['ParentPath']) !== false; - } - elseif ($page['ItemPath'] == $template) { - // physical template in menu - $active = true; - } - - $category_active = $page['CategoryId'] == $real_cat_id; - } - - /*if ($page['ItemType'] == 'cat_index') { - $check_path = str_replace($root_path, '', $page['ParentPath']); - $active = strpos($parent_path, $check_path) !== false; - } - - if ($page['ItemType'] == 'page') { - $active = $page['ItemPath'] == preg_replace('/^Content\//i', '', $this->Application->GetVar('t')); - }*/ - - $block_params = Array ( - 'title' => $title, - 'template' => $page['ItemPath'], - 'active' => $active, - 'category_active' => $category_active, // new - 'parent_path' => $page['ParentPath'], - 'parent_id' => $page['ParentId'], - 'cat_id' => $page['CategoryId'], - 'item_type' => $page['ItemType'], - 'page_id' => $page['ItemId'], - 'use_section' => ($page['Type'] == PAGE_TYPE_TEMPLATE) && ($page['ItemPath'] != 'index'), - 'has_sub_menu' => isset($page['sub_items']) && count($page['sub_items']) > 0, - 'external_url' => $page['UseExternalUrl'] ? $page['ExternalUrl'] : false, // for backward compatibility - 'menu_icon' => $page['UseMenuIconUrl'] ? $page['MenuIconUrl'] : false, - ); - - return $block_params; - } - - /** - * Returns only items, that are visible in menu - * - * @param Array $menu - * @return Array - */ - function _removeNonMenuItems($menu) - { - $theme_id = $this->Application->GetVar('m_theme'); - - foreach ($menu as $menu_index => $menu_item) { - // $menu_index is in "cN" format, where N is category id - if (!$menu_item['IsMenu'] || ($menu_item['ThemeId'] != $theme_id && $menu_item['ThemeId'] != 0)) { - // don't show sections, that are not from menu OR system templates from other themes - unset($menu[$menu_index]); - } - } - - return $menu; - } - - /** - * Builds cache for children of given category (no matter, what menu status is) - * - * @param Array $parent - * @return Array - */ - function _altBuildMenuStructure($parent) - { - static $lang_part = null; - - if (!isset($lang_part)) { - $ml_helper =& $this->Application->recallObject('kMultiLanguageHelper'); - /* @var $ml_helper kMultiLanguageHelper */ - - $lang_part = ''; - $languages = $ml_helper->getLanguages(); - - foreach ($languages as $language_id) { - $lang_part .= 'c.l' . $language_id . '_MenuTitle AS l' . $language_id . '_ItemName,' . "\n"; - } - } - - // Sub-categories from current category - $items = $this->getSubCategories( $parent['CategoryId'] ); - - // sort menu items - uasort($items, Array (&$this, '_menuSort')); - - // store menu items - $the_items = Array(); - foreach ($items as $index => $an_item) { - $the_items[ $an_item['ItemId'] ] = $an_item; - - $this->parentPaths[ $an_item['CategoryId'] ] = $an_item['ParentPath']; - } - - // process submenus of each menu - $items = $the_items; - foreach ($items as $key => $menu_item) { - if ($menu_item['CategoryId'] == $parent['CategoryId']) { - // don't process myself - prevents recursion - continue; - } - - $sub_items = $this->_altBuildMenuStructure($menu_item); - - if ($sub_items) { - $items[$key]['sub_items'] = $sub_items; - } - } - - return $items; - } - - function getSubCategories($parent_id) - { - static $items_by_parent = null; - - if (!isset($items_by_parent)) { - $ml_helper =& $this->Application->recallObject('kMultiLanguageHelper'); - /* @var $ml_helper kMultiLanguageHelper */ - - $lang_part = ''; - $items_by_parent = Array (); - $languages = $ml_helper->getLanguages(); - - foreach ($languages as $language_id) { - $lang_part .= 'c.l' . $language_id . '_MenuTitle AS l' . $language_id . '_ItemName,' . "\n"; - } - - // Sub-categories from current category - $sql = 'SELECT - c.CategoryId AS CategoryId, - CONCAT(\'c\', c.CategoryId) AS ItemId, - c.Priority AS ItemPriority, - ' . $lang_part . ' - - IF(c.`Type` = ' . PAGE_TYPE_TEMPLATE . ', c.Template, CONCAT("id:", c.CategoryId)) AS ItemPath, - c.ParentPath AS ParentPath, - c.ParentId As ParentId, - \'cat\' AS ItemType, - c.IsMenu, c.Type, c.ThemeId, c.UseExternalUrl, c.ExternalUrl, c.UseMenuIconUrl, c.MenuIconUrl - FROM ' . TABLE_PREFIX . 'Category AS c - WHERE c.Status = ' . STATUS_ACTIVE; - $items = $this->Conn->Query($sql, 'ItemId'); - - foreach ($items as $item_id => $item_data) { - $item_parent_id = $item_data['ParentId']; - - if ( !array_key_exists($item_parent_id, $items_by_parent) ) { - $items_by_parent[$item_parent_id] = Array (); - } - - $items_by_parent[$item_parent_id][$item_id] = $item_data; - } - } - - return array_key_exists($parent_id, $items_by_parent) ? $items_by_parent[$parent_id] : Array (); - } - - /** - * Method for sorting pages by priority in decending order - * - * @param Array $a - * @param Array $b - * @return int - */ - function _menuSort($a, $b) - { - if ($a['ItemPriority'] == $b['ItemPriority']) { - return 0; - } - - return ($a['ItemPriority'] < $b['ItemPriority']) ? 1 : -1; //descending - } - } +_prepareMenu(); + $cat = $this->_getCategoryId($params); + + $parent_path = array_key_exists($cat, $this->parentPaths) ? $this->parentPaths[$cat] : ''; + $parent_path = str_replace($root_path, '', $parent_path); // menu starts from module path + + $levels = explode('|', trim($parent_path, '|')); + + if ($levels[0] === '') { + $levels = Array (); + } + + if (array_key_exists('level', $params) && $params['level'] > count($levels)) { + // current level is deeper, then requested level + return ; + } + + $level = max(array_key_exists('level', $params) ? $params['level'] - 1 : count($levels) - 1, 0); + $parent = array_key_exists($level, $levels) ? $levels[$level] : 0; + + $cur_menu =& $menu; + $menu_path = array_slice($levels, 0, $level + 1); + + foreach ($menu_path as $elem) { + $cur_menu =& $cur_menu['c' . $elem]['sub_items']; + } + + $block_params = $this->prepareTagParams($prefix_special, $params); + $block_params['name'] = $params['render_as']; + + $this->Application->SetVar('cur_parent_path', $parent_path); + $real_cat_id = $this->Application->GetVar('m_cat_id'); + + if (!is_array($cur_menu) || !$cur_menu) { + // no menus on this level + return ''; + } + + $ret = ''; + $cur_item = 1; + $cur_menu = $this->_removeNonMenuItems($cur_menu); + $block_params['total_items'] = count($cur_menu); + + foreach ($cur_menu as $page) { + $block_params = array_merge( + $block_params, + $this->_prepareMenuItem($page, $real_cat_id, $root_path) + ); + + $block_params['is_last'] = $cur_item == $block_params['total_items']; + $block_params['is_first'] = $cur_item == 1; + + // bug #1: this breaks active section highlighting when 2 menu levels are printed on same page (both visible) + // bug #2: people doesn't pass cat_id parameter to m_Link tags in their blocks, so this line helps them; when removed their links will lead to nowhere + $this->Application->SetVar('m_cat_id', $page['CategoryId']); + + $ret .= $this->Application->ParseBlock($block_params); + $cur_item++; + } + + $this->Application->SetVar('m_cat_id', $real_cat_id); + + return $ret; + } + + /** + * Builds cached menu version + * + * @return Array + */ + function _prepareMenu() + { + static $root_cat = null; + static $root_path = null; + + if (!$root_cat) { + $root_cat = $this->Application->getBaseCategory(); + $cache_key = 'parent_paths[%CIDSerial:' . $root_cat . '%]'; + $root_path = $this->Application->getCache($cache_key); + + if ($root_path === false) { + $this->Conn->nextQueryCachable = true; + $sql = 'SELECT ParentPath + FROM ' . TABLE_PREFIX . 'Category + WHERE CategoryId = ' . $root_cat; + $root_path = $this->Conn->GetOne($sql); + $this->Application->setCache($cache_key, $root_path); + } + } + + if (!$this->Menu) { + if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) { + $menu = $this->Application->getCache('master:cms_menu', false); + } + else { + $menu = $this->Application->getDBCache('cms_menu'); + } + + if ($menu) { + $menu = unserialize($menu); + $this->parentPaths = $menu['parentPaths']; + } + else { + $menu = $this->_altBuildMenuStructure(Array ('CategoryId' => $root_cat, 'ParentPath' => $root_path)); + $menu['parentPaths'] = $this->parentPaths; + + if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) { + $this->Application->setCache('master:cms_menu', serialize($menu)); + } + else { + $this->Application->setDBCache('cms_menu', serialize($menu)); + } + } + + unset($menu['parentPaths']); + $this->Menu = $menu; + } + + return Array ($this->Menu, $root_path); + } + + /** + * Returns category id based tag parameters + * + * @param Array $params + * @return int + */ + function _getCategoryId($params) + { + $cat = isset($params['category_id']) && $params['category_id'] != '' ? $params['category_id'] : $this->Application->GetVar('m_cat_id'); + if ("$cat" == 'parent') { + $this_category =& $this->Application->recallObject('c'); + /* @var $this_category kDBItem */ + + $cat = $this_category->GetDBField('ParentId'); + } + elseif ($cat == 0) { + $cat = $this->Application->getBaseCategory(); + } + + return $cat; + } + + /** + * Prepares cms menu item block parameters + * + * @param Array $page + * @param int $real_cat_id + * @param string $root_path + * @return Array + */ + function _prepareMenuItem($page, $real_cat_id, $root_path) + { + static $language_id = null; + static $primary_language_id = null; + static $template = null; + + if (!isset($language_id)) { + $language_id = $this->Application->GetVar('m_lang'); + $primary_language_id = $this->Application->GetDefaultLanguageId(); + $template = $this->Application->GetVar('t'); + } + + $active = $category_active = false; + $title = $page['l' . $language_id . '_ItemName'] ? $page['l' . $language_id . '_ItemName'] : $page['l' . $primary_language_id . '_ItemName']; + + if ($page['ItemType'] == 'cat') { + if (array_key_exists($real_cat_id, $this->parentPaths)) { + $active = strpos($this->parentPaths[$real_cat_id], $page['ParentPath']) !== false; + } + elseif ($page['ItemPath'] == $template) { + // physical template in menu + $active = true; + } + + $category_active = $page['CategoryId'] == $real_cat_id; + } + + /*if ($page['ItemType'] == 'cat_index') { + $check_path = str_replace($root_path, '', $page['ParentPath']); + $active = strpos($parent_path, $check_path) !== false; + } + + if ($page['ItemType'] == 'page') { + $active = $page['ItemPath'] == preg_replace('/^Content\//i', '', $this->Application->GetVar('t')); + }*/ + + $block_params = Array ( + 'title' => $title, + 'template' => $page['ItemPath'], + 'active' => $active, + 'category_active' => $category_active, // new + 'parent_path' => $page['ParentPath'], + 'parent_id' => $page['ParentId'], + 'cat_id' => $page['CategoryId'], + 'item_type' => $page['ItemType'], + 'page_id' => $page['ItemId'], + 'use_section' => ($page['Type'] == PAGE_TYPE_TEMPLATE) && ($page['ItemPath'] != 'index'), + 'has_sub_menu' => isset($page['sub_items']) && count($page['sub_items']) > 0, + 'external_url' => $page['UseExternalUrl'] ? $page['ExternalUrl'] : false, // for backward compatibility + 'menu_icon' => $page['UseMenuIconUrl'] ? $page['MenuIconUrl'] : false, + ); + + return $block_params; + } + + /** + * Returns only items, that are visible in menu + * + * @param Array $menu + * @return Array + */ + function _removeNonMenuItems($menu) + { + $theme_id = $this->Application->GetVar('m_theme'); + + foreach ($menu as $menu_index => $menu_item) { + // $menu_index is in "cN" format, where N is category id + if (!$menu_item['IsMenu'] || ($menu_item['ThemeId'] != $theme_id && $menu_item['ThemeId'] != 0)) { + // don't show sections, that are not from menu OR system templates from other themes + unset($menu[$menu_index]); + } + } + + return $menu; + } + + /** + * Builds cache for children of given category (no matter, what menu status is) + * + * @param Array $parent + * @return Array + */ + function _altBuildMenuStructure($parent) + { + static $lang_part = null; + + if (!isset($lang_part)) { + $ml_helper =& $this->Application->recallObject('kMultiLanguageHelper'); + /* @var $ml_helper kMultiLanguageHelper */ + + $lang_part = ''; + $languages = $ml_helper->getLanguages(); + + foreach ($languages as $language_id) { + $lang_part .= 'c.l' . $language_id . '_MenuTitle AS l' . $language_id . '_ItemName,' . "\n"; + } + } + + // Sub-categories from current category + $items = $this->getSubCategories( $parent['CategoryId'] ); + + // sort menu items + uasort($items, Array (&$this, '_menuSort')); + + // store menu items + $the_items = Array(); + foreach ($items as $index => $an_item) { + $the_items[ $an_item['ItemId'] ] = $an_item; + + $this->parentPaths[ $an_item['CategoryId'] ] = $an_item['ParentPath']; + } + + // process submenus of each menu + $items = $the_items; + foreach ($items as $key => $menu_item) { + if ($menu_item['CategoryId'] == $parent['CategoryId']) { + // don't process myself - prevents recursion + continue; + } + + $sub_items = $this->_altBuildMenuStructure($menu_item); + + if ($sub_items) { + $items[$key]['sub_items'] = $sub_items; + } + } + + return $items; + } + + function getSubCategories($parent_id) + { + static $items_by_parent = null; + + if (!isset($items_by_parent)) { + $ml_helper =& $this->Application->recallObject('kMultiLanguageHelper'); + /* @var $ml_helper kMultiLanguageHelper */ + + $lang_part = ''; + $items_by_parent = Array (); + $languages = $ml_helper->getLanguages(); + + foreach ($languages as $language_id) { + $lang_part .= 'c.l' . $language_id . '_MenuTitle AS l' . $language_id . '_ItemName,' . "\n"; + } + + // Sub-categories from current category + $sql = 'SELECT + c.CategoryId AS CategoryId, + CONCAT(\'c\', c.CategoryId) AS ItemId, + c.Priority AS ItemPriority, + ' . $lang_part . ' + + IF(c.`Type` = ' . PAGE_TYPE_TEMPLATE . ', c.Template, CONCAT("id:", c.CategoryId)) AS ItemPath, + c.ParentPath AS ParentPath, + c.ParentId As ParentId, + \'cat\' AS ItemType, + c.IsMenu, c.Type, c.ThemeId, c.UseExternalUrl, c.ExternalUrl, c.UseMenuIconUrl, c.MenuIconUrl + FROM ' . TABLE_PREFIX . 'Category AS c + WHERE c.Status = ' . STATUS_ACTIVE; + $items = $this->Conn->Query($sql, 'ItemId'); + + foreach ($items as $item_id => $item_data) { + $item_parent_id = $item_data['ParentId']; + + if ( !array_key_exists($item_parent_id, $items_by_parent) ) { + $items_by_parent[$item_parent_id] = Array (); + } + + $items_by_parent[$item_parent_id][$item_id] = $item_data; + } + } + + return array_key_exists($parent_id, $items_by_parent) ? $items_by_parent[$parent_id] : Array (); + } + + /** + * Method for sorting pages by priority in decending order + * + * @param Array $a + * @param Array $b + * @return int + */ + function _menuSort($a, $b) + { + if ($a['ItemPriority'] == $b['ItemPriority']) { + return 0; + } + + return ($a['ItemPriority'] < $b['ItemPriority']) ? 1 : -1; //descending + } + }