Page MenuHomeIn-Portal Phabricator

in-portal
No OneTemporary

File Metadata

Created
Sat, Feb 22, 12:56 PM

in-portal

Index: branches/5.3.x/core/units/helpers/menu_helper.php
===================================================================
--- branches/5.3.x/core/units/helpers/menu_helper.php (revision 16380)
+++ branches/5.3.x/core/units/helpers/menu_helper.php (revision 16381)
@@ -1,412 +1,487 @@
<?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 MenuHelper extends kHelper {
/**
* Cached version of site menu
*
* @var Array
* @access protected
*/
protected $Menu = NULL;
/**
* Parent path mapping used in CachedMenu tag
*
* @var Array
* @access protected
*/
protected $parentPaths = Array ();
/**
+ * Extra parameters, that should be available in each menu item (key - param name, value - category field).
+ *
+ * @var array
+ */
+ protected $itemParams = array(
+ // 'filename' => array('Filename'),
+ // 'created_on' => array('CreatedOn', '-- d/m/Y --'),
+ );
+
+ /**
+ * Category dummy for formatting purposes.
+ *
+ * @var CategoriesItem
+ */
+ protected $categoryDummy;
+
+ /**
+ * Set's references to kApplication and DBConnection interface class instances
+ *
+ * @access public
+ */
+ public function __construct()
+ {
+ parent::__construct();
+
+ if ( $this->menuItemsRequireFormatting() ) {
+ $this->categoryDummy = $this->Application->recallObject(
+ 'c.menu-item',
+ null,
+ array('skip_autoload' => true)
+ );
+ }
+ }
+
+ /**
* Builds site menu
*
* @param string $prefix_special
* @param Array $params
*
* @return string
* @access public
*/
public function menuTag($prefix_special, $params)
{
list ($menu, $root_path) = $this->_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;
$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
* @access protected
*/
protected function _prepareMenu()
{
static $root_cat = NULL, $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 . 'Categories
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, CacheSettings::$cmsMenuRebuildTime);
}
else {
$menu = $this->Application->getDBCache('cms_menu', CacheSettings::$cmsMenuRebuildTime);
}
if ( $menu ) {
$menu = unserialize($menu);
$this->parentPaths = $menu['parentPaths'];
}
else {
$menu = $this->_buildMenuStructure($root_cat);
$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
* @access protected
*/
protected function _prepareMenuItem($page, $real_cat_id, $root_path)
{
static $language_id = NULL, $primary_language_id = NULL, $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'));
}*/
if ( substr($page['ItemPath'], 0, 3) == 'id:' ) {
// resolve ID path here, since it can be used directly without m_Link tag (that usually resolves it)
$page['ItemPath'] = $this->Application->getVirtualPageTemplate(substr($page['ItemPath'], 3));
}
$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($this->_removeNonMenuItems($page['sub_items'])) > 0,
'external_url' => $page['UseExternalUrl'] ? $page['ExternalUrl'] : false, // for backward compatibility
'menu_icon' => $page['UseMenuIconUrl'] ? $page['MenuIconUrl'] : false,
);
+ if ( isset($this->categoryDummy) ) {
+ $this->categoryDummy->SetDBFieldsFromHash($page);
+ }
+
+ foreach ( $this->itemParams as $param_name => $category_field_data ) {
+ $category_field_name = $category_field_data[0];
+
+ if ( array_key_exists(1, $category_field_data) ) {
+ $block_params[$param_name] = $this->categoryDummy->GetField(
+ $category_field_name,
+ $category_field_data[1]
+ );
+ }
+ else {
+ $block_params[$param_name] = $page[$category_field_name];
+ }
+ }
+
return $block_params;
}
/**
* Returns only items, that are visible in menu
*
* @param Array $menu
* @return Array
* @access protected
*/
protected 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['Status'] != STATUS_ACTIVE || ($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 of all menu items and their parent categories
*
* @param int $top_category_id
* @return Array
* @access protected
*/
protected function _buildMenuStructure($top_category_id)
{
// 1. get parent paths of leaf categories, that are in menu (across all themes)
$sql = 'SELECT ParentPath, CategoryId
FROM ' . $this->Application->getUnitConfig('c')->getTableName() . '
WHERE IsMenu = 1 AND Status = ' . STATUS_ACTIVE;
$this->parentPaths = $this->Conn->GetCol($sql ,'CategoryId');
// 2. figure out parent paths of all categories in path to leaf categories
foreach ($this->parentPaths as $leaf_parent_path) {
$parent_categories = explode('|', substr($leaf_parent_path, 1, -1));
foreach ($parent_categories as $index => $parent_category_id) {
if ( !isset($this->parentPaths[$parent_category_id]) ) {
$parent_path = array_slice($parent_categories, 0, $index + 1);
$this->parentPaths[$parent_category_id] = '|' . implode('|', $parent_path) . '|';
}
}
}
return $this->_altBuildMenuStructure($top_category_id, implode(',', array_keys($this->parentPaths)));
}
/**
* Builds cache for children of given category (no matter, what menu status is)
*
* @param int $parent_category_id
* @param string $category_limit
* @return Array
* @access protected
*/
protected function _altBuildMenuStructure($parent_category_id, $category_limit = NULL)
{
// Sub-categories from current category
$items = $this->_getSubCategories($parent_category_id, $category_limit);
// sort menu items
uasort($items, Array (&$this, '_menuSort'));
// process sub-menus of each menu
foreach ($items as $key => $menu_item) {
if ( $menu_item['CategoryId'] == $parent_category_id ) {
// don't process myself - prevents recursion
continue;
}
$sub_items = $this->_altBuildMenuStructure($menu_item['CategoryId'], $category_limit);
if ( $sub_items ) {
$items[$key]['sub_items'] = $sub_items;
}
}
return $items;
}
/**
* Returns given category sub-categories
*
* @param int $parent_id
* @param string $category_limit
* @return Array
* @access protected
*/
protected function _getSubCategories($parent_id, $category_limit = NULL)
{
static $items_by_parent = NULL, $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";
}
}
if ( !isset($items_by_parent) ) {
$items_by_parent = Array ();
+ $extra_select_clause = '';
+
+ foreach ( $this->itemParams as $category_field_data ) {
+ $extra_select_clause .= ', c.' . $category_field_data[0];
+ }
// 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,
- c.Status
+ c.Status' . $extra_select_clause . '
FROM ' . TABLE_PREFIX . 'Categories AS c';
if ( isset($category_limit) && $category_limit ) {
$sql .= ' WHERE c.CategoryId IN (' . $category_limit . ')';
}
$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 descending 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
}
+
+ /**
+ * Determines if menu item parameters require formatting.
+ *
+ * @return boolean
+ */
+ protected function menuItemsRequireFormatting()
+ {
+ foreach ( $this->itemParams as $category_field_data ) {
+ if ( array_key_exists(1, $category_field_data) ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
}

Event Timeline