[ 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) 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 }
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 |