AntonLV hace 5 años
padre
commit
1ac6045911

+ 4 - 1
inc/classes/BxDolMenu.php

@@ -91,7 +91,8 @@ class BxDolMenu extends BxDolFactory implements iBxDolFactoryObject, iBxDolRepla
     protected $_aObject;
     protected $_oQuery;
     protected $_oPermalinks;
-    protected $_aMarkers = array ();
+    protected $_aMarkers = array();
+    protected $_bMultilevel = false;
 
     /**
      * Constructor
@@ -109,6 +110,8 @@ class BxDolMenu extends BxDolFactory implements iBxDolFactoryObject, iBxDolRepla
         $this->_oQuery = new BxDolMenuQuery($this->_aObject);
         $this->_oPermalinks = BxDolPermalinks::getInstance();
 
+        $this->_bMultilevel = $this->_oQuery->isSetMultilevel($this->_aObject['set_name']);
+
         if (isLogged()) {
             $oProfile = BxDolProfile::getInstance();
             if ($oProfile) {

+ 64 - 6
inc/classes/BxDolMenuQuery.php

@@ -77,11 +77,45 @@ class BxDolMenuQuery extends BxDolDb
     {
         return $this->getMenuItemsFromSet($this->_aObject['set_name']);
     }
+    
+    public function getMenuItemsHierarchy()
+    {
+        $aWithSubitems = $this->getMenuItemsBy(array(
+            'type' => 'with_subitems', 
+            'set_name' => $this->_aObject['set_name']
+        ));
+
+        $aResult = array();
+        $this->_getMenuItemsHierarchy($this->_aObject['set_name'], 0, $aWithSubitems, $aResult);
+
+        return $aResult;
+    }
+
+    public function _getMenuItemsHierarchy($sSet, $iParentId, $aWithSubitems, &$aResult)
+    {
+        $aItems = $this->getMenuItemsFromSet($sSet, $iParentId);
+        foreach($aItems as $sItem => $aItem) {
+            $aResult[$sItem] = $aItem;
 
-    public function getMenuItemsFromSet($sSetName)
+            if(!in_array($sItem, $aWithSubitems))
+                continue;
+
+            $this->_getMenuItemsHierarchy($sSet, $aItem['id'], $aWithSubitems, $aResult[$sItem]['subitems']);
+        }
+    }
+
+    public function getMenuItemsFromSet($sSetName, $iParentId = false)
     {
-        $sQuery = $this->prepare("SELECT * FROM `sys_menu_items` WHERE `set_name` = ? ORDER BY `order` ASC", $sSetName);
-        return $this->getAllWithKey($sQuery, 'name');
+        $aBindings = array('set_name' => $sSetName);
+        $sWhereClause = " AND `set_name` = :set_name";
+        
+        if($iParentId !== false) {
+            $aBindings['parent_id'] = $iParentId;
+
+            $sWhereClause .= " AND `parent_id` = :parent_id";
+        }
+
+        return $this->getAllWithKey("SELECT * FROM `sys_menu_items` WHERE 1" . $sWhereClause . " ORDER BY `order` ASC", 'name', $aBindings);
     }
 
     public function getMenuItemsBy($aParams = array())
@@ -90,7 +124,7 @@ class BxDolMenuQuery extends BxDolDb
         $aBindings = array();
 
     	$sSelectClause = '*';
-    	$sWhereClause = $sGroupClause = $sOrderClause = '';
+    	$sJoinClause = $sWhereClause = $sGroupClause = $sOrderClause = '';
     	$sLimitClause = isset($aParams['start']) && !empty($aParams['per_page']) ? " LIMIT " . $aParams['start'] . ", " . $aParams['per_page'] : "";
 
     	if(!empty($aParams['type']))
@@ -101,7 +135,7 @@ class BxDolMenuQuery extends BxDolDb
                     $sWhereClause = 'AND `set_name` = :set_name';
                     $sOrderClause = '`order` ASC';
                     break;
-                    
+
                 case 'set_name_duplicates':
                     $aMethod['name'] = 'getColumn';
                     $aBindings['set_name'] = $aParams['set_name'];
@@ -110,6 +144,24 @@ class BxDolMenuQuery extends BxDolDb
                     $sWhereClause = 'AND `set_name` = :set_name';
                     $sGroupClause = '`name` HAVING COUNT(`id`) > 1';
                     break;
+
+                case 'with_subitems':
+                    $aMethod['name'] = 'getColumn';
+                    $aBindings['set_name'] = $aParams['set_name'];
+
+                    $sSelectClause = 'DISTINCT `ti`.`name`';
+                    $sJoinClause = 'AS `ti` INNER JOIN `sys_menu_items` AS `tsi` ON `ti`.`id`=`tsi`.`parent_id`';
+                    $sWhereClause = 'AND `ti`.`set_name` = :set_name';
+                    break;
+
+                case 'subitems':
+                    $aMethod['name'] = 'getAllWithKey';
+                    $aMethod['params'][1] = 'name';
+                    $aBindings['parent_id'] = $aParams['parent_id'];
+
+                    $sWhereClause = 'AND `parent_id` = :parent_id';
+                    $sOrderClause = '`order` ASC';
+                    break;
             }
 
         if(!empty($sGroupClause))
@@ -118,7 +170,7 @@ class BxDolMenuQuery extends BxDolDb
         if(!empty($sOrderClause))
             $sOrderClause = " ORDER BY " . $sOrderClause;
 
-        $aMethod['params'][0] = "SELECT " . $sSelectClause . " FROM `sys_menu_items` WHERE 1 " . $sWhereClause . $sGroupClause . $sOrderClause . $sLimitClause;
+        $aMethod['params'][0] = "SELECT " . $sSelectClause . " FROM `sys_menu_items` " . $sJoinClause . " WHERE 1 " . $sWhereClause . $sGroupClause . $sOrderClause . $sLimitClause;
         $aMethod['params'][] = $aBindings;
 
         return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
@@ -129,6 +181,12 @@ class BxDolMenuQuery extends BxDolDb
         $sSql = $this->prepare("SELECT * FROM `sys_menu_templates` WHERE `id`=? LIMIT 1", $iId);
         return $bFromCache ? $this->fromMemory('sys_menu_templates_' . $iId, 'getRow', $sSql) : $this->getRow($sSql);
     }
+
+    public function isSetMultilevel($sName)
+    {
+        $sSql = $this->prepare("SELECT COUNT(*) FROM `sys_menu_items` WHERE `set_name`=? AND `parent_id`<>'0' LIMIT 1", $sName);
+        return (int)$this->getOne($sSql) > 0;
+    }
 }
 
 /** @} */

+ 1 - 0
install/sql/system.sql

@@ -4154,6 +4154,7 @@ INSERT INTO `sys_menu_sets` (`set_name`, `module`, `title`, `deletable`) VALUES
 
 CREATE TABLE IF NOT EXISTS `sys_menu_items` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
+  `parent_id` int(11) NOT NULL DEFAULT '0',
   `set_name` varchar(64) NOT NULL,
   `module` varchar(32) NOT NULL,
   `name` varchar(255) NOT NULL,

+ 5 - 1
modules/boonex/english/data/langs/system/en.xml

@@ -1141,7 +1141,8 @@
 	<string name="_adm_nav_dsc_items_icon_image_current"><![CDATA[No icon uploaded. Text is used in menu item.]]></string>
 	<string name="_adm_nav_dsc_items_link"><![CDATA[Enter the link URL]]></string>
 	<string name="_adm_nav_dsc_items_onclick"><![CDATA[Enter a correct JavaScript code for the link]]></string>
-	<string name="_adm_nav_dsc_items_submenu"><![CDATA[Select if you want the link to have a submenu.]]></string>
+        <string name="_adm_nav_dsc_items_parent_id"><![CDATA[Use it to create multilevel menu inside one menu. Leave empty for one level menus.]]></string>
+	<string name="_adm_nav_dsc_items_submenu"><![CDATA[Select if you want the link to use another menu as submenu.]]></string>
 	<string name="_adm_nav_dsc_items_target"><![CDATA[Select if you want the link to open as a new page or within current page.]]></string>
 	<string name="_adm_nav_dsc_items_title"><![CDATA[Link name displayed in site menus. Use at least 3 characters.]]></string>
 	<string name="_adm_nav_dsc_items_title_system"><![CDATA[Item name, displayed in Studio only.]]></string>
@@ -1182,6 +1183,7 @@
 	<string name="_adm_nav_txt_items_gl_link"><![CDATA[URL]]></string>
 	<string name="_adm_nav_txt_items_gl_link_custom"><![CDATA[Custom Action]]></string>
 	<string name="_adm_nav_txt_items_gl_link_menu"><![CDATA[Menu:]]></string>
+        <string name="_adm_nav_txt_items_gl_link_subitems"><![CDATA[Subitems:]]></string>
 	<string name="_adm_nav_txt_items_gl_module"><![CDATA[Module]]></string>
 	<string name="_adm_nav_txt_items_gl_title_system"><![CDATA[System Name]]></string>
 	<string name="_adm_nav_txt_items_gl_visible"><![CDATA[Visible For]]></string>
@@ -1192,6 +1194,8 @@
 	<string name="_adm_nav_txt_items_import_popup"><![CDATA[Import Items]]></string>
 	<string name="_adm_nav_txt_items_link"><![CDATA[URL]]></string>
 	<string name="_adm_nav_txt_items_onclick"><![CDATA[OnClick]]></string>
+        <string name="_adm_nav_txt_items_parent_id"><![CDATA[Parent Item]]></string>
+        <string name="_adm_nav_txt_items_parent_id_empty"><![CDATA[No Parent Item]]></string>
 	<string name="_adm_nav_txt_items_show_to_popup"><![CDATA[Show "{0}" menu item to: ]]></string>
 	<string name="_adm_nav_txt_items_submenu"><![CDATA[Submenu]]></string>
 	<string name="_adm_nav_txt_items_submenu_empty"><![CDATA[No Submenu]]></string>

+ 5 - 1
modules/boonex/russian/data/langs/system/ru.xml

@@ -1127,7 +1127,8 @@
 	<string name="_adm_nav_dsc_items_icon_image_current"><![CDATA[Картинка не загружена. В элементе меню будет использован текст.]]></string>
 	<string name="_adm_nav_dsc_items_link"><![CDATA[Введите URL ссылки]]></string>
 	<string name="_adm_nav_dsc_items_onclick"><![CDATA[Введите корректный JavaScript код для ссылки]]></string>
-	<string name="_adm_nav_dsc_items_submenu"><![CDATA[Выберите, если у ссылки должно быть подменю.]]></string>
+        <string name="_adm_nav_dsc_items_parent_id"><![CDATA[Используйте для создания многоуровневого меню внутри одного меню. Оставьте пустым для одноуровневых меню.]]></string>
+	<string name="_adm_nav_dsc_items_submenu"><![CDATA[Выберите, если пункт меню должен использовать другое меню в качестве подменю.]]></string>
 	<string name="_adm_nav_dsc_items_target"><![CDATA[Выберите, если ссылка должна открываться на новой или текущей странице.]]></string>
 	<string name="_adm_nav_dsc_items_title"><![CDATA[Название ссылки для отображения в меню сайта. Используйте как минимум 3 символа.]]></string>
 	<string name="_adm_nav_dsc_items_title_system"><![CDATA[Имя элемента, отображаемое только в Студии.]]></string>
@@ -1168,6 +1169,7 @@
 	<string name="_adm_nav_txt_items_gl_link"><![CDATA[URL]]></string>
 	<string name="_adm_nav_txt_items_gl_link_custom"><![CDATA[Индивидуальное действие]]></string>
 	<string name="_adm_nav_txt_items_gl_link_menu"><![CDATA[Меню:]]></string>
+        <string name="_adm_nav_txt_items_gl_link_subitems"><![CDATA[Подпункты:]]></string>
 	<string name="_adm_nav_txt_items_gl_module"><![CDATA[Модуль]]></string>
 	<string name="_adm_nav_txt_items_gl_title_system"><![CDATA[Системное имя]]></string>
 	<string name="_adm_nav_txt_items_gl_visible"><![CDATA[Видимо для]]></string>
@@ -1178,6 +1180,8 @@
 	<string name="_adm_nav_txt_items_import_popup"><![CDATA[Импортировать элементы]]></string>
 	<string name="_adm_nav_txt_items_link"><![CDATA[URL]]></string>
 	<string name="_adm_nav_txt_items_onclick"><![CDATA[OnClick]]></string>
+        <string name="_adm_nav_txt_items_parent_id"><![CDATA[Родительский пункт]]></string>
+        <string name="_adm_nav_txt_items_parent_id_empty"><![CDATA[Нет родительского пункта]]></string>
 	<string name="_adm_nav_txt_items_show_to_popup"><![CDATA[Отображать "{0}" элемент меню для: ]]></string>
 	<string name="_adm_nav_txt_items_submenu"><![CDATA[Подменю]]></string>
 	<string name="_adm_nav_txt_items_submenu_empty"><![CDATA[Без подменю]]></string>

+ 19 - 2
studio/classes/BxDolStudioNavigationQuery.php

@@ -203,6 +203,14 @@ class BxDolStudioNavigationQuery extends BxDolDb
         return (int)$this->getOne("SELECT FOUND_ROWS()");
     }
 
+    function isParentItem($iItemId)
+    {
+        $aItems = array();
+        $this->getItems(array('type' => 'by_parent_id', 'value' => $iItemId), $aItems, false);
+
+        return !empty($aItems) && is_array($aItems);
+    }
+
     function getItems($aParams, &$aItems, $bReturnCount = true)
     {
         $aMethod = array('name' => 'getAll', 'params' => array(0 => 'query'));
@@ -215,15 +223,23 @@ class BxDolStudioNavigationQuery extends BxDolDb
             case 'by_id':
                 $aMethod['name'] = 'getRow';
                 $aMethod['params'][1] = array(
-                	'id' => $aParams['value']
+                    'id' => $aParams['value']
                 );
 
                 $sWhereClause = " AND `tmi`.`id`=:id ";
                 break;
 
+            case 'by_parent_id':
+                $aMethod['params'][1] = array(
+                    'parent_id' => $aParams['value']
+                );
+
+                $sWhereClause = " AND `tmi`.`parent_id`=:parent_id ";
+                break;
+
             case 'by_set_name':
             	$aMethod['params'][1] = array(
-                	'set_name' => $aParams['value']
+                    'set_name' => $aParams['value']
                 );
 
                 $sWhereClause = " AND `tmi`.`set_name`=:set_name ";
@@ -251,6 +267,7 @@ class BxDolStudioNavigationQuery extends BxDolDb
 
         $aMethod['params'][0] = "SELECT " . ($bReturnCount ? "SQL_CALC_FOUND_ROWS" : "") . "
                 `tmi`.`id` AS `id`,
+                `tmi`.`parent_id` AS `parent_id`,
                 `tmi`.`set_name` AS `set_name`,
                 `tmi`.`module` AS `module`,
                 `tmi`.`name` AS `name`,

+ 39 - 2
studio/template/scripts/BxBaseStudioNavigationItems.php

@@ -421,7 +421,23 @@ class BxBaseStudioNavigationItems extends BxDolStudioNavigationItems
 
     protected function _getCellLink ($mixedValue, $sKey, $aField, $aRow)
     {
-        if($aRow['submenu_object'] != "") {
+        $aSubitems = array();
+        $this->oDb->getItems(array('type' => 'by_parent_id', 'value' => $aRow['id']), $aSubitems, false);
+
+        if(!empty($aSubitems) && is_array($aSubitems)) {
+            $sPrefix = _t('_adm_nav_txt_items_gl_link_subitems');
+
+            $aField['chars_limit'] -= strlen($sPrefix);
+
+            $aTitles = array();
+            foreach($aSubitems as $aSubitem)
+                $aTitles[] = _t(!empty($aSubitem['title_system']) ? $aSubitem['title_system'] : $aSubitem['title']);
+
+            $aValue = $this->_limitMaxLength(implode(', ', $aTitles), $sKey, $aField, $aRow, $this->_isDisplayPopupOnTextOverflow, false);
+
+            $mixedValue = $sPrefix . ' ' . $aValue[0] . (isset($aValue[1]) ? $aValue[1] : '');
+        }
+        else if($aRow['submenu_object'] != "") {
             $aMenu = array();
             $this->oDb->getMenus(array('type' => 'by_object', 'value' => $aRow['submenu_object']), $aMenu, false);
 
@@ -438,7 +454,8 @@ class BxBaseStudioNavigationItems extends BxDolStudioNavigationItems
             }
             else 
                 $mixedValue = $sPrefix . ' ' . _t('_undefined');
-        } else if($aRow['submenu_object'] == "" && $aRow['onclick'] != "")
+        } 
+        else if($aRow['submenu_object'] == "" && $aRow['onclick'] != "")
             $mixedValue = $this->_limitMaxLength(_t('_adm_nav_txt_items_gl_link_custom'), $sKey, $aField, $aRow, $this->_isDisplayPopupOnTextOverflow);
         else
             $mixedValue = BxDolPermalinks::getInstance()->permalink($mixedValue);
@@ -595,6 +612,21 @@ class BxBaseStudioNavigationItems extends BxDolStudioNavigationItems
                         'pass' => 'Xss',
                     ),
                 ),
+                'parent_id' => array(
+                    'type' => 'select',
+                    'name' => 'parent_id',
+                    'caption' => _t('_adm_nav_txt_items_parent_id'),
+                    'info' => _t('_adm_nav_dsc_items_parent_id'),
+                    'value' => isset($aItem['parent_id']) ? $aItem['parent_id'] : '',
+                    'values' => array(
+                        0 => _t('_adm_nav_txt_items_parent_id_empty')
+                    ),
+                    'required' => '0',
+                    'attrs' => array(),
+                    'db' => array (
+                        'pass' => 'Int',
+                    ),
+                ),
                 'submenu_object' => array(
                     'type' => 'select',
                     'name' => 'submenu_object',
@@ -741,6 +773,11 @@ class BxBaseStudioNavigationItems extends BxDolStudioNavigationItems
                 )
             )
         );
+        
+        $aItems = array();
+        $this->oDb->getItems(array('type' => 'by_set_name', 'value' => $this->sSet), $aItems, false);
+        foreach($aItems as $aItem)
+            $aForm['inputs']['parent_id']['values'][$aItem['id']] = _t(!empty($aItem['title_system']) ? $aItem['title_system'] : $aItem['title']);
 
         $aMenus = array();
         $this->oDb->getMenus(array('type' => 'all'), $aMenus, false);

+ 26 - 1
template/scripts/BxBaseMenu.php

@@ -136,7 +136,10 @@ class BxBaseMenu extends BxDolMenu
      */
     protected function getMenuItemsRaw ()
     {
-        return $this->_oQuery->getMenuItems();
+        if($this->_bMultilevel)
+            return $this->_oQuery->getMenuItemsHierarchy();
+        else 
+            return $this->_oQuery->getMenuItems();
     }
 
     protected function _getMenuItem ($a)
@@ -226,6 +229,28 @@ class BxBaseMenu extends BxDolMenu
             'content' => $aTmplVarsAddon
         );
 
+        $aTmplVarsSubitems = array('subitems' => '');
+        $bTmplVarsSubitems = $this->_bMultilevel && !empty($a['subitems']);
+        if($bTmplVarsSubitems) {
+            $aSubitems = array();
+            foreach($a['subitems'] as $aSubitem) {
+                $aSubitem = $this->_getMenuItem($aSubitem);
+                if($aSubitem !== false)
+                    $aSubitems[] = $aSubitem;
+            }
+
+            $aTmplVarsSubitems['subitems'] = $this->_oTemplate->parseHtmlByName(str_replace('.html', '_subitems.html', $this->_aObject['template']), array(
+                'bx_repeat:menu_items' => $aSubitems,
+            ));
+        }
+
+        $a['bx_if:show_subitems'] = array (
+            'condition' => $bTmplVarsSubitems,
+            'content' => $aTmplVarsSubitems
+        );
+
+        unset($a['subitems']);
+
         foreach ($this->_aOptionalParams as $sName => $sDefaultValue)
             if (!isset($a[$sName]))
                 $a[$sName] = $sDefaultValue;