[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/administrator/components/com_menus/src/Model/ -> MenutypesModel.php (source)

   1  <?php
   2  
   3  /**
   4   * @package     Joomla.Administrator
   5   * @subpackage  com_menus
   6   *
   7   * @copyright   (C) 2011 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\Model;
  12  
  13  use Joomla\CMS\Application\ApplicationHelper;
  14  use Joomla\CMS\Factory;
  15  use Joomla\CMS\Filesystem\Folder;
  16  use Joomla\CMS\MVC\Model\BaseDatabaseModel;
  17  use Joomla\CMS\Object\CMSObject;
  18  use Joomla\Component\Menus\Administrator\Helper\MenusHelper;
  19  
  20  // phpcs:disable PSR1.Files.SideEffects
  21  \defined('_JEXEC') or die;
  22  // phpcs:enable PSR1.Files.SideEffects
  23  
  24  /**
  25   * Menu Item Types Model for Menus.
  26   *
  27   * @since  1.6
  28   */
  29  class MenutypesModel extends BaseDatabaseModel
  30  {
  31      /**
  32       * A reverse lookup of the base link URL to Title
  33       *
  34       * @var  array
  35       */
  36      protected $rlu = array();
  37  
  38      /**
  39       * Method to auto-populate the model state.
  40       *
  41       * This method should only be called once per instantiation and is designed
  42       * to be called on the first call to the getState() method unless the model
  43       * configuration flag to ignore the request is set.
  44       *
  45       * @return  void
  46       *
  47       * @note    Calling getState in this method will result in recursion.
  48       * @since   3.0.1
  49       */
  50      protected function populateState()
  51      {
  52          parent::populateState();
  53  
  54          $clientId = Factory::getApplication()->input->get('client_id', 0);
  55  
  56          $this->state->set('client_id', $clientId);
  57      }
  58  
  59      /**
  60       * Method to get the reverse lookup of the base link URL to Title
  61       *
  62       * @return  array  Array of reverse lookup of the base link URL to Title
  63       *
  64       * @since   1.6
  65       */
  66      public function getReverseLookup()
  67      {
  68          if (empty($this->rlu)) {
  69              $this->getTypeOptions();
  70          }
  71  
  72          return $this->rlu;
  73      }
  74  
  75      /**
  76       * Method to get the available menu item type options.
  77       *
  78       * @return  array  Array of groups with menu item types.
  79       *
  80       * @since   1.6
  81       */
  82      public function getTypeOptions()
  83      {
  84          $lang = Factory::getLanguage();
  85          $list = array();
  86  
  87          // Get the list of components.
  88          $db    = $this->getDatabase();
  89          $query = $db->getQuery(true)
  90              ->select(
  91                  [
  92                      $db->quoteName('name'),
  93                      $db->quoteName('element', 'option'),
  94                  ]
  95              )
  96              ->from($db->quoteName('#__extensions'))
  97              ->where(
  98                  [
  99                      $db->quoteName('type') . ' = ' . $db->quote('component'),
 100                      $db->quoteName('enabled') . ' = 1',
 101                  ]
 102              )
 103              ->order($db->quoteName('name') . ' ASC');
 104          $db->setQuery($query);
 105          $components = $db->loadObjectList();
 106  
 107          foreach ($components as $component) {
 108              $options = $this->getTypeOptionsByComponent($component->option);
 109  
 110              if ($options) {
 111                  $list[$component->name] = $options;
 112  
 113                  // Create the reverse lookup for link-to-name.
 114                  foreach ($options as $option) {
 115                      if (isset($option->request)) {
 116                          $this->addReverseLookupUrl($option);
 117  
 118                          if (isset($option->request['option'])) {
 119                              $componentLanguageFolder = JPATH_ADMINISTRATOR . '/components/' . $option->request['option'];
 120                              $lang->load($option->request['option'] . '.sys', JPATH_ADMINISTRATOR)
 121                                  || $lang->load($option->request['option'] . '.sys', $componentLanguageFolder);
 122                          }
 123                      }
 124                  }
 125              }
 126          }
 127  
 128          // Allow a system plugin to insert dynamic menu types to the list shown in menus:
 129          Factory::getApplication()->triggerEvent('onAfterGetMenuTypeOptions', array(&$list, $this));
 130  
 131          return $list;
 132      }
 133  
 134      /**
 135       * Method to create the reverse lookup for link-to-name.
 136       * (can be used from onAfterGetMenuTypeOptions handlers)
 137       *
 138       * @param   CMSObject  $option  Object with request array or string and title public variables
 139       *
 140       * @return  void
 141       *
 142       * @since   3.1
 143       */
 144      public function addReverseLookupUrl($option)
 145      {
 146          $this->rlu[MenusHelper::getLinkKey($option->request)] = $option->get('title');
 147      }
 148  
 149      /**
 150       * Get menu types by component.
 151       *
 152       * @param   string  $component  Component URL option.
 153       *
 154       * @return  array
 155       *
 156       * @since   1.6
 157       */
 158      protected function getTypeOptionsByComponent($component)
 159      {
 160          $options = array();
 161          $client  = ApplicationHelper::getClientInfo($this->getState('client_id'));
 162          $mainXML = $client->path . '/components/' . $component . '/metadata.xml';
 163  
 164          if (is_file($mainXML)) {
 165              $options = $this->getTypeOptionsFromXml($mainXML, $component);
 166          }
 167  
 168          if (empty($options)) {
 169              $options = $this->getTypeOptionsFromMvc($component);
 170          }
 171  
 172          if ($client->id == 1 && empty($options)) {
 173              $options = $this->getTypeOptionsFromManifest($component);
 174          }
 175  
 176          return $options;
 177      }
 178  
 179      /**
 180       * Get the menu types from an XML file
 181       *
 182       * @param   string  $file       File path
 183       * @param   string  $component  Component option as in URL
 184       *
 185       * @return  array|boolean
 186       *
 187       * @since   1.6
 188       */
 189      protected function getTypeOptionsFromXml($file, $component)
 190      {
 191          $options = array();
 192  
 193          // Attempt to load the xml file.
 194          if (!$xml = simplexml_load_file($file)) {
 195              return false;
 196          }
 197  
 198          // Look for the first menu node off of the root node.
 199          if (!$menu = $xml->xpath('menu[1]')) {
 200              return false;
 201          } else {
 202              $menu = $menu[0];
 203          }
 204  
 205          // If we have no options to parse, just add the base component to the list of options.
 206          if (!empty($menu['options']) && $menu['options'] == 'none') {
 207              // Create the menu option for the component.
 208              $o = new CMSObject();
 209              $o->title       = (string) $menu['name'];
 210              $o->description = (string) $menu['msg'];
 211              $o->request     = array('option' => $component);
 212  
 213              $options[] = $o;
 214  
 215              return $options;
 216          }
 217  
 218          // Look for the first options node off of the menu node.
 219          if (!$optionsNode = $menu->xpath('options[1]')) {
 220              return false;
 221          } else {
 222              $optionsNode = $optionsNode[0];
 223          }
 224  
 225          // Make sure the options node has children.
 226          if (!$children = $optionsNode->children()) {
 227              return false;
 228          }
 229  
 230          // Process each child as an option.
 231          foreach ($children as $child) {
 232              if ($child->getName() == 'option') {
 233                  // Create the menu option for the component.
 234                  $o = new CMSObject();
 235                  $o->title       = (string) $child['name'];
 236                  $o->description = (string) $child['msg'];
 237                  $o->request     = array('option' => $component, (string) $optionsNode['var'] => (string) $child['value']);
 238  
 239                  $options[] = $o;
 240              } elseif ($child->getName() == 'default') {
 241                  // Create the menu option for the component.
 242                  $o = new CMSObject();
 243                  $o->title       = (string) $child['name'];
 244                  $o->description = (string) $child['msg'];
 245                  $o->request     = array('option' => $component);
 246  
 247                  $options[] = $o;
 248              }
 249          }
 250  
 251          return $options;
 252      }
 253  
 254      /**
 255       * Get menu types from MVC
 256       *
 257       * @param   string  $component  Component option like in URLs
 258       *
 259       * @return  array|boolean
 260       *
 261       * @since   1.6
 262       */
 263      protected function getTypeOptionsFromMvc($component)
 264      {
 265          $options = array();
 266          $views   = array();
 267  
 268          foreach ($this->getFolders($component) as $path) {
 269              if (!is_dir($path)) {
 270                  continue;
 271              }
 272  
 273              $views = array_merge($views, Folder::folders($path, '.', false, true));
 274          }
 275  
 276          foreach ($views as $viewPath) {
 277              $view = basename($viewPath);
 278  
 279              // Ignore private views.
 280              if (strpos($view, '_') !== 0) {
 281                  // Determine if a metadata file exists for the view.
 282                  $file = $viewPath . '/metadata.xml';
 283  
 284                  if (is_file($file)) {
 285                      // Attempt to load the xml file.
 286                      if ($xml = simplexml_load_file($file)) {
 287                          // Look for the first view node off of the root node.
 288                          if ($menu = $xml->xpath('view[1]')) {
 289                              $menu = $menu[0];
 290  
 291                              // If the view is hidden from the menu, discard it and move on to the next view.
 292                              if (!empty($menu['hidden']) && $menu['hidden'] == 'true') {
 293                                  unset($xml);
 294                                  continue;
 295                              }
 296  
 297                              // Do we have an options node or should we process layouts?
 298                              // Look for the first options node off of the menu node.
 299                              if ($optionsNode = $menu->xpath('options[1]')) {
 300                                  $optionsNode = $optionsNode[0];
 301  
 302                                  // Make sure the options node has children.
 303                                  if ($children = $optionsNode->children()) {
 304                                      // Process each child as an option.
 305                                      foreach ($children as $child) {
 306                                          if ($child->getName() == 'option') {
 307                                              // Create the menu option for the component.
 308                                              $o = new CMSObject();
 309                                              $o->title       = (string) $child['name'];
 310                                              $o->description = (string) $child['msg'];
 311                                              $o->request     = array('option' => $component, 'view' => $view, (string) $optionsNode['var'] => (string) $child['value']);
 312  
 313                                              $options[] = $o;
 314                                          } elseif ($child->getName() == 'default') {
 315                                              // Create the menu option for the component.
 316                                              $o = new CMSObject();
 317                                              $o->title       = (string) $child['name'];
 318                                              $o->description = (string) $child['msg'];
 319                                              $o->request     = array('option' => $component, 'view' => $view);
 320  
 321                                              $options[] = $o;
 322                                          }
 323                                      }
 324                                  }
 325                              } else {
 326                                  $options = array_merge($options, (array) $this->getTypeOptionsFromLayouts($component, $view));
 327                              }
 328                          }
 329  
 330                          unset($xml);
 331                      }
 332                  } else {
 333                      $options = array_merge($options, (array) $this->getTypeOptionsFromLayouts($component, $view));
 334                  }
 335              }
 336          }
 337  
 338          return $options;
 339      }
 340  
 341      /**
 342       * Get menu types from Component manifest
 343       *
 344       * @param   string  $component  Component option like in URLs
 345       *
 346       * @return  array|boolean
 347       *
 348       * @since   3.7.0
 349       */
 350      protected function getTypeOptionsFromManifest($component)
 351      {
 352          // Load the component manifest
 353          $fileName = JPATH_ADMINISTRATOR . '/components/' . $component . '/' . str_replace('com_', '', $component) . '.xml';
 354  
 355          if (!is_file($fileName)) {
 356              return false;
 357          }
 358  
 359          if (!($manifest = simplexml_load_file($fileName))) {
 360              return false;
 361          }
 362  
 363          // Check for a valid XML root tag.
 364          if ($manifest->getName() != 'extension') {
 365              return false;
 366          }
 367  
 368          $options = array();
 369  
 370          // Start with the component root menu.
 371          $rootMenu = $manifest->administration->menu;
 372  
 373          // If the menu item doesn't exist or is hidden do nothing.
 374          if (!$rootMenu || in_array((string) $rootMenu['hidden'], array('true', 'hidden'))) {
 375              return $options;
 376          }
 377  
 378          // Create the root menu option.
 379          $ro = new \stdClass();
 380          $ro->title       = (string) trim($rootMenu);
 381          $ro->description = '';
 382          $ro->request     = array('option' => $component);
 383  
 384          // Process submenu options.
 385          $submenu = $manifest->administration->submenu;
 386  
 387          if (!$submenu) {
 388              return $options;
 389          }
 390  
 391          foreach ($submenu->menu as $child) {
 392              $attributes = $child->attributes();
 393  
 394              $o = new \stdClass();
 395              $o->title       = (string) trim($child);
 396              $o->description = '';
 397  
 398              if ((string) $attributes->link) {
 399                  parse_str((string) $attributes->link, $request);
 400              } else {
 401                  $request = array();
 402  
 403                  $request['option']     = $component;
 404                  $request['act']        = (string) $attributes->act;
 405                  $request['task']       = (string) $attributes->task;
 406                  $request['controller'] = (string) $attributes->controller;
 407                  $request['view']       = (string) $attributes->view;
 408                  $request['layout']     = (string) $attributes->layout;
 409                  $request['sub']        = (string) $attributes->sub;
 410              }
 411  
 412              $o->request = array_filter($request, 'strlen');
 413              $options[]  = new CMSObject($o);
 414  
 415              // Do not repeat the default view link (index.php?option=com_abc).
 416              if (count($o->request) == 1) {
 417                  $ro = null;
 418              }
 419          }
 420  
 421          if ($ro) {
 422              $options[] = new CMSObject($ro);
 423          }
 424  
 425          return $options;
 426      }
 427  
 428      /**
 429       * Get the menu types from component layouts
 430       *
 431       * @param   string  $component  Component option as in URLs
 432       * @param   string  $view       Name of the view
 433       *
 434       * @return  array
 435       *
 436       * @since   1.6
 437       */
 438      protected function getTypeOptionsFromLayouts($component, $view)
 439      {
 440          $options     = array();
 441          $layouts     = array();
 442          $layoutNames = array();
 443          $lang        = Factory::getLanguage();
 444          $client      = ApplicationHelper::getClientInfo($this->getState('client_id'));
 445  
 446          // Get the views for this component.
 447          foreach ($this->getFolders($component) as $folder) {
 448              $path = $folder . '/' . $view . '/tmpl';
 449  
 450              if (!is_dir($path)) {
 451                  $path = $folder . '/' . $view;
 452              }
 453  
 454              if (!is_dir($path)) {
 455                  continue;
 456              }
 457  
 458              $layouts = array_merge($layouts, Folder::files($path, '.xml$', false, true));
 459          }
 460  
 461          // Build list of standard layout names
 462          foreach ($layouts as $layout) {
 463              // Ignore private layouts.
 464              if (strpos(basename($layout), '_') === false) {
 465                  // Get the layout name.
 466                  $layoutNames[] = basename($layout, '.xml');
 467              }
 468          }
 469  
 470          // Get the template layouts
 471          // @todo: This should only search one template -- the current template for this item (default of specified)
 472          $folders = Folder::folders($client->path . '/templates', '', false, true);
 473  
 474          // Array to hold association between template file names and templates
 475          $templateName = array();
 476  
 477          foreach ($folders as $folder) {
 478              if (is_dir($folder . '/html/' . $component . '/' . $view)) {
 479                  $template = basename($folder);
 480                  $lang->load('tpl_' . $template . '.sys', $client->path)
 481                  || $lang->load('tpl_' . $template . '.sys', $client->path . '/templates/' . $template);
 482  
 483                  $templateLayouts = Folder::files($folder . '/html/' . $component . '/' . $view, '.xml$', false, true);
 484  
 485                  foreach ($templateLayouts as $layout) {
 486                      // Get the layout name.
 487                      $templateLayoutName = basename($layout, '.xml');
 488  
 489                      // Add to the list only if it is not a standard layout
 490                      if (array_search($templateLayoutName, $layoutNames) === false) {
 491                          $layouts[] = $layout;
 492  
 493                          // Set template name array so we can get the right template for the layout
 494                          $templateName[$layout] = basename($folder);
 495                      }
 496                  }
 497              }
 498          }
 499  
 500          // Process the found layouts.
 501          foreach ($layouts as $layout) {
 502              // Ignore private layouts.
 503              if (strpos(basename($layout), '_') === false) {
 504                  $file = $layout;
 505  
 506                  // Get the layout name.
 507                  $layout = basename($layout, '.xml');
 508  
 509                  // Create the menu option for the layout.
 510                  $o = new CMSObject();
 511                  $o->title       = ucfirst($layout);
 512                  $o->description = '';
 513                  $o->request     = array('option' => $component, 'view' => $view);
 514  
 515                  // Only add the layout request argument if not the default layout.
 516                  if ($layout != 'default') {
 517                      // If the template is set, add in format template:layout so we save the template name
 518                      $o->request['layout'] = isset($templateName[$file]) ? $templateName[$file] . ':' . $layout : $layout;
 519                  }
 520  
 521                  // Load layout metadata if it exists.
 522                  if (is_file($file)) {
 523                      // Attempt to load the xml file.
 524                      if ($xml = simplexml_load_file($file)) {
 525                          // Look for the first view node off of the root node.
 526                          if ($menu = $xml->xpath('layout[1]')) {
 527                              $menu = $menu[0];
 528  
 529                              // If the view is hidden from the menu, discard it and move on to the next view.
 530                              if (!empty($menu['hidden']) && $menu['hidden'] == 'true') {
 531                                  unset($xml);
 532                                  unset($o);
 533                                  continue;
 534                              }
 535  
 536                              // Populate the title and description if they exist.
 537                              if (!empty($menu['title'])) {
 538                                  $o->title = trim((string) $menu['title']);
 539                              }
 540  
 541                              if (!empty($menu->message[0])) {
 542                                  $o->description = trim((string) $menu->message[0]);
 543                              }
 544                          }
 545                      }
 546                  }
 547  
 548                  // Add the layout to the options array.
 549                  $options[] = $o;
 550              }
 551          }
 552  
 553          return $options;
 554      }
 555  
 556      /**
 557       * Get the folders with template files for the given component.
 558       *
 559       * @param   string  $component  Component option as in URLs
 560       *
 561       * @return  array
 562       *
 563       * @since   4.0.0
 564       */
 565      private function getFolders($component)
 566      {
 567          $client  = ApplicationHelper::getClientInfo($this->getState('client_id'));
 568  
 569          if (!is_dir($client->path . '/components/' . $component)) {
 570              return array();
 571          }
 572  
 573          $folders = Folder::folders($client->path . '/components/' . $component, '^view[s]?$', false, true);
 574          $folders = array_merge($folders, Folder::folders($client->path . '/components/' . $component, '^tmpl?$', false, true));
 575  
 576          if (!$folders) {
 577              return array();
 578          }
 579  
 580          return $folders;
 581      }
 582  }


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