[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * @package Joomla.Administrator 5 * @subpackage com_menus 6 * 7 * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> 8 * @license GNU General Public License version 2 or later; see LICENSE.txt 9 */ 10 11 namespace Joomla\Component\Menus\Administrator\Helper; 12 13 use Joomla\CMS\Application\ApplicationHelper; 14 use Joomla\CMS\Component\ComponentHelper; 15 use Joomla\CMS\Factory; 16 use Joomla\CMS\Filesystem\File; 17 use Joomla\CMS\Filesystem\Folder; 18 use Joomla\CMS\Helper\ContentHelper; 19 use Joomla\CMS\Language\Associations; 20 use Joomla\CMS\Language\Multilanguage; 21 use Joomla\CMS\Language\Text; 22 use Joomla\CMS\Menu\AdministratorMenuItem; 23 use Joomla\CMS\Table\Table; 24 use Joomla\Database\DatabaseInterface; 25 use Joomla\Database\ParameterType; 26 use Joomla\Registry\Registry; 27 28 // phpcs:disable PSR1.Files.SideEffects 29 \defined('_JEXEC') or die; 30 // phpcs:enable PSR1.Files.SideEffects 31 32 /** 33 * Menus component helper. 34 * 35 * @since 1.6 36 */ 37 class MenusHelper extends ContentHelper 38 { 39 /** 40 * Defines the valid request variables for the reverse lookup. 41 * 42 * @var array 43 */ 44 protected static $_filter = array('option', 'view', 'layout'); 45 46 /** 47 * List of preset include paths 48 * 49 * @var array 50 * 51 * @since 4.0.0 52 */ 53 protected static $presets = null; 54 55 /** 56 * Gets a standard form of a link for lookups. 57 * 58 * @param mixed $request A link string or array of request variables. 59 * 60 * @return mixed A link in standard option-view-layout form, or false if the supplied response is invalid. 61 * 62 * @since 1.6 63 */ 64 public static function getLinkKey($request) 65 { 66 if (empty($request)) { 67 return false; 68 } 69 70 // Check if the link is in the form of index.php?... 71 if (is_string($request)) { 72 $args = array(); 73 74 if (strpos($request, 'index.php') === 0) { 75 parse_str(parse_url(htmlspecialchars_decode($request), PHP_URL_QUERY), $args); 76 } else { 77 parse_str($request, $args); 78 } 79 80 $request = $args; 81 } 82 83 // Only take the option, view and layout parts. 84 foreach ($request as $name => $value) { 85 if ((!in_array($name, self::$_filter)) && (!($name == 'task' && !array_key_exists('view', $request)))) { 86 // Remove the variables we want to ignore. 87 unset($request[$name]); 88 } 89 } 90 91 ksort($request); 92 93 return 'index.php?' . http_build_query($request, '', '&'); 94 } 95 96 /** 97 * Get the menu list for create a menu module 98 * 99 * @param int $clientId Optional client id - viz 0 = site, 1 = administrator, can be NULL for all 100 * 101 * @return array The menu array list 102 * 103 * @since 1.6 104 */ 105 public static function getMenuTypes($clientId = 0) 106 { 107 $db = Factory::getDbo(); 108 $query = $db->getQuery(true) 109 ->select($db->quoteName('a.menutype')) 110 ->from($db->quoteName('#__menu_types', 'a')); 111 112 if (isset($clientId)) { 113 $clientId = (int) $clientId; 114 $query->where($db->quoteName('a.client_id') . ' = :clientId') 115 ->bind(':clientId', $clientId, ParameterType::INTEGER); 116 } 117 118 $db->setQuery($query); 119 120 return $db->loadColumn(); 121 } 122 123 /** 124 * Get a list of menu links for one or all menus. 125 * 126 * @param string $menuType An option menu to filter the list on, otherwise all menu with given client id links 127 * are returned as a grouped array. 128 * @param integer $parentId An optional parent ID to pivot results around. 129 * @param integer $mode An optional mode. If parent ID is set and mode=2, the parent and children are excluded from the list. 130 * @param array $published An optional array of states 131 * @param array $languages Optional array of specify which languages we want to filter 132 * @param int $clientId Optional client id - viz 0 = site, 1 = administrator, can be NULL for all (used only if menutype not given) 133 * 134 * @return array|boolean 135 * 136 * @since 1.6 137 */ 138 public static function getMenuLinks($menuType = null, $parentId = 0, $mode = 0, $published = array(), $languages = array(), $clientId = 0) 139 { 140 $hasClientId = $clientId !== null; 141 $clientId = (int) $clientId; 142 143 $db = Factory::getDbo(); 144 $query = $db->getQuery(true) 145 ->select( 146 [ 147 'DISTINCT ' . $db->quoteName('a.id', 'value'), 148 $db->quoteName('a.title', 'text'), 149 $db->quoteName('a.alias'), 150 $db->quoteName('a.level'), 151 $db->quoteName('a.menutype'), 152 $db->quoteName('a.client_id'), 153 $db->quoteName('a.type'), 154 $db->quoteName('a.published'), 155 $db->quoteName('a.template_style_id'), 156 $db->quoteName('a.checked_out'), 157 $db->quoteName('a.language'), 158 $db->quoteName('a.lft'), 159 $db->quoteName('e.name', 'componentname'), 160 $db->quoteName('e.element'), 161 ] 162 ) 163 ->from($db->quoteName('#__menu', 'a')) 164 ->join('LEFT', $db->quoteName('#__extensions', 'e'), $db->quoteName('e.extension_id') . ' = ' . $db->quoteName('a.component_id')); 165 166 if (Multilanguage::isEnabled()) { 167 $query->select( 168 [ 169 $db->quoteName('l.title', 'language_title'), 170 $db->quoteName('l.image', 'language_image'), 171 $db->quoteName('l.sef', 'language_sef'), 172 ] 173 ) 174 ->join('LEFT', $db->quoteName('#__languages', 'l'), $db->quoteName('l.lang_code') . ' = ' . $db->quoteName('a.language')); 175 } 176 177 // Filter by the type if given, this is more specific than client id 178 if ($menuType) { 179 $query->where('(' . $db->quoteName('a.menutype') . ' = :menuType OR ' . $db->quoteName('a.parent_id') . ' = 0)') 180 ->bind(':menuType', $menuType); 181 } elseif ($hasClientId) { 182 $query->where($db->quoteName('a.client_id') . ' = :clientId') 183 ->bind(':clientId', $clientId, ParameterType::INTEGER); 184 } 185 186 // Prevent the parent and children from showing if requested. 187 if ($parentId && $mode == 2) { 188 $query->join('LEFT', $db->quoteName('#__menu', 'p'), $db->quoteName('p.id') . ' = :parentId') 189 ->where( 190 '(' . $db->quoteName('a.lft') . ' <= ' . $db->quoteName('p.lft') 191 . ' OR ' . $db->quoteName('a.rgt') . ' >= ' . $db->quoteName('p.rgt') . ')' 192 ) 193 ->bind(':parentId', $parentId, ParameterType::INTEGER); 194 } 195 196 if (!empty($languages)) { 197 $query->whereIn($db->quoteName('a.language'), (array) $languages, ParameterType::STRING); 198 } 199 200 if (!empty($published)) { 201 $query->whereIn($db->quoteName('a.published'), (array) $published); 202 } 203 204 $query->where($db->quoteName('a.published') . ' != -2'); 205 $query->order($db->quoteName('a.lft') . ' ASC'); 206 207 try { 208 // Get the options. 209 $db->setQuery($query); 210 $links = $db->loadObjectList(); 211 } catch (\RuntimeException $e) { 212 Factory::getApplication()->enqueueMessage($e->getMessage(), 'error'); 213 214 return false; 215 } 216 217 if (empty($menuType)) { 218 // If the menutype is empty, group the items by menutype. 219 $query = $db->getQuery(true) 220 ->select('*') 221 ->from($db->quoteName('#__menu_types')) 222 ->where($db->quoteName('menutype') . ' <> ' . $db->quote('')) 223 ->order( 224 [ 225 $db->quoteName('title'), 226 $db->quoteName('menutype'), 227 ] 228 ); 229 230 if ($hasClientId) { 231 $query->where($db->quoteName('client_id') . ' = :clientId') 232 ->bind(':clientId', $clientId, ParameterType::INTEGER); 233 } 234 235 try { 236 $db->setQuery($query); 237 $menuTypes = $db->loadObjectList(); 238 } catch (\RuntimeException $e) { 239 Factory::getApplication()->enqueueMessage($e->getMessage(), 'error'); 240 241 return false; 242 } 243 244 // Create a reverse lookup and aggregate the links. 245 $rlu = array(); 246 247 foreach ($menuTypes as &$type) { 248 $rlu[$type->menutype] = & $type; 249 $type->links = array(); 250 } 251 252 // Loop through the list of menu links. 253 foreach ($links as &$link) { 254 if (isset($rlu[$link->menutype])) { 255 $rlu[$link->menutype]->links[] = & $link; 256 257 // Cleanup garbage. 258 unset($link->menutype); 259 } 260 } 261 262 return $menuTypes; 263 } else { 264 return $links; 265 } 266 } 267 268 /** 269 * Get the associations 270 * 271 * @param integer $pk Menu item id 272 * 273 * @return array 274 * 275 * @since 3.0 276 */ 277 public static function getAssociations($pk) 278 { 279 $langAssociations = Associations::getAssociations('com_menus', '#__menu', 'com_menus.item', $pk, 'id', '', ''); 280 $associations = array(); 281 282 foreach ($langAssociations as $langAssociation) { 283 $associations[$langAssociation->language] = $langAssociation->id; 284 } 285 286 return $associations; 287 } 288 289 /** 290 * Load the menu items from database for the given menutype 291 * 292 * @param string $menutype The selected menu type 293 * @param boolean $enabledOnly Whether to load only enabled/published menu items. 294 * @param int[] $exclude The menu items to exclude from the list 295 * 296 * @return AdministratorMenuItem A root node with the menu items as children 297 * 298 * @since 4.0.0 299 */ 300 public static function getMenuItems($menutype, $enabledOnly = false, $exclude = array()) 301 { 302 $root = new AdministratorMenuItem(); 303 $db = Factory::getContainer()->get(DatabaseInterface::class); 304 $query = $db->getQuery(true); 305 306 // Prepare the query. 307 $query->select($db->quoteName('m') . '.*') 308 ->from($db->quoteName('#__menu', 'm')) 309 ->where( 310 [ 311 $db->quoteName('m.menutype') . ' = :menutype', 312 $db->quoteName('m.client_id') . ' = 1', 313 $db->quoteName('m.id') . ' > 1', 314 ] 315 ) 316 ->bind(':menutype', $menutype); 317 318 if ($enabledOnly) { 319 $query->where($db->quoteName('m.published') . ' = 1'); 320 } 321 322 // Filter on the enabled states. 323 $query->select($db->quoteName('e.element')) 324 ->join('LEFT', $db->quoteName('#__extensions', 'e'), $db->quoteName('m.component_id') . ' = ' . $db->quoteName('e.extension_id')) 325 ->extendWhere( 326 'AND', 327 [ 328 $db->quoteName('e.enabled') . ' = 1', 329 $db->quoteName('e.enabled') . ' IS NULL', 330 ], 331 'OR' 332 ); 333 334 if (count($exclude)) { 335 $exId = array_map('intval', array_filter($exclude, 'is_numeric')); 336 $exEl = array_filter($exclude, 'is_string'); 337 338 if ($exId) { 339 $query->whereNotIn($db->quoteName('m.id'), $exId) 340 ->whereNotIn($db->quoteName('m.parent_id'), $exId); 341 } 342 343 if ($exEl) { 344 $query->whereNotIn($db->quoteName('e.element'), $exEl, ParameterType::STRING); 345 } 346 } 347 348 // Order by lft. 349 $query->order($db->quoteName('m.lft')); 350 351 try { 352 $menuItems = []; 353 $iterator = $db->setQuery($query)->getIterator(); 354 355 foreach ($iterator as $item) { 356 $menuItems[$item->id] = new AdministratorMenuItem((array) $item); 357 } 358 359 unset($iterator); 360 361 foreach ($menuItems as $menuitem) { 362 // Resolve the alias item to get the original item 363 if ($menuitem->type == 'alias') { 364 static::resolveAlias($menuitem); 365 } 366 367 if ($menuitem->link = in_array($menuitem->type, array('separator', 'heading', 'container')) ? '#' : trim($menuitem->link)) { 368 $menuitem->submenu = array(); 369 $menuitem->class = $menuitem->img ?? ''; 370 $menuitem->scope = $menuitem->scope ?? null; 371 $menuitem->target = $menuitem->browserNav ? '_blank' : ''; 372 } 373 374 $menuitem->ajaxbadge = $menuitem->getParams()->get('ajax-badge'); 375 $menuitem->dashboard = $menuitem->getParams()->get('dashboard'); 376 377 if ($menuitem->parent_id > 1) { 378 if (isset($menuItems[$menuitem->parent_id])) { 379 $menuItems[$menuitem->parent_id]->addChild($menuitem); 380 } 381 } else { 382 $root->addChild($menuitem); 383 } 384 } 385 } catch (\RuntimeException $e) { 386 Factory::getApplication()->enqueueMessage(Text::_('JERROR_AN_ERROR_HAS_OCCURRED'), 'error'); 387 } 388 389 return $root; 390 } 391 392 /** 393 * Method to install a preset menu into database and link them to the given menutype 394 * 395 * @param string $preset The preset name 396 * @param string $menutype The target menutype 397 * 398 * @return void 399 * 400 * @throws \Exception 401 * 402 * @since 4.0.0 403 */ 404 public static function installPreset($preset, $menutype) 405 { 406 $root = static::loadPreset($preset, false); 407 408 if (count($root->getChildren()) == 0) { 409 throw new \Exception(Text::_('COM_MENUS_PRESET_LOAD_FAILED')); 410 } 411 412 static::installPresetItems($root, $menutype); 413 } 414 415 /** 416 * Method to install a preset menu item into database and link it to the given menutype 417 * 418 * @param AdministratorMenuItem $node The parent node of the items to process 419 * @param string $menutype The target menutype 420 * 421 * @return void 422 * 423 * @throws \Exception 424 * 425 * @since 4.0.0 426 */ 427 protected static function installPresetItems($node, $menutype) 428 { 429 $db = Factory::getDbo(); 430 $query = $db->getQuery(true); 431 $items = $node->getChildren(); 432 433 static $components = array(); 434 435 if (!$components) { 436 $query->select( 437 [ 438 $db->quoteName('extension_id'), 439 $db->quoteName('element'), 440 ] 441 ) 442 ->from($db->quoteName('#__extensions')) 443 ->where($db->quoteName('type') . ' = ' . $db->quote('component')); 444 $components = $db->setQuery($query)->loadObjectList(); 445 $components = array_column((array) $components, 'element', 'extension_id'); 446 } 447 448 Factory::getApplication()->triggerEvent('onPreprocessMenuItems', array('com_menus.administrator.import', &$items, null, true)); 449 450 foreach ($items as $item) { 451 /** @var \Joomla\CMS\Table\Menu $table */ 452 $table = Table::getInstance('Menu'); 453 454 $item->alias = $menutype . '-' . $item->title; 455 456 // Temporarily set unicodeslugs if a menu item has an unicode alias 457 $unicode = Factory::getApplication()->set('unicodeslugs', 1); 458 $item->alias = ApplicationHelper::stringURLSafe($item->alias); 459 Factory::getApplication()->set('unicodeslugs', $unicode); 460 461 if ($item->type == 'separator') { 462 // Do not reuse a separator 463 $item->title = $item->title ?: '-'; 464 $item->alias = microtime(true); 465 } elseif ($item->type == 'heading' || $item->type == 'container') { 466 // Try to match an existing record to have minimum collision for a heading 467 $keys = array( 468 'menutype' => $menutype, 469 'type' => $item->type, 470 'title' => $item->title, 471 'parent_id' => (int) $item->getParent()->id, 472 'client_id' => 1, 473 ); 474 $table->load($keys); 475 } elseif ($item->type == 'url' || $item->type == 'component') { 476 if (substr($item->link, 0, 8) === 'special:') { 477 $special = substr($item->link, 8); 478 479 if ($special === 'language-forum') { 480 $item->link = 'index.php?option=com_admin&view=help&layout=langforum'; 481 } elseif ($special === 'custom-forum') { 482 $item->link = ''; 483 } 484 } 485 486 // Try to match an existing record to have minimum collision for a link 487 $keys = array( 488 'menutype' => $menutype, 489 'type' => $item->type, 490 'link' => $item->link, 491 'parent_id' => (int) $item->getParent()->id, 492 'client_id' => 1, 493 ); 494 $table->load($keys); 495 } 496 497 // Translate "hideitems" param value from "element" into "menu-item-id" 498 if ($item->type == 'container' && count($hideitems = (array) $item->getParams()->get('hideitems'))) { 499 foreach ($hideitems as &$hel) { 500 if (!is_numeric($hel)) { 501 $hel = array_search($hel, $components); 502 } 503 } 504 505 $query = $db->getQuery(true) 506 ->select($db->quoteName('id')) 507 ->from($db->quoteName('#__menu')) 508 ->whereIn($db->quoteName('component_id'), $hideitems); 509 $hideitems = $db->setQuery($query)->loadColumn(); 510 511 $item->getParams()->set('hideitems', $hideitems); 512 } 513 514 $record = array( 515 'menutype' => $menutype, 516 'title' => $item->title, 517 'alias' => $item->alias, 518 'type' => $item->type, 519 'link' => $item->link, 520 'browserNav' => $item->browserNav, 521 'img' => $item->class, 522 'access' => $item->access, 523 'component_id' => array_search($item->element, $components) ?: 0, 524 'parent_id' => (int) $item->getParent()->id, 525 'client_id' => 1, 526 'published' => 1, 527 'language' => '*', 528 'home' => 0, 529 'params' => (string) $item->getParams(), 530 ); 531 532 if (!$table->bind($record)) { 533 throw new \Exception($table->getError()); 534 } 535 536 $table->setLocation($item->getParent()->id, 'last-child'); 537 538 if (!$table->check()) { 539 throw new \Exception($table->getError()); 540 } 541 542 if (!$table->store()) { 543 throw new \Exception($table->getError()); 544 } 545 546 $item->id = $table->get('id'); 547 548 if ($item->hasChildren()) { 549 static::installPresetItems($item, $menutype); 550 } 551 } 552 } 553 554 /** 555 * Add a custom preset externally via plugin or any other means. 556 * WARNING: Presets with same name will replace previously added preset *except* Joomla's default preset (joomla) 557 * 558 * @param string $name The unique identifier for the preset. 559 * @param string $title The display label for the preset. 560 * @param string $path The path to the preset file. 561 * @param bool $replace Whether to replace the preset with the same name if any (except 'joomla'). 562 * 563 * @return void 564 * 565 * @since 4.0.0 566 */ 567 public static function addPreset($name, $title, $path, $replace = true) 568 { 569 if (static::$presets === null) { 570 static::getPresets(); 571 } 572 573 if ($name == 'joomla') { 574 $replace = false; 575 } 576 577 if (($replace || !array_key_exists($name, static::$presets)) && is_file($path)) { 578 $preset = new \stdClass(); 579 580 $preset->name = $name; 581 $preset->title = $title; 582 $preset->path = $path; 583 584 static::$presets[$name] = $preset; 585 } 586 } 587 588 /** 589 * Get a list of available presets. 590 * 591 * @return \stdClass[] 592 * 593 * @since 4.0.0 594 */ 595 public static function getPresets() 596 { 597 if (static::$presets === null) { 598 // Important: 'null' will cause infinite recursion. 599 static::$presets = array(); 600 601 $components = ComponentHelper::getComponents(); 602 $lang = Factory::getApplication()->getLanguage(); 603 604 foreach ($components as $component) { 605 if (!$component->enabled) { 606 continue; 607 } 608 609 $folder = JPATH_ADMINISTRATOR . '/components/' . $component->option . '/presets/'; 610 611 if (!Folder::exists($folder)) { 612 continue; 613 } 614 615 $lang->load($component->option . '.sys', JPATH_ADMINISTRATOR) 616 || $lang->load($component->option . '.sys', JPATH_ADMINISTRATOR . '/components/' . $component->option); 617 618 $presets = Folder::files($folder, '.xml'); 619 620 foreach ($presets as $preset) { 621 $name = File::stripExt($preset); 622 $title = strtoupper($component->option . '_MENUS_PRESET_' . $name); 623 static::addPreset($name, $title, $folder . $preset); 624 } 625 } 626 627 // Load from template folder automatically 628 $app = Factory::getApplication(); 629 $tpl = JPATH_THEMES . '/' . $app->getTemplate() . '/html/com_menus/presets'; 630 631 if (is_dir($tpl)) { 632 $files = Folder::files($tpl, '\.xml$'); 633 634 foreach ($files as $file) { 635 $name = substr($file, 0, -4); 636 $title = str_replace('-', ' ', $name); 637 638 static::addPreset(strtolower($name), ucwords($title), $tpl . '/' . $file); 639 } 640 } 641 } 642 643 return static::$presets; 644 } 645 646 /** 647 * Load the menu items from a preset file into a hierarchical list of objects 648 * 649 * @param string $name The preset name 650 * @param bool $fallback Fallback to default (joomla) preset if the specified one could not be loaded? 651 * @param AdministratorMenuItem $parent Root node of the menu 652 * 653 * @return AdministratorMenuItem 654 * 655 * @since 4.0.0 656 */ 657 public static function loadPreset($name, $fallback = true, $parent = null) 658 { 659 $presets = static::getPresets(); 660 661 if (!$parent) { 662 $parent = new AdministratorMenuItem(); 663 } 664 665 if (isset($presets[$name]) && ($xml = simplexml_load_file($presets[$name]->path, null, LIBXML_NOCDATA)) && $xml instanceof \SimpleXMLElement) { 666 static::loadXml($xml, $parent); 667 } elseif ($fallback && isset($presets['default'])) { 668 if (($xml = simplexml_load_file($presets['default']->path, null, LIBXML_NOCDATA)) && $xml instanceof \SimpleXMLElement) { 669 static::loadXml($xml, $parent); 670 } 671 } 672 673 return $parent; 674 } 675 676 /** 677 * Method to resolve the menu item alias type menu item 678 * 679 * @param AdministratorMenuItem &$item The alias object 680 * 681 * @return void 682 * 683 * @since 4.0.0 684 */ 685 public static function resolveAlias(&$item) 686 { 687 $obj = $item; 688 689 while ($obj->type == 'alias') { 690 $aliasTo = (int) $obj->getParams()->get('aliasoptions'); 691 692 $db = Factory::getDbo(); 693 $query = $db->getQuery(true); 694 $query->select( 695 [ 696 $db->quoteName('a.id'), 697 $db->quoteName('a.link'), 698 $db->quoteName('a.type'), 699 $db->quoteName('e.element'), 700 ] 701 ) 702 ->from($db->quoteName('#__menu', 'a')) 703 ->join('LEFT', $db->quoteName('#__extensions', 'e'), $db->quoteName('e.extension_id') . ' = ' . $db->quoteName('a.component_id')) 704 ->where($db->quoteName('a.id') . ' = :aliasTo') 705 ->bind(':aliasTo', $aliasTo, ParameterType::INTEGER); 706 707 try { 708 $obj = new AdministratorMenuItem($db->setQuery($query)->loadAssoc()); 709 710 if (!$obj) { 711 $item->link = ''; 712 713 return; 714 } 715 } catch (\Exception $e) { 716 $item->link = ''; 717 718 return; 719 } 720 } 721 722 $item->id = $obj->id; 723 $item->link = $obj->link; 724 $item->type = $obj->type; 725 $item->element = $obj->element; 726 } 727 728 /** 729 * Parse the flat list of menu items and prepare the hierarchy of them using parent-child relationship. 730 * 731 * @param AdministratorMenuItem $item Menu item to preprocess 732 * 733 * @return void 734 * 735 * @since 4.0.0 736 */ 737 public static function preprocess($item) 738 { 739 // Resolve the alias item to get the original item 740 if ($item->type == 'alias') { 741 static::resolveAlias($item); 742 } 743 744 if ($item->link = in_array($item->type, array('separator', 'heading', 'container')) ? '#' : trim($item->link)) { 745 $item->class = $item->img ?? ''; 746 $item->scope = $item->scope ?? null; 747 $item->target = $item->browserNav ? '_blank' : ''; 748 } 749 } 750 751 /** 752 * Load a menu tree from an XML file 753 * 754 * @param \SimpleXMLElement[] $elements The xml menuitem nodes 755 * @param AdministratorMenuItem $parent The menu hierarchy list to be populated 756 * @param string[] $replace The substring replacements for iterator type items 757 * 758 * @return void 759 * 760 * @since 4.0.0 761 */ 762 protected static function loadXml($elements, $parent, $replace = array()) 763 { 764 foreach ($elements as $element) { 765 if ($element->getName() != 'menuitem') { 766 continue; 767 } 768 769 $select = (string) $element['sql_select']; 770 $from = (string) $element['sql_from']; 771 772 /** 773 * Following is a repeatable group based on simple database query. This requires sql_* attributes (sql_select and sql_from are required) 774 * The values can be used like - "{sql:columnName}" in any attribute of repeated elements. 775 * The repeated elements are place inside this xml node but they will be populated in the same level in the rendered menu 776 */ 777 if ($select && $from) { 778 $hidden = $element['hidden'] == 'true'; 779 $where = (string) $element['sql_where']; 780 $order = (string) $element['sql_order']; 781 $group = (string) $element['sql_group']; 782 $lJoin = (string) $element['sql_leftjoin']; 783 $iJoin = (string) $element['sql_innerjoin']; 784 785 $db = Factory::getDbo(); 786 $query = $db->getQuery(true); 787 $query->select($select)->from($from); 788 789 if ($where) { 790 $query->where($where); 791 } 792 793 if ($order) { 794 $query->order($order); 795 } 796 797 if ($group) { 798 $query->group($group); 799 } 800 801 if ($lJoin) { 802 $query->join('LEFT', $lJoin); 803 } 804 805 if ($iJoin) { 806 $query->join('INNER', $iJoin); 807 } 808 809 $results = $db->setQuery($query)->loadObjectList(); 810 811 // Skip the entire group if no items to iterate over. 812 if ($results) { 813 // Show the repeatable group heading node only if not set as hidden. 814 if (!$hidden) { 815 $child = static::parseXmlNode($element, $replace); 816 $parent->addChild($child); 817 } 818 819 // Iterate over the matching records, items goes in the same level (not $item->submenu) as this node. 820 if ('self' == (string) $element['sql_target']) { 821 foreach ($results as $result) { 822 static::loadXml($element->menuitem, $child, $result); 823 } 824 } else { 825 foreach ($results as $result) { 826 static::loadXml($element->menuitem, $parent, $result); 827 } 828 } 829 } 830 } else { 831 $item = static::parseXmlNode($element, $replace); 832 833 // Process the child nodes 834 static::loadXml($element->menuitem, $item, $replace); 835 836 $parent->addChild($item); 837 } 838 } 839 } 840 841 /** 842 * Create a menu item node from an xml element 843 * 844 * @param \SimpleXMLElement $node A menuitem element from preset xml 845 * @param string[] $replace The values to substitute in the title, link and element texts 846 * 847 * @return \stdClass 848 * 849 * @since 4.0.0 850 */ 851 protected static function parseXmlNode($node, $replace = array()) 852 { 853 $item = new AdministratorMenuItem(); 854 855 $item->id = null; 856 $item->type = (string) $node['type']; 857 $item->title = (string) $node['title']; 858 $item->alias = (string) $node['alias']; 859 $item->link = (string) $node['link']; 860 $item->target = (string) $node['target']; 861 $item->element = (string) $node['element']; 862 $item->class = (string) $node['class']; 863 $item->icon = (string) $node['icon']; 864 $item->access = (int) $node['access']; 865 $item->scope = (string) $node['scope'] ?: 'default'; 866 $item->ajaxbadge = (string) $node['ajax-badge']; 867 $item->dashboard = (string) $node['dashboard']; 868 869 $params = new Registry(trim($node->params)); 870 $params->set('menu-permission', (string) $node['permission']); 871 872 if ($item->type == 'separator' && trim($item->title, '- ')) { 873 $params->set('text_separator', 1); 874 } 875 876 if ($item->type == 'heading' || $item->type == 'container') { 877 $item->link = '#'; 878 } 879 880 if ((string) $node['quicktask']) { 881 $params->set('menu-quicktask', (string) $node['quicktask']); 882 $params->set('menu-quicktask-title', (string) $node['quicktask-title']); 883 $params->set('menu-quicktask-icon', (string) $node['quicktask-icon']); 884 $params->set('menu-quicktask-permission', (string) $node['quicktask-permission']); 885 } 886 887 // Translate attributes for iterator values 888 foreach ($replace as $var => $val) { 889 $item->title = str_replace("{sql:$var}", $val, $item->title); 890 $item->element = str_replace("{sql:$var}", $val, $item->element); 891 $item->link = str_replace("{sql:$var}", $val, $item->link); 892 $item->class = str_replace("{sql:$var}", $val, $item->class); 893 $item->icon = str_replace("{sql:$var}", $val, $item->icon); 894 $params->set('menu-quicktask', str_replace("{sql:$var}", $val, $params->get('menu-quicktask'))); 895 } 896 897 $item->setParams($params); 898 899 return $item; 900 } 901 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |