[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Helper/ -> ModuleHelper.php (source)

   1  <?php
   2  
   3  /**
   4   * Joomla! Content Management System
   5   *
   6   * @copyright  (C) 2005 Open Source Matters, Inc. <https://www.joomla.org>
   7   * @license    GNU General Public License version 2 or later; see LICENSE.txt
   8   */
   9  
  10  namespace Joomla\CMS\Helper;
  11  
  12  use Joomla\CMS\Cache\CacheControllerFactoryInterface;
  13  use Joomla\CMS\Cache\Controller\CallbackController;
  14  use Joomla\CMS\Component\ComponentHelper;
  15  use Joomla\CMS\Factory;
  16  use Joomla\CMS\Filesystem\Path;
  17  use Joomla\CMS\Filter\InputFilter;
  18  use Joomla\CMS\Language\LanguageHelper;
  19  use Joomla\CMS\Language\Text;
  20  use Joomla\CMS\Layout\LayoutHelper;
  21  use Joomla\CMS\Log\Log;
  22  use Joomla\CMS\Profiler\Profiler;
  23  use Joomla\Database\ParameterType;
  24  use Joomla\Registry\Registry;
  25  
  26  // phpcs:disable PSR1.Files.SideEffects
  27  \defined('JPATH_PLATFORM') or die;
  28  // phpcs:enable PSR1.Files.SideEffects
  29  
  30  /**
  31   * Module helper class
  32   *
  33   * @since  1.5
  34   */
  35  abstract class ModuleHelper
  36  {
  37      /**
  38       * Get module by name (real, eg 'Breadcrumbs' or folder, eg 'mod_breadcrumbs')
  39       *
  40       * @param   string  $name   The name of the module
  41       * @param   string  $title  The title of the module, optional
  42       *
  43       * @return  \stdClass  The Module object
  44       *
  45       * @since   1.5
  46       */
  47      public static function &getModule($name, $title = null)
  48      {
  49          $result = null;
  50          $modules =& static::load();
  51          $total = \count($modules);
  52  
  53          for ($i = 0; $i < $total; $i++) {
  54              // Match the name of the module
  55              if ($modules[$i]->name === $name || $modules[$i]->module === $name) {
  56                  // Match the title if we're looking for a specific instance of the module
  57                  if (!$title || $modules[$i]->title === $title) {
  58                      // Found it
  59                      $result = &$modules[$i];
  60                      break;
  61                  }
  62              }
  63          }
  64  
  65          // If we didn't find it, and the name is mod_something, create a dummy object
  66          if ($result === null && strpos($name, 'mod_') === 0) {
  67              $result = static::createDummyModule();
  68              $result->module = $name;
  69          }
  70  
  71          return $result;
  72      }
  73  
  74      /**
  75       * Get modules by position
  76       *
  77       * @param   string  $position  The position of the module
  78       *
  79       * @return  array  An array of module objects
  80       *
  81       * @since   1.5
  82       */
  83      public static function &getModules($position)
  84      {
  85          $position = strtolower($position);
  86          $result   = array();
  87          $input    = Factory::getApplication()->input;
  88          $modules  = &static::load();
  89          $total    = \count($modules);
  90  
  91          for ($i = 0; $i < $total; $i++) {
  92              if ($modules[$i]->position === $position) {
  93                  $result[] = &$modules[$i];
  94              }
  95          }
  96  
  97          // Prepend a dummy module for template preview if no module is published in the position
  98          if (empty($result) && $input->getBool('tp') && ComponentHelper::getParams('com_templates')->get('template_positions_display')) {
  99              $dummy = static::createDummyModule();
 100              $dummy->title = $position;
 101              $dummy->position = $position;
 102              $dummy->content = $position;
 103              $dummy->contentRendered = true;
 104  
 105              array_unshift($result, $dummy);
 106          }
 107  
 108          return $result;
 109      }
 110  
 111      /**
 112       * Checks if a module is enabled. A given module will only be returned
 113       * if it meets the following criteria: it is enabled, it is assigned to
 114       * the current menu item or all items, and the user meets the access level
 115       * requirements.
 116       *
 117       * @param   string  $module  The module name
 118       *
 119       * @return  boolean See description for conditions.
 120       *
 121       * @since   1.5
 122       */
 123      public static function isEnabled($module)
 124      {
 125          $result = static::getModule($module);
 126  
 127          return $result !== null && $result->id !== 0;
 128      }
 129  
 130      /**
 131       * Render the module.
 132       *
 133       * @param   object  $module   A module object.
 134       * @param   array   $attribs  An array of attributes for the module (probably from the XML).
 135       *
 136       * @return  string  The HTML content of the module output.
 137       *
 138       * @since   1.5
 139       */
 140      public static function renderModule($module, $attribs = array())
 141      {
 142          $app = Factory::getApplication();
 143  
 144          // Check that $module is a valid module object
 145          if (!\is_object($module) || !isset($module->module) || !isset($module->params)) {
 146              if (JDEBUG) {
 147                  Log::addLogger(array('text_file' => 'jmodulehelper.log.php'), Log::ALL, array('modulehelper'));
 148                  $app->getLogger()->debug(
 149                      __METHOD__ . '() - The $module parameter should be a module object.',
 150                      array('category' => 'modulehelper')
 151                  );
 152              }
 153  
 154              return '';
 155          }
 156  
 157          // Get module parameters
 158          $params = new Registry($module->params);
 159  
 160          // Render the module content
 161          static::renderRawModule($module, $params, $attribs);
 162  
 163          // Return early if only the content is required
 164          if (!empty($attribs['contentOnly'])) {
 165              return $module->content;
 166          }
 167  
 168          if (JDEBUG) {
 169              Profiler::getInstance('Application')->mark('beforeRenderModule ' . $module->module . ' (' . $module->title . ')');
 170          }
 171  
 172          // Record the scope.
 173          $scope = $app->scope;
 174  
 175          // Set scope to component name
 176          $app->scope = $module->module;
 177  
 178          // Get the template
 179          $template = $app->getTemplate();
 180  
 181          // Check if the current module has a style param to override template module style
 182          $paramsChromeStyle = $params->get('style');
 183          $basePath          = '';
 184  
 185          if ($paramsChromeStyle) {
 186              $paramsChromeStyle   = explode('-', $paramsChromeStyle, 2);
 187              $ChromeStyleTemplate = strtolower($paramsChromeStyle[0]);
 188              $attribs['style']    = $paramsChromeStyle[1];
 189  
 190              // Only set $basePath if the specified template isn't the current or system one.
 191              if ($ChromeStyleTemplate !== $template && $ChromeStyleTemplate !== 'system') {
 192                  $basePath = JPATH_THEMES . '/' . $ChromeStyleTemplate . '/html/layouts';
 193              }
 194          }
 195  
 196          // Make sure a style is set
 197          if (!isset($attribs['style'])) {
 198              $attribs['style'] = 'none';
 199          }
 200  
 201          // Dynamically add outline style
 202          if ($app->input->getBool('tp') && ComponentHelper::getParams('com_templates')->get('template_positions_display')) {
 203              $attribs['style'] .= ' outline';
 204          }
 205  
 206          $module->style = $attribs['style'];
 207  
 208          // If the $module is nulled it will return an empty content, otherwise it will render the module normally.
 209          $app->triggerEvent('onRenderModule', array(&$module, &$attribs));
 210  
 211          if ($module === null || !isset($module->content)) {
 212              return '';
 213          }
 214  
 215          $displayData = array(
 216              'module'  => $module,
 217              'params'  => $params,
 218              'attribs' => $attribs,
 219          );
 220  
 221          foreach (explode(' ', $attribs['style']) as $style) {
 222              if ($moduleContent = LayoutHelper::render('chromes.' . $style, $displayData, $basePath)) {
 223                  $module->content = $moduleContent;
 224              }
 225          }
 226  
 227          // Revert the scope
 228          $app->scope = $scope;
 229  
 230          $app->triggerEvent('onAfterRenderModule', array(&$module, &$attribs));
 231  
 232          if (JDEBUG) {
 233              Profiler::getInstance('Application')->mark('afterRenderModule ' . $module->module . ' (' . $module->title . ')');
 234          }
 235  
 236          return $module->content;
 237      }
 238  
 239      /**
 240       * Render the module content.
 241       *
 242       * @param   object    $module   A module object
 243       * @param   Registry  $params   A module parameters
 244       * @param   array     $attribs  An array of attributes for the module (probably from the XML).
 245       *
 246       * @return  string
 247       *
 248       * @since   4.0.0
 249       */
 250      public static function renderRawModule($module, Registry $params, $attribs = array())
 251      {
 252          if (!empty($module->contentRendered)) {
 253              return $module->content;
 254          }
 255  
 256          if (JDEBUG) {
 257              Profiler::getInstance('Application')->mark('beforeRenderRawModule ' . $module->module . ' (' . $module->title . ')');
 258          }
 259  
 260          $app = Factory::getApplication();
 261  
 262          // Record the scope.
 263          $scope = $app->scope;
 264  
 265          // Set scope to component name
 266          $app->scope = $module->module;
 267  
 268          // Get module path
 269          $module->module = preg_replace('/[^A-Z0-9_\.-]/i', '', $module->module);
 270  
 271          $dispatcher = $app->bootModule($module->module, $app->getName())->getDispatcher($module, $app);
 272  
 273          // Check if we have a dispatcher
 274          if ($dispatcher) {
 275              ob_start();
 276              $dispatcher->dispatch();
 277              $module->content = ob_get_clean();
 278          }
 279  
 280          // Add the flag that the module content has been rendered
 281          $module->contentRendered = true;
 282  
 283          // Revert the scope
 284          $app->scope = $scope;
 285  
 286          if (JDEBUG) {
 287              Profiler::getInstance('Application')->mark('afterRenderRawModule ' . $module->module . ' (' . $module->title . ')');
 288          }
 289  
 290          return $module->content;
 291      }
 292  
 293      /**
 294       * Get the path to a layout for a module
 295       *
 296       * @param   string  $module  The name of the module
 297       * @param   string  $layout  The name of the module layout. If alternative layout, in the form template:filename.
 298       *
 299       * @return  string  The path to the module layout
 300       *
 301       * @since   1.5
 302       */
 303      public static function getLayoutPath($module, $layout = 'default')
 304      {
 305          $templateObj   = Factory::getApplication()->getTemplate(true);
 306          $defaultLayout = $layout;
 307          $template      = $templateObj->template;
 308  
 309          if (strpos($layout, ':') !== false) {
 310              // Get the template and file name from the string
 311              $temp = explode(':', $layout);
 312              $template = $temp[0] === '_' ? $template : $temp[0];
 313              $layout = $temp[1];
 314              $defaultLayout = $temp[1] ?: 'default';
 315          }
 316  
 317          $dPath = JPATH_BASE . '/modules/' . $module . '/tmpl/default.php';
 318  
 319          try {
 320              // Build the template and base path for the layout
 321              $tPath = Path::check(JPATH_THEMES . '/' . $template . '/html/' . $module . '/' . $layout . '.php');
 322              $iPath = Path::check(JPATH_THEMES . '/' . $templateObj->parent . '/html/' . $module . '/' . $layout . '.php');
 323              $bPath = Path::check(JPATH_BASE . '/modules/' . $module . '/tmpl/' . $defaultLayout . '.php');
 324          } catch (\Exception $e) {
 325              // On error fallback to the default path
 326              return $dPath;
 327          }
 328  
 329          // If the template has a layout override use it
 330          if (is_file($tPath)) {
 331              return $tPath;
 332          }
 333  
 334          if (!empty($templateObj->parent) && is_file($iPath)) {
 335              return $iPath;
 336          }
 337  
 338          if (is_file($bPath)) {
 339              return $bPath;
 340          }
 341  
 342          return $dPath;
 343      }
 344  
 345      /**
 346       * Load published modules.
 347       *
 348       * @return  array
 349       *
 350       * @since   3.2
 351       */
 352      protected static function &load()
 353      {
 354          static $modules;
 355  
 356          if (isset($modules)) {
 357              return $modules;
 358          }
 359  
 360          $app = Factory::getApplication();
 361  
 362          $modules = null;
 363  
 364          $app->triggerEvent('onPrepareModuleList', array(&$modules));
 365  
 366          // If the onPrepareModuleList event returns an array of modules, then ignore the default module list creation
 367          if (!\is_array($modules)) {
 368              $modules = static::getModuleList();
 369          }
 370  
 371          $app->triggerEvent('onAfterModuleList', array(&$modules));
 372  
 373          $modules = static::cleanModuleList($modules);
 374  
 375          $app->triggerEvent('onAfterCleanModuleList', array(&$modules));
 376  
 377          return $modules;
 378      }
 379  
 380      /**
 381       * Module list
 382       *
 383       * @return  array
 384       */
 385      public static function getModuleList()
 386      {
 387          $app      = Factory::getApplication();
 388          $itemId   = $app->input->getInt('Itemid', 0);
 389          $groups   = $app->getIdentity()->getAuthorisedViewLevels();
 390          $clientId = (int) $app->getClientId();
 391  
 392          // Build a cache ID for the resulting data object
 393          $cacheId = implode(',', $groups) . '.' . $clientId . '.' . $itemId;
 394  
 395          $db      = Factory::getDbo();
 396          $query   = $db->getQuery(true);
 397          $nowDate = Factory::getDate()->toSql();
 398  
 399          $query->select($db->quoteName(['m.id', 'm.title', 'm.module', 'm.position', 'm.content', 'm.showtitle', 'm.params', 'mm.menuid']))
 400              ->from($db->quoteName('#__modules', 'm'))
 401              ->join(
 402                  'LEFT',
 403                  $db->quoteName('#__modules_menu', 'mm'),
 404                  $db->quoteName('mm.moduleid') . ' = ' . $db->quoteName('m.id')
 405              )
 406              ->join(
 407                  'LEFT',
 408                  $db->quoteName('#__extensions', 'e'),
 409                  $db->quoteName('e.element') . ' = ' . $db->quoteName('m.module')
 410                  . ' AND ' . $db->quoteName('e.client_id') . ' = ' . $db->quoteName('m.client_id')
 411              )
 412              ->where(
 413                  [
 414                      $db->quoteName('m.published') . ' = 1',
 415                      $db->quoteName('e.enabled') . ' = 1',
 416                      $db->quoteName('m.client_id') . ' = :clientId',
 417                  ]
 418              )
 419              ->bind(':clientId', $clientId, ParameterType::INTEGER)
 420              ->whereIn($db->quoteName('m.access'), $groups)
 421              ->extendWhere(
 422                  'AND',
 423                  [
 424                      $db->quoteName('m.publish_up') . ' IS NULL',
 425                      $db->quoteName('m.publish_up') . ' <= :publishUp',
 426                  ],
 427                  'OR'
 428              )
 429              ->bind(':publishUp', $nowDate)
 430              ->extendWhere(
 431                  'AND',
 432                  [
 433                      $db->quoteName('m.publish_down') . ' IS NULL',
 434                      $db->quoteName('m.publish_down') . ' >= :publishDown',
 435                  ],
 436                  'OR'
 437              )
 438              ->bind(':publishDown', $nowDate)
 439              ->extendWhere(
 440                  'AND',
 441                  [
 442                      $db->quoteName('mm.menuid') . ' = :itemId',
 443                      $db->quoteName('mm.menuid') . ' <= 0',
 444                  ],
 445                  'OR'
 446              )
 447              ->bind(':itemId', $itemId, ParameterType::INTEGER);
 448  
 449          // Filter by language
 450          if ($app->isClient('site') && $app->getLanguageFilter() || $app->isClient('administrator') && static::isAdminMultilang()) {
 451              $language = $app->getLanguage()->getTag();
 452  
 453              $query->whereIn($db->quoteName('m.language'), [$language, '*'], ParameterType::STRING);
 454              $cacheId .= $language . '*';
 455          }
 456  
 457          $query->order($db->quoteName(['m.position', 'm.ordering']));
 458  
 459          // Set the query
 460          $db->setQuery($query);
 461  
 462          try {
 463              /** @var CallbackController $cache */
 464              $cache = Factory::getContainer()->get(CacheControllerFactoryInterface::class)
 465                  ->createCacheController('callback', ['defaultgroup' => 'com_modules']);
 466  
 467              $modules = $cache->get(array($db, 'loadObjectList'), array(), md5($cacheId), false);
 468          } catch (\RuntimeException $e) {
 469              $app->getLogger()->warning(
 470                  Text::sprintf('JLIB_APPLICATION_ERROR_MODULE_LOAD', $e->getMessage()),
 471                  array('category' => 'jerror')
 472              );
 473  
 474              return array();
 475          }
 476  
 477          return $modules;
 478      }
 479  
 480      /**
 481       * Clean the module list
 482       *
 483       * @param   array  $modules  Array with module objects
 484       *
 485       * @return  array
 486       */
 487      public static function cleanModuleList($modules)
 488      {
 489          // Apply negative selections and eliminate duplicates
 490          $Itemid = Factory::getApplication()->input->getInt('Itemid');
 491          $negId = $Itemid ? -(int) $Itemid : false;
 492          $clean = array();
 493          $dupes = array();
 494  
 495          foreach ($modules as $i => $module) {
 496              // The module is excluded if there is an explicit prohibition
 497              $negHit = ($negId === (int) $module->menuid);
 498  
 499              if (isset($dupes[$module->id])) {
 500                  // If this item has been excluded, keep the duplicate flag set,
 501                  // but remove any item from the modules array.
 502                  if ($negHit) {
 503                      unset($clean[$module->id]);
 504                  }
 505  
 506                  continue;
 507              }
 508  
 509              $dupes[$module->id] = true;
 510  
 511              // Only accept modules without explicit exclusions.
 512              if ($negHit) {
 513                  continue;
 514              }
 515  
 516              $module->name = substr($module->module, 4);
 517              $module->style = null;
 518              $module->position = strtolower($module->position);
 519  
 520              $clean[$module->id] = $module;
 521          }
 522  
 523          unset($dupes);
 524  
 525          // Return to simple indexing that matches the query order.
 526          return array_values($clean);
 527      }
 528  
 529      /**
 530       * Module cache helper
 531       *
 532       * Caching modes:
 533       * To be set in XML:
 534       * 'static'      One cache file for all pages with the same module parameters
 535       * 'itemid'      Changes on itemid change, to be called from inside the module:
 536       * 'safeuri'     Id created from $cacheparams->modeparams array,
 537       * 'id'          Module sets own cache id's
 538       *
 539       * @param   object  $module        Module object
 540       * @param   object  $moduleparams  Module parameters
 541       * @param   object  $cacheparams   Module cache parameters - id or URL parameters, depending on the module cache mode
 542       *
 543       * @return  string
 544       *
 545       * @see     InputFilter::clean()
 546       * @since   1.6
 547       */
 548      public static function moduleCache($module, $moduleparams, $cacheparams)
 549      {
 550          if (!isset($cacheparams->modeparams)) {
 551              $cacheparams->modeparams = null;
 552          }
 553  
 554          if (!isset($cacheparams->cachegroup)) {
 555              $cacheparams->cachegroup = $module->module;
 556          }
 557  
 558          if (!isset($cacheparams->cachesuffix)) {
 559              $cacheparams->cachesuffix = '';
 560          }
 561  
 562          $user = Factory::getUser();
 563          $app  = Factory::getApplication();
 564  
 565          /** @var CallbackController $cache */
 566          $cache = Factory::getContainer()->get(CacheControllerFactoryInterface::class)
 567              ->createCacheController('callback', ['defaultgroup' => $cacheparams->cachegroup]);
 568  
 569          // Turn cache off for internal callers if parameters are set to off and for all logged in users
 570          $ownCacheDisabled = $moduleparams->get('owncache') === 0 || $moduleparams->get('owncache') === '0';
 571          $cacheDisabled = $moduleparams->get('cache') === 0 || $moduleparams->get('cache') === '0';
 572  
 573          if ($ownCacheDisabled || $cacheDisabled || $app->get('caching') == 0 || $user->get('id')) {
 574              $cache->setCaching(false);
 575          }
 576  
 577          // Module cache is set in seconds, global cache in minutes, setLifeTime works in minutes
 578          $cache->setLifeTime($moduleparams->get('cache_time', $app->get('cachetime') * 60) / 60);
 579  
 580          $wrkaroundoptions = array('nopathway' => 1, 'nohead' => 0, 'nomodules' => 1, 'modulemode' => 1, 'mergehead' => 1);
 581  
 582          $wrkarounds = true;
 583          $view_levels = md5(serialize($user->getAuthorisedViewLevels()));
 584  
 585          switch ($cacheparams->cachemode) {
 586              case 'id':
 587                  $ret = $cache->get(
 588                      array($cacheparams->class, $cacheparams->method),
 589                      $cacheparams->methodparams,
 590                      $cacheparams->modeparams . $cacheparams->cachesuffix,
 591                      $wrkarounds,
 592                      $wrkaroundoptions
 593                  );
 594                  break;
 595  
 596              case 'safeuri':
 597                  $secureid = null;
 598  
 599                  if (\is_array($cacheparams->modeparams)) {
 600                      $input   = $app->input;
 601                      $uri     = $input->getArray();
 602                      $safeuri = new \stdClass();
 603                      $noHtmlFilter = InputFilter::getInstance();
 604  
 605                      foreach ($cacheparams->modeparams as $key => $value) {
 606                          // Use int filter for id/catid to clean out spamy slugs
 607                          if (isset($uri[$key])) {
 608                              $safeuri->$key = $noHtmlFilter->clean($uri[$key], $value);
 609                          }
 610                      }
 611                  }
 612  
 613                  $secureid = md5(serialize(array($safeuri, $cacheparams->method, $moduleparams)));
 614                  $ret = $cache->get(
 615                      array($cacheparams->class, $cacheparams->method),
 616                      $cacheparams->methodparams,
 617                      $module->id . $view_levels . $secureid . $cacheparams->cachesuffix,
 618                      $wrkarounds,
 619                      $wrkaroundoptions
 620                  );
 621                  break;
 622  
 623              case 'static':
 624                  $ret = $cache->get(
 625                      array($cacheparams->class, $cacheparams->method),
 626                      $cacheparams->methodparams,
 627                      $module->module . md5(serialize($cacheparams->methodparams)) . $cacheparams->cachesuffix,
 628                      $wrkarounds,
 629                      $wrkaroundoptions
 630                  );
 631                  break;
 632  
 633              case 'itemid':
 634              default:
 635                  $ret = $cache->get(
 636                      array($cacheparams->class, $cacheparams->method),
 637                      $cacheparams->methodparams,
 638                      $module->id . $view_levels . $app->input->getInt('Itemid', null) . $cacheparams->cachesuffix,
 639                      $wrkarounds,
 640                      $wrkaroundoptions
 641                  );
 642                  break;
 643          }
 644  
 645          return $ret;
 646      }
 647  
 648      /**
 649       * Method to determine if filtering by language is enabled in back-end for modules.
 650       *
 651       * @return  boolean  True if enabled; false otherwise.
 652       *
 653       * @since   3.8.0
 654       */
 655      public static function isAdminMultilang()
 656      {
 657          static $enabled = false;
 658  
 659          if (\count(LanguageHelper::getInstalledLanguages(1)) > 1) {
 660              $enabled = (bool) ComponentHelper::getParams('com_modules')->get('adminlangfilter', 0);
 661          }
 662  
 663          return $enabled;
 664      }
 665  
 666      /**
 667       * Get module by id
 668       *
 669       * @param   string  $id  The id of the module
 670       *
 671       * @return  \stdClass  The Module object
 672       *
 673       * @since   3.9.0
 674       */
 675      public static function &getModuleById($id)
 676      {
 677          $modules =& static::load();
 678  
 679          $total = \count($modules);
 680  
 681          for ($i = 0; $i < $total; $i++) {
 682              // Match the id of the module
 683              if ((string) $modules[$i]->id === $id) {
 684                  // Found it
 685                  return $modules[$i];
 686              }
 687          }
 688  
 689          // If we didn't find it, create a dummy object
 690          $result = static::createDummyModule();
 691  
 692          return $result;
 693      }
 694  
 695      /**
 696       * Method to create a dummy module.
 697       *
 698       * @return  \stdClass  The Module object
 699       *
 700       * @since   4.0.0
 701       */
 702      protected static function createDummyModule(): \stdClass
 703      {
 704          $module            = new \stdClass();
 705          $module->id        = 0;
 706          $module->title     = '';
 707          $module->module    = '';
 708          $module->position  = '';
 709          $module->content   = '';
 710          $module->showtitle = 0;
 711          $module->control   = '';
 712          $module->params    = '';
 713  
 714          return $module;
 715      }
 716  }


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