1<?php 2/** 3 * @package Joomla.Libraries 4 * @subpackage HTML 5 * 6 * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. 7 * @license GNU General Public License version 2 or later; see LICENSE.txt 8 */ 9 10defined('JPATH_PLATFORM') or die; 11 12/** 13 * Utility class working with menu select lists 14 * 15 * @since 1.5 16 */ 17abstract class JHtmlMenu 18{ 19 /** 20 * Cached array of the menus. 21 * 22 * @var array 23 * @since 1.6 24 */ 25 protected static $menus = array(); 26 27 /** 28 * Cached array of the menus items. 29 * 30 * @var array 31 * @since 1.6 32 */ 33 protected static $items = array(); 34 35 /** 36 * Get a list of the available menus. 37 * 38 * @param int $clientId The client id 39 * 40 * @return array 41 * 42 * @since 1.6 43 */ 44 public static function menus($clientId = 0) 45 { 46 $key = serialize($clientId); 47 48 if (!isset(static::$menus[$key])) 49 { 50 $db = JFactory::getDbo(); 51 52 $query = $db->getQuery(true) 53 ->select($db->qn(array('id', 'menutype', 'title', 'client_id'), array('id', 'value', 'text', 'client_id'))) 54 ->from($db->quoteName('#__menu_types')) 55 ->order('client_id, title'); 56 57 if (isset($clientId)) 58 { 59 $query->where('client_id = ' . (int) $clientId); 60 } 61 62 static::$menus[$key] = $db->setQuery($query)->loadObjectList(); 63 } 64 65 return static::$menus[$key]; 66 } 67 68 /** 69 * Returns an array of menu items grouped by menu. 70 * 71 * @param array $config An array of configuration options [published, checkacl, clientid]. 72 * 73 * @return array 74 * 75 * @since 1.6 76 */ 77 public static function menuItems($config = array()) 78 { 79 $key = serialize($config); 80 81 if (empty(static::$items[$key])) 82 { 83 // B/C - not passed = 0, null can be passed for both clients 84 $clientId = array_key_exists('clientid', $config) ? $config['clientid'] : 0; 85 $menus = static::menus($clientId); 86 87 $db = JFactory::getDbo(); 88 $query = $db->getQuery(true) 89 ->select('a.id AS value, a.title AS text, a.level, a.menutype, a.client_id') 90 ->from('#__menu AS a') 91 ->where('a.parent_id > 0'); 92 93 // Filter on the client id 94 if (isset($clientId)) 95 { 96 $query->where('a.client_id = ' . (int) $clientId); 97 } 98 99 // Filter on the published state 100 if (isset($config['published'])) 101 { 102 if (is_numeric($config['published'])) 103 { 104 $query->where('a.published = ' . (int) $config['published']); 105 } 106 elseif ($config['published'] === '') 107 { 108 $query->where('a.published IN (0,1)'); 109 } 110 } 111 112 $query->order('a.lft'); 113 114 $db->setQuery($query); 115 $items = $db->loadObjectList(); 116 117 // Collate menu items based on menutype 118 $lookup = array(); 119 120 foreach ($items as &$item) 121 { 122 if (!isset($lookup[$item->menutype])) 123 { 124 $lookup[$item->menutype] = array(); 125 } 126 127 $lookup[$item->menutype][] = &$item; 128 129 // Translate the menu item title when client is administrator 130 if ($clientId === 1) 131 { 132 $item->text = JText::_($item->text); 133 } 134 135 $item->text = str_repeat('- ', $item->level) . $item->text; 136 } 137 138 static::$items[$key] = array(); 139 140 $user = JFactory::getUser(); 141 142 $aclcheck = !empty($config['checkacl']) ? (int) $config['checkacl'] : 0; 143 144 foreach ($menus as &$menu) 145 { 146 if ($aclcheck) 147 { 148 $action = $aclcheck == $menu->id ? 'edit' : 'create'; 149 150 if (!$user->authorise('core.' . $action, 'com_menus.menu.' . $menu->id)) 151 { 152 continue; 153 } 154 } 155 156 // Start group: 157 static::$items[$key][] = JHtml::_('select.optgroup', $menu->text); 158 159 // Special "Add to this Menu" option: 160 static::$items[$key][] = JHtml::_('select.option', $menu->value . '.1', JText::_('JLIB_HTML_ADD_TO_THIS_MENU')); 161 162 // Menu items: 163 if (isset($lookup[$menu->value])) 164 { 165 foreach ($lookup[$menu->value] as &$item) 166 { 167 static::$items[$key][] = JHtml::_('select.option', $menu->value . '.' . $item->value, $item->text); 168 } 169 } 170 171 // Finish group: 172 static::$items[$key][] = JHtml::_('select.optgroup', $menu->text); 173 } 174 } 175 176 return static::$items[$key]; 177 } 178 179 /** 180 * Displays an HTML select list of menu items. 181 * 182 * @param string $name The name of the control. 183 * @param string $selected The value of the selected option. 184 * @param string $attribs Attributes for the control. 185 * @param array $config An array of options for the control [id, published, checkacl, clientid]. 186 * 187 * @return string 188 * 189 * @since 1.6 190 */ 191 public static function menuItemList($name, $selected = null, $attribs = null, $config = array()) 192 { 193 static $count; 194 195 $options = static::menuItems($config); 196 197 return JHtml::_( 198 'select.genericlist', $options, $name, 199 array( 200 'id' => isset($config['id']) ? $config['id'] : 'assetgroups_' . (++$count), 201 'list.attr' => $attribs === null ? 'class="inputbox" size="1"' : $attribs, 202 'list.select' => (int) $selected, 203 'list.translate' => false, 204 ) 205 ); 206 } 207 208 /** 209 * Build the select list for Menu Ordering 210 * 211 * @param object &$row The row object 212 * @param integer $id The id for the row. Must exist to enable menu ordering 213 * 214 * @return string 215 * 216 * @since 1.5 217 */ 218 public static function ordering(&$row, $id) 219 { 220 if ($id) 221 { 222 $db = JFactory::getDbo(); 223 $query = $db->getQuery(true) 224 ->select('ordering AS value, title AS text') 225 ->from($db->quoteName('#__menu')) 226 ->where($db->quoteName('menutype') . ' = ' . $db->quote($row->menutype)) 227 ->where($db->quoteName('parent_id') . ' = ' . (int) $row->parent_id) 228 ->where($db->quoteName('published') . ' != -2') 229 ->order('ordering'); 230 $order = JHtml::_('list.genericordering', $query); 231 $ordering = JHtml::_( 232 'select.genericlist', $order, 'ordering', 233 array('list.attr' => 'class="inputbox" size="1"', 'list.select' => (int) $row->ordering) 234 ); 235 } 236 else 237 { 238 $ordering = '<input type="hidden" name="ordering" value="' . $row->ordering . '" />' . JText::_('JGLOBAL_NEWITEMSLAST_DESC'); 239 } 240 241 return $ordering; 242 } 243 244 /** 245 * Build the multiple select list for Menu Links/Pages 246 * 247 * @param boolean $all True if all can be selected 248 * @param boolean $unassigned True if unassigned can be selected 249 * @param int $clientId The client id 250 * 251 * @return string 252 * 253 * @since 1.5 254 */ 255 public static function linkOptions($all = false, $unassigned = false, $clientId = 0) 256 { 257 $db = JFactory::getDbo(); 258 259 // Get a list of the menu items 260 $query = $db->getQuery(true) 261 ->select('m.id, m.parent_id, m.title, m.menutype, m.client_id') 262 ->from($db->quoteName('#__menu') . ' AS m') 263 ->where($db->quoteName('m.published') . ' = 1') 264 ->order('m.client_id, m.menutype, m.parent_id'); 265 266 if (isset($clientId)) 267 { 268 $query->where('m.client_id = ' . (int) $clientId); 269 } 270 271 $db->setQuery($query); 272 273 $mitems = $db->loadObjectList(); 274 275 if (!$mitems) 276 { 277 $mitems = array(); 278 } 279 280 // Establish the hierarchy of the menu 281 $children = array(); 282 283 // First pass - collect children 284 foreach ($mitems as $v) 285 { 286 $pt = $v->parent_id; 287 $list = @$children[$pt] ? $children[$pt] : array(); 288 $list[] = $v; 289 $children[$pt] = $list; 290 } 291 292 // Second pass - get an indent list of the items 293 $list = static::treerecurse((int) $mitems[0]->parent_id, '', array(), $children, 9999, 0, 0); 294 295 // Code that adds menu name to Display of Page(s) 296 $mitems = array(); 297 298 if ($all | $unassigned) 299 { 300 $mitems[] = JHtml::_('select.option', '<OPTGROUP>', JText::_('JOPTION_MENUS')); 301 302 if ($all) 303 { 304 $mitems[] = JHtml::_('select.option', 0, JText::_('JALL')); 305 } 306 307 if ($unassigned) 308 { 309 $mitems[] = JHtml::_('select.option', -1, JText::_('JOPTION_UNASSIGNED')); 310 } 311 312 $mitems[] = JHtml::_('select.option', '</OPTGROUP>'); 313 } 314 315 $lastMenuType = null; 316 $tmpMenuType = null; 317 318 foreach ($list as $list_a) 319 { 320 if ($list_a->menutype != $lastMenuType) 321 { 322 if ($tmpMenuType) 323 { 324 $mitems[] = JHtml::_('select.option', '</OPTGROUP>'); 325 } 326 327 $mitems[] = JHtml::_('select.option', '<OPTGROUP>', $list_a->menutype); 328 $lastMenuType = $list_a->menutype; 329 $tmpMenuType = $list_a->menutype; 330 } 331 332 $mitems[] = JHtml::_('select.option', $list_a->id, $list_a->title); 333 } 334 335 if ($lastMenuType !== null) 336 { 337 $mitems[] = JHtml::_('select.option', '</OPTGROUP>'); 338 } 339 340 return $mitems; 341 } 342 343 /** 344 * Build the list representing the menu tree 345 * 346 * @param integer $id Id of the menu item 347 * @param string $indent The indentation string 348 * @param array $list The list to process 349 * @param array &$children The children of the current item 350 * @param integer $maxlevel The maximum number of levels in the tree 351 * @param integer $level The starting level 352 * @param int $type Set the type of spacer to use. Use 1 for |_ or 0 for - 353 * 354 * @return array 355 * 356 * @since 1.5 357 */ 358 public static function treerecurse($id, $indent, $list, &$children, $maxlevel = 9999, $level = 0, $type = 1) 359 { 360 if ($level <= $maxlevel && isset($children[$id]) && is_array($children[$id])) 361 { 362 if ($type) 363 { 364 $pre = '<sup>|_</sup> '; 365 $spacer = '.      '; 366 } 367 else 368 { 369 $pre = '- '; 370 $spacer = '  '; 371 } 372 373 foreach ($children[$id] as $v) 374 { 375 $id = $v->id; 376 377 if ($v->parent_id == 0) 378 { 379 $txt = $v->title; 380 } 381 else 382 { 383 $txt = $pre . $v->title; 384 } 385 386 $list[$id] = $v; 387 $list[$id]->treename = $indent . $txt; 388 389 if (isset($children[$id]) && is_array($children[$id])) 390 { 391 $list[$id]->children = count($children[$id]); 392 $list = static::treerecurse($id, $indent . $spacer, $list, $children, $maxlevel, $level + 1, $type); 393 } 394 else 395 { 396 $list[$id]->children = 0; 397 } 398 } 399 } 400 401 return $list; 402 } 403} 404