[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/administrator/components/com_menus/src/Helper/ -> MenusHelper.php (source)

   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&amp;view=help&amp;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  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer