[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/administrator/components/com_config/src/Model/ -> ApplicationModel.php (source)

   1  <?php
   2  
   3  /**
   4   * @package     Joomla.Administrator
   5   * @subpackage  com_config
   6   *
   7   * @copyright   (C) 2013 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\Config\Administrator\Model;
  12  
  13  use Joomla\CMS\Access\Access;
  14  use Joomla\CMS\Access\Rules;
  15  use Joomla\CMS\Cache\Exception\CacheConnectingException;
  16  use Joomla\CMS\Cache\Exception\UnsupportedCacheException;
  17  use Joomla\CMS\Component\ComponentHelper;
  18  use Joomla\CMS\Factory;
  19  use Joomla\CMS\Filesystem\File;
  20  use Joomla\CMS\Filesystem\Folder;
  21  use Joomla\CMS\Filesystem\Path;
  22  use Joomla\CMS\Filter\OutputFilter;
  23  use Joomla\CMS\Http\HttpFactory;
  24  use Joomla\CMS\Language\Text;
  25  use Joomla\CMS\Log\Log;
  26  use Joomla\CMS\Mail\Exception\MailDisabledException;
  27  use Joomla\CMS\Mail\MailTemplate;
  28  use Joomla\CMS\MVC\Model\FormModel;
  29  use Joomla\CMS\Table\Asset;
  30  use Joomla\CMS\Table\Table;
  31  use Joomla\CMS\Uri\Uri;
  32  use Joomla\CMS\User\UserHelper;
  33  use Joomla\Database\DatabaseDriver;
  34  use Joomla\Database\ParameterType;
  35  use Joomla\Registry\Registry;
  36  use Joomla\Utilities\ArrayHelper;
  37  use PHPMailer\PHPMailer\Exception as phpMailerException;
  38  
  39  // phpcs:disable PSR1.Files.SideEffects
  40  \defined('_JEXEC') or die;
  41  // phpcs:enable PSR1.Files.SideEffects
  42  
  43  /**
  44   * Model for the global configuration
  45   *
  46   * @since  3.2
  47   */
  48  class ApplicationModel extends FormModel
  49  {
  50      /**
  51       * Array of protected password fields from the configuration.php
  52       *
  53       * @var    array
  54       * @since  3.9.23
  55       */
  56      private $protectedConfigurationFields = array('password', 'secret', 'smtppass', 'redis_server_auth', 'session_redis_server_auth');
  57  
  58      /**
  59       * Method to get a form object.
  60       *
  61       * @param   array    $data      Data for the form.
  62       * @param   boolean  $loadData  True if the form is to load its own data (default case), false if not.
  63       *
  64       * @return  mixed  A JForm object on success, false on failure
  65       *
  66       * @since   1.6
  67       */
  68      public function getForm($data = array(), $loadData = true)
  69      {
  70          // Get the form.
  71          $form = $this->loadForm('com_config.application', 'application', array('control' => 'jform', 'load_data' => $loadData));
  72  
  73          if (empty($form)) {
  74              return false;
  75          }
  76  
  77          return $form;
  78      }
  79  
  80      /**
  81       * Method to get the configuration data.
  82       *
  83       * This method will load the global configuration data straight from
  84       * JConfig. If configuration data has been saved in the session, that
  85       * data will be merged into the original data, overwriting it.
  86       *
  87       * @return  array  An array containing all global config data.
  88       *
  89       * @since   1.6
  90       */
  91      public function getData()
  92      {
  93          // Get the config data.
  94          $config = new \JConfig();
  95          $data   = ArrayHelper::fromObject($config);
  96  
  97          // Get the correct driver at runtime
  98          $data['dbtype'] = $this->getDatabase()->getName();
  99  
 100          // Prime the asset_id for the rules.
 101          $data['asset_id'] = 1;
 102  
 103          // Get the text filter data
 104          $params          = ComponentHelper::getParams('com_config');
 105          $data['filters'] = ArrayHelper::fromObject($params->get('filters'));
 106  
 107          // If no filter data found, get from com_content (update of 1.6/1.7 site)
 108          if (empty($data['filters'])) {
 109              $contentParams = ComponentHelper::getParams('com_content');
 110              $data['filters'] = ArrayHelper::fromObject($contentParams->get('filters'));
 111          }
 112  
 113          // Check for data in the session.
 114          $temp = Factory::getApplication()->getUserState('com_config.config.global.data');
 115  
 116          // Merge in the session data.
 117          if (!empty($temp)) {
 118              // $temp can sometimes be an object, and we need it to be an array
 119              if (is_object($temp)) {
 120                  $temp = ArrayHelper::fromObject($temp);
 121              }
 122  
 123              $data = array_merge($temp, $data);
 124          }
 125  
 126          // Correct error_reporting value, since we removed "development", the "maximum" should be set instead
 127          // @TODO: This can be removed in 5.0
 128          if (!empty($data['error_reporting']) && $data['error_reporting'] === 'development') {
 129              $data['error_reporting'] = 'maximum';
 130          }
 131  
 132          return $data;
 133      }
 134  
 135      /**
 136       * Method to validate the db connection properties.
 137       *
 138       * @param   array  $data  An array containing all global config data.
 139       *
 140       * @return  array|boolean  Array with the validated global config data or boolean false on a validation failure.
 141       *
 142       * @since   4.0.0
 143       */
 144      public function validateDbConnection($data)
 145      {
 146          // Validate database connection encryption options
 147          if ((int) $data['dbencryption'] === 0) {
 148              // Reset unused options
 149              if (!empty($data['dbsslkey'])) {
 150                  $data['dbsslkey'] = '';
 151              }
 152  
 153              if (!empty($data['dbsslcert'])) {
 154                  $data['dbsslcert'] = '';
 155              }
 156  
 157              if ((bool) $data['dbsslverifyservercert'] === true) {
 158                  $data['dbsslverifyservercert'] = false;
 159              }
 160  
 161              if (!empty($data['dbsslca'])) {
 162                  $data['dbsslca'] = '';
 163              }
 164  
 165              if (!empty($data['dbsslcipher'])) {
 166                  $data['dbsslcipher'] = '';
 167              }
 168          } else {
 169              // Check localhost
 170              if (strtolower($data['host']) === 'localhost') {
 171                  Factory::getApplication()->enqueueMessage(Text::_('COM_CONFIG_ERROR_DATABASE_ENCRYPTION_LOCALHOST'), 'error');
 172  
 173                  return false;
 174              }
 175  
 176              // Check CA file and folder depending on database type if server certificate verification
 177              if ((bool) $data['dbsslverifyservercert'] === true) {
 178                  if (empty($data['dbsslca'])) {
 179                      Factory::getApplication()->enqueueMessage(
 180                          Text::sprintf(
 181                              'COM_CONFIG_ERROR_DATABASE_ENCRYPTION_FILE_FIELD_EMPTY',
 182                              Text::_('COM_CONFIG_FIELD_DATABASE_ENCRYPTION_CA_LABEL')
 183                          ),
 184                          'error'
 185                      );
 186  
 187                      return false;
 188                  }
 189  
 190                  if (!File::exists(Path::clean($data['dbsslca']))) {
 191                      Factory::getApplication()->enqueueMessage(
 192                          Text::sprintf(
 193                              'COM_CONFIG_ERROR_DATABASE_ENCRYPTION_FILE_FIELD_BAD',
 194                              Text::_('COM_CONFIG_FIELD_DATABASE_ENCRYPTION_CA_LABEL')
 195                          ),
 196                          'error'
 197                      );
 198  
 199                      return false;
 200                  }
 201              } else {
 202                  // Reset unused option
 203                  if (!empty($data['dbsslca'])) {
 204                      $data['dbsslca'] = '';
 205                  }
 206              }
 207  
 208              // Check key and certificate if two-way encryption
 209              if ((int) $data['dbencryption'] === 2) {
 210                  if (empty($data['dbsslkey'])) {
 211                      Factory::getApplication()->enqueueMessage(
 212                          Text::sprintf(
 213                              'COM_CONFIG_ERROR_DATABASE_ENCRYPTION_FILE_FIELD_EMPTY',
 214                              Text::_('COM_CONFIG_FIELD_DATABASE_ENCRYPTION_KEY_LABEL')
 215                          ),
 216                          'error'
 217                      );
 218  
 219                      return false;
 220                  }
 221  
 222                  if (!File::exists(Path::clean($data['dbsslkey']))) {
 223                      Factory::getApplication()->enqueueMessage(
 224                          Text::sprintf(
 225                              'COM_CONFIG_ERROR_DATABASE_ENCRYPTION_FILE_FIELD_BAD',
 226                              Text::_('COM_CONFIG_FIELD_DATABASE_ENCRYPTION_KEY_LABEL')
 227                          ),
 228                          'error'
 229                      );
 230  
 231                      return false;
 232                  }
 233  
 234                  if (empty($data['dbsslcert'])) {
 235                      Factory::getApplication()->enqueueMessage(
 236                          Text::sprintf(
 237                              'COM_CONFIG_ERROR_DATABASE_ENCRYPTION_FILE_FIELD_EMPTY',
 238                              Text::_('COM_CONFIG_FIELD_DATABASE_ENCRYPTION_CERT_LABEL')
 239                          ),
 240                          'error'
 241                      );
 242  
 243                      return false;
 244                  }
 245  
 246                  if (!File::exists(Path::clean($data['dbsslcert']))) {
 247                      Factory::getApplication()->enqueueMessage(
 248                          Text::sprintf(
 249                              'COM_CONFIG_ERROR_DATABASE_ENCRYPTION_FILE_FIELD_BAD',
 250                              Text::_('COM_CONFIG_FIELD_DATABASE_ENCRYPTION_CERT_LABEL')
 251                          ),
 252                          'error'
 253                      );
 254  
 255                      return false;
 256                  }
 257              } else {
 258                  // Reset unused options
 259                  if (!empty($data['dbsslkey'])) {
 260                      $data['dbsslkey'] = '';
 261                  }
 262  
 263                  if (!empty($data['dbsslcert'])) {
 264                      $data['dbsslcert'] = '';
 265                  }
 266              }
 267          }
 268  
 269          return $data;
 270      }
 271  
 272      /**
 273       * Method to save the configuration data.
 274       *
 275       * @param   array  $data  An array containing all global config data.
 276       *
 277       * @return  boolean  True on success, false on failure.
 278       *
 279       * @since   1.6
 280       */
 281      public function save($data)
 282      {
 283          $app = Factory::getApplication();
 284  
 285          // Try to load the values from the configuration file
 286          foreach ($this->protectedConfigurationFields as $fieldKey) {
 287              if (!isset($data[$fieldKey])) {
 288                  $data[$fieldKey] = $app->get($fieldKey, '');
 289              }
 290          }
 291  
 292          // Check that we aren't setting wrong database configuration
 293          $options = array(
 294              'driver'   => $data['dbtype'],
 295              'host'     => $data['host'],
 296              'user'     => $data['user'],
 297              'password' => $data['password'],
 298              'database' => $data['db'],
 299              'prefix'   => $data['dbprefix'],
 300          );
 301  
 302          if ((int) $data['dbencryption'] !== 0) {
 303              $options['ssl'] = [
 304                  'enable'             => true,
 305                  'verify_server_cert' => (bool) $data['dbsslverifyservercert'],
 306              ];
 307  
 308              foreach (['cipher', 'ca', 'key', 'cert'] as $value) {
 309                  $confVal = trim($data['dbssl' . $value]);
 310  
 311                  if ($confVal !== '') {
 312                      $options['ssl'][$value] = $confVal;
 313                  }
 314              }
 315          }
 316  
 317          try {
 318              $revisedDbo = DatabaseDriver::getInstance($options);
 319              $revisedDbo->getVersion();
 320          } catch (\Exception $e) {
 321              $app->enqueueMessage(Text::sprintf('COM_CONFIG_ERROR_DATABASE_NOT_AVAILABLE', $e->getCode(), $e->getMessage()), 'error');
 322  
 323              return false;
 324          }
 325  
 326          if ((int) $data['dbencryption'] !== 0 && empty($revisedDbo->getConnectionEncryption())) {
 327              if ($revisedDbo->isConnectionEncryptionSupported()) {
 328                  Factory::getApplication()->enqueueMessage(Text::_('COM_CONFIG_ERROR_DATABASE_ENCRYPTION_CONN_NOT_ENCRYPT'), 'error');
 329              } else {
 330                  Factory::getApplication()->enqueueMessage(Text::_('COM_CONFIG_ERROR_DATABASE_ENCRYPTION_SRV_NOT_SUPPORTS'), 'error');
 331              }
 332  
 333              return false;
 334          }
 335  
 336          // Check if we can set the Force SSL option
 337          if ((int) $data['force_ssl'] !== 0 && (int) $data['force_ssl'] !== (int) $app->get('force_ssl', '0')) {
 338              try {
 339                  // Make an HTTPS request to check if the site is available in HTTPS.
 340                  $host    = Uri::getInstance()->getHost();
 341                  $options = new Registry();
 342                  $options->set('userAgent', 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:41.0) Gecko/20100101 Firefox/41.0');
 343  
 344                  // Do not check for valid server certificate here, leave this to the user, moreover disable using a proxy if any is configured.
 345                  $options->set(
 346                      'transport.curl',
 347                      array(
 348                          CURLOPT_SSL_VERIFYPEER => false,
 349                          CURLOPT_SSL_VERIFYHOST => false,
 350                          CURLOPT_PROXY => null,
 351                          CURLOPT_PROXYUSERPWD => null,
 352                      )
 353                  );
 354                  $response = HttpFactory::getHttp($options)->get('https://' . $host . Uri::root(true) . '/', array('Host' => $host), 10);
 355  
 356                  // If available in HTTPS check also the status code.
 357                  if (!in_array($response->code, array(200, 503, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 401), true)) {
 358                      throw new \RuntimeException(Text::_('COM_CONFIG_ERROR_SSL_NOT_AVAILABLE_HTTP_CODE'));
 359                  }
 360              } catch (\RuntimeException $e) {
 361                  $data['force_ssl'] = 0;
 362  
 363                  // Also update the user state
 364                  $app->setUserState('com_config.config.global.data.force_ssl', 0);
 365  
 366                  // Inform the user
 367                  $app->enqueueMessage(Text::sprintf('COM_CONFIG_ERROR_SSL_NOT_AVAILABLE', $e->getMessage()), 'warning');
 368              }
 369          }
 370  
 371          // Save the rules
 372          if (isset($data['rules'])) {
 373              $rules = new Rules($data['rules']);
 374  
 375              // Check that we aren't removing our Super User permission
 376              // Need to get groups from database, since they might have changed
 377              $myGroups      = Access::getGroupsByUser(Factory::getUser()->get('id'));
 378              $myRules       = $rules->getData();
 379              $hasSuperAdmin = $myRules['core.admin']->allow($myGroups);
 380  
 381              if (!$hasSuperAdmin) {
 382                  $app->enqueueMessage(Text::_('COM_CONFIG_ERROR_REMOVING_SUPER_ADMIN'), 'error');
 383  
 384                  return false;
 385              }
 386  
 387              $asset = Table::getInstance('asset');
 388  
 389              if ($asset->loadByName('root.1')) {
 390                  $asset->rules = (string) $rules;
 391  
 392                  if (!$asset->check() || !$asset->store()) {
 393                      $app->enqueueMessage($asset->getError(), 'error');
 394  
 395                      return false;
 396                  }
 397              } else {
 398                  $app->enqueueMessage(Text::_('COM_CONFIG_ERROR_ROOT_ASSET_NOT_FOUND'), 'error');
 399  
 400                  return false;
 401              }
 402  
 403              unset($data['rules']);
 404          }
 405  
 406          // Save the text filters
 407          if (isset($data['filters'])) {
 408              $registry = new Registry(array('filters' => $data['filters']));
 409  
 410              $extension = Table::getInstance('extension');
 411  
 412              // Get extension_id
 413              $extensionId = $extension->find(array('name' => 'com_config'));
 414  
 415              if ($extension->load((int) $extensionId)) {
 416                  $extension->params = (string) $registry;
 417  
 418                  if (!$extension->check() || !$extension->store()) {
 419                      $app->enqueueMessage($extension->getError(), 'error');
 420  
 421                      return false;
 422                  }
 423              } else {
 424                  $app->enqueueMessage(Text::_('COM_CONFIG_ERROR_CONFIG_EXTENSION_NOT_FOUND'), 'error');
 425  
 426                  return false;
 427              }
 428  
 429              unset($data['filters']);
 430          }
 431  
 432          // Get the previous configuration.
 433          $prev = new \JConfig();
 434          $prev = ArrayHelper::fromObject($prev);
 435  
 436          // Merge the new data in. We do this to preserve values that were not in the form.
 437          $data = array_merge($prev, $data);
 438  
 439          /*
 440           * Perform miscellaneous options based on configuration settings/changes.
 441           */
 442  
 443          // Escape the offline message if present.
 444          if (isset($data['offline_message'])) {
 445              $data['offline_message'] = OutputFilter::ampReplace($data['offline_message']);
 446          }
 447  
 448          // Purge the database session table if we are changing to the database handler.
 449          if ($prev['session_handler'] != 'database' && $data['session_handler'] == 'database') {
 450              $db    = $this->getDatabase();
 451              $query = $db->getQuery(true)
 452                  ->delete($db->quoteName('#__session'))
 453                  ->where($db->quoteName('time') . ' < ' . (time() - 1));
 454              $db->setQuery($query);
 455              $db->execute();
 456          }
 457  
 458          // Purge the database session table if we are disabling session metadata
 459          if ($prev['session_metadata'] == 1 && $data['session_metadata'] == 0) {
 460              try {
 461                  // If we are are using the session handler, purge the extra columns, otherwise truncate the whole session table
 462                  if ($data['session_handler'] === 'database') {
 463                      $revisedDbo->setQuery(
 464                          $revisedDbo->getQuery(true)
 465                              ->update('#__session')
 466                              ->set(
 467                                  [
 468                                      $revisedDbo->quoteName('client_id') . ' = 0',
 469                                      $revisedDbo->quoteName('guest') . ' = NULL',
 470                                      $revisedDbo->quoteName('userid') . ' = NULL',
 471                                      $revisedDbo->quoteName('username') . ' = NULL',
 472                                  ]
 473                              )
 474                      )->execute();
 475                  } else {
 476                      $revisedDbo->truncateTable('#__session');
 477                  }
 478              } catch (\RuntimeException $e) {
 479                  /*
 480                   * The database API logs errors on failures so we don't need to add any error handling mechanisms here.
 481                   * Also, this data won't be added or checked anymore once the configuration is saved, so it'll purge itself
 482                   * through normal garbage collection anyway or if not using the database handler someone can purge the
 483                   * table on their own.  Either way, carry on Soldier!
 484                   */
 485              }
 486          }
 487  
 488          // Ensure custom session file path exists or try to create it if changed
 489          if (!empty($data['session_filesystem_path'])) {
 490              $currentPath = $prev['session_filesystem_path'] ?? null;
 491  
 492              if ($currentPath) {
 493                  $currentPath = Path::clean($currentPath);
 494              }
 495  
 496              $data['session_filesystem_path'] = Path::clean($data['session_filesystem_path']);
 497  
 498              if ($currentPath !== $data['session_filesystem_path']) {
 499                  if (!Folder::exists($data['session_filesystem_path']) && !Folder::create($data['session_filesystem_path'])) {
 500                      try {
 501                          Log::add(
 502                              Text::sprintf(
 503                                  'COM_CONFIG_ERROR_CUSTOM_SESSION_FILESYSTEM_PATH_NOTWRITABLE_USING_DEFAULT',
 504                                  $data['session_filesystem_path']
 505                              ),
 506                              Log::WARNING,
 507                              'jerror'
 508                          );
 509                      } catch (\RuntimeException $logException) {
 510                          $app->enqueueMessage(
 511                              Text::sprintf(
 512                                  'COM_CONFIG_ERROR_CUSTOM_SESSION_FILESYSTEM_PATH_NOTWRITABLE_USING_DEFAULT',
 513                                  $data['session_filesystem_path']
 514                              ),
 515                              'warning'
 516                          );
 517                      }
 518  
 519                      $data['session_filesystem_path'] = $currentPath;
 520                  }
 521              }
 522          }
 523  
 524          // Set the shared session configuration
 525          if (isset($data['shared_session'])) {
 526              $currentShared = $prev['shared_session'] ?? '0';
 527  
 528              // Has the user enabled shared sessions?
 529              if ($data['shared_session'] == 1 && $currentShared == 0) {
 530                  // Generate a random shared session name
 531                  $data['session_name'] = UserHelper::genRandomPassword(16);
 532              }
 533  
 534              // Has the user disabled shared sessions?
 535              if ($data['shared_session'] == 0 && $currentShared == 1) {
 536                  // Remove the session name value
 537                  unset($data['session_name']);
 538              }
 539          }
 540  
 541          // Set the shared session configuration
 542          if (isset($data['shared_session'])) {
 543              $currentShared = $prev['shared_session'] ?? '0';
 544  
 545              // Has the user enabled shared sessions?
 546              if ($data['shared_session'] == 1 && $currentShared == 0) {
 547                  // Generate a random shared session name
 548                  $data['session_name'] = UserHelper::genRandomPassword(16);
 549              }
 550  
 551              // Has the user disabled shared sessions?
 552              if ($data['shared_session'] == 0 && $currentShared == 1) {
 553                  // Remove the session name value
 554                  unset($data['session_name']);
 555              }
 556          }
 557  
 558          if (empty($data['cache_handler'])) {
 559              $data['caching'] = 0;
 560          }
 561  
 562          /*
 563           * Look for a custom cache_path
 564           * First check if a path is given in the submitted data, then check if a path exists in the previous data, otherwise use the default
 565           */
 566          if (!empty($data['cache_path'])) {
 567              $path = $data['cache_path'];
 568          } elseif (!empty($prev['cache_path'])) {
 569              $path = $prev['cache_path'];
 570          } else {
 571              $path = JPATH_CACHE;
 572          }
 573  
 574          // Give a warning if the cache-folder can not be opened
 575          if ($data['caching'] > 0 && $data['cache_handler'] == 'file' && @opendir($path) == false) {
 576              $error = true;
 577  
 578              // If a custom path is in use, try using the system default instead of disabling cache
 579              if ($path !== JPATH_CACHE && @opendir(JPATH_CACHE) != false) {
 580                  try {
 581                      Log::add(
 582                          Text::sprintf('COM_CONFIG_ERROR_CUSTOM_CACHE_PATH_NOTWRITABLE_USING_DEFAULT', $path, JPATH_CACHE),
 583                          Log::WARNING,
 584                          'jerror'
 585                      );
 586                  } catch (\RuntimeException $logException) {
 587                      $app->enqueueMessage(
 588                          Text::sprintf('COM_CONFIG_ERROR_CUSTOM_CACHE_PATH_NOTWRITABLE_USING_DEFAULT', $path, JPATH_CACHE),
 589                          'warning'
 590                      );
 591                  }
 592  
 593                  $path  = JPATH_CACHE;
 594                  $error = false;
 595  
 596                  $data['cache_path'] = '';
 597              }
 598  
 599              if ($error) {
 600                  try {
 601                      Log::add(Text::sprintf('COM_CONFIG_ERROR_CACHE_PATH_NOTWRITABLE', $path), Log::WARNING, 'jerror');
 602                  } catch (\RuntimeException $exception) {
 603                      $app->enqueueMessage(Text::sprintf('COM_CONFIG_ERROR_CACHE_PATH_NOTWRITABLE', $path), 'warning');
 604                  }
 605  
 606                  $data['caching'] = 0;
 607              }
 608          }
 609  
 610          // Did the user remove their custom cache path?  Don't save the variable to the config
 611          if (empty($data['cache_path'])) {
 612              unset($data['cache_path']);
 613          }
 614  
 615          // Clean the cache if disabled but previously enabled or changing cache handlers; these operations use the `$prev` data already in memory
 616          if ((!$data['caching'] && $prev['caching']) || $data['cache_handler'] !== $prev['cache_handler']) {
 617              try {
 618                  Factory::getCache()->clean();
 619              } catch (CacheConnectingException $exception) {
 620                  try {
 621                      Log::add(Text::_('COM_CONFIG_ERROR_CACHE_CONNECTION_FAILED'), Log::WARNING, 'jerror');
 622                  } catch (\RuntimeException $logException) {
 623                      $app->enqueueMessage(Text::_('COM_CONFIG_ERROR_CACHE_CONNECTION_FAILED'), 'warning');
 624                  }
 625              } catch (UnsupportedCacheException $exception) {
 626                  try {
 627                      Log::add(Text::_('COM_CONFIG_ERROR_CACHE_DRIVER_UNSUPPORTED'), Log::WARNING, 'jerror');
 628                  } catch (\RuntimeException $logException) {
 629                      $app->enqueueMessage(Text::_('COM_CONFIG_ERROR_CACHE_DRIVER_UNSUPPORTED'), 'warning');
 630                  }
 631              }
 632          }
 633  
 634          /*
 635           * Look for a custom tmp_path
 636           * First check if a path is given in the submitted data, then check if a path exists in the previous data, otherwise use the default
 637           */
 638          $defaultTmpPath = JPATH_ROOT . '/tmp';
 639  
 640          if (!empty($data['tmp_path'])) {
 641              $path = $data['tmp_path'];
 642          } elseif (!empty($prev['tmp_path'])) {
 643              $path = $prev['tmp_path'];
 644          } else {
 645              $path = $defaultTmpPath;
 646          }
 647  
 648          $path = Path::clean($path);
 649  
 650          // Give a warning if the tmp-folder is not valid or not writable
 651          if (!is_dir($path) || !is_writable($path)) {
 652              $error = true;
 653  
 654              // If a custom path is in use, try using the system default tmp path
 655              if ($path !== $defaultTmpPath && is_dir($defaultTmpPath) && is_writable($defaultTmpPath)) {
 656                  try {
 657                      Log::add(
 658                          Text::sprintf('COM_CONFIG_ERROR_CUSTOM_TEMP_PATH_NOTWRITABLE_USING_DEFAULT', $path, $defaultTmpPath),
 659                          Log::WARNING,
 660                          'jerror'
 661                      );
 662                  } catch (\RuntimeException $logException) {
 663                      $app->enqueueMessage(
 664                          Text::sprintf('COM_CONFIG_ERROR_CUSTOM_TEMP_PATH_NOTWRITABLE_USING_DEFAULT', $path, $defaultTmpPath),
 665                          'warning'
 666                      );
 667                  }
 668  
 669                  $error = false;
 670  
 671                  $data['tmp_path'] = $defaultTmpPath;
 672              }
 673  
 674              if ($error) {
 675                  try {
 676                      Log::add(Text::sprintf('COM_CONFIG_ERROR_TMP_PATH_NOTWRITABLE', $path), Log::WARNING, 'jerror');
 677                  } catch (\RuntimeException $exception) {
 678                      $app->enqueueMessage(Text::sprintf('COM_CONFIG_ERROR_TMP_PATH_NOTWRITABLE', $path), 'warning');
 679                  }
 680              }
 681          }
 682  
 683          /*
 684           * Look for a custom log_path
 685           * First check if a path is given in the submitted data, then check if a path exists in the previous data, otherwise use the default
 686           */
 687          $defaultLogPath = JPATH_ADMINISTRATOR . '/logs';
 688  
 689          if (!empty($data['log_path'])) {
 690              $path = $data['log_path'];
 691          } elseif (!empty($prev['log_path'])) {
 692              $path = $prev['log_path'];
 693          } else {
 694              $path = $defaultLogPath;
 695          }
 696  
 697          $path = Path::clean($path);
 698  
 699          // Give a warning if the log-folder is not valid or not writable
 700          if (!is_dir($path) || !is_writable($path)) {
 701              $error = true;
 702  
 703              // If a custom path is in use, try using the system default log path
 704              if ($path !== $defaultLogPath && is_dir($defaultLogPath) && is_writable($defaultLogPath)) {
 705                  try {
 706                      Log::add(
 707                          Text::sprintf('COM_CONFIG_ERROR_CUSTOM_LOG_PATH_NOTWRITABLE_USING_DEFAULT', $path, $defaultLogPath),
 708                          Log::WARNING,
 709                          'jerror'
 710                      );
 711                  } catch (\RuntimeException $logException) {
 712                      $app->enqueueMessage(
 713                          Text::sprintf('COM_CONFIG_ERROR_CUSTOM_LOG_PATH_NOTWRITABLE_USING_DEFAULT', $path, $defaultLogPath),
 714                          'warning'
 715                      );
 716                  }
 717  
 718                  $error = false;
 719                  $data['log_path'] = $defaultLogPath;
 720              }
 721  
 722              if ($error) {
 723                  try {
 724                      Log::add(Text::sprintf('COM_CONFIG_ERROR_LOG_PATH_NOTWRITABLE', $path), Log::WARNING, 'jerror');
 725                  } catch (\RuntimeException $exception) {
 726                      $app->enqueueMessage(Text::sprintf('COM_CONFIG_ERROR_LOG_PATH_NOTWRITABLE', $path), 'warning');
 727                  }
 728              }
 729          }
 730  
 731          // Create the new configuration object.
 732          $config = new Registry($data);
 733  
 734          // Overwrite webservices cors settings
 735          $app->set('cors', $data['cors']);
 736          $app->set('cors_allow_origin', $data['cors_allow_origin']);
 737          $app->set('cors_allow_headers', $data['cors_allow_headers']);
 738          $app->set('cors_allow_methods', $data['cors_allow_methods']);
 739  
 740          // Clear cache of com_config component.
 741          $this->cleanCache('_system');
 742  
 743          $result = $app->triggerEvent('onApplicationBeforeSave', array($config));
 744  
 745          // Store the data.
 746          if (in_array(false, $result, true)) {
 747              throw new \RuntimeException(Text::_('COM_CONFIG_ERROR_UNKNOWN_BEFORE_SAVING'));
 748          }
 749  
 750          // Write the configuration file.
 751          $result = $this->writeConfigFile($config);
 752  
 753          // Trigger the after save event.
 754          $app->triggerEvent('onApplicationAfterSave', array($config));
 755  
 756          return $result;
 757      }
 758  
 759      /**
 760       * Method to unset the root_user value from configuration data.
 761       *
 762       * This method will load the global configuration data straight from
 763       * JConfig and remove the root_user value for security, then save the configuration.
 764       *
 765       * @return  boolean  True on success, false on failure.
 766       *
 767       * @since   1.6
 768       */
 769      public function removeroot()
 770      {
 771          $app = Factory::getApplication();
 772  
 773          // Get the previous configuration.
 774          $prev = new \JConfig();
 775          $prev = ArrayHelper::fromObject($prev);
 776  
 777          // Create the new configuration object, and unset the root_user property
 778          unset($prev['root_user']);
 779          $config = new Registry($prev);
 780  
 781          $result = $app->triggerEvent('onApplicationBeforeSave', array($config));
 782  
 783          // Store the data.
 784          if (in_array(false, $result, true)) {
 785              throw new \RuntimeException(Text::_('COM_CONFIG_ERROR_UNKNOWN_BEFORE_SAVING'));
 786          }
 787  
 788          // Write the configuration file.
 789          $result = $this->writeConfigFile($config);
 790  
 791          // Trigger the after save event.
 792          $app->triggerEvent('onApplicationAfterSave', array($config));
 793  
 794          return $result;
 795      }
 796  
 797      /**
 798       * Method to write the configuration to a file.
 799       *
 800       * @param   Registry  $config  A Registry object containing all global config data.
 801       *
 802       * @return  boolean  True on success, false on failure.
 803       *
 804       * @since   2.5.4
 805       * @throws  \RuntimeException
 806       */
 807      private function writeConfigFile(Registry $config)
 808      {
 809          // Set the configuration file path.
 810          $file = JPATH_CONFIGURATION . '/configuration.php';
 811  
 812          $app = Factory::getApplication();
 813  
 814          // Attempt to make the file writeable.
 815          if (Path::isOwner($file) && !Path::setPermissions($file, '0644')) {
 816              $app->enqueueMessage(Text::_('COM_CONFIG_ERROR_CONFIGURATION_PHP_NOTWRITABLE'), 'notice');
 817          }
 818  
 819          // Attempt to write the configuration file as a PHP class named JConfig.
 820          $configuration = $config->toString('PHP', array('class' => 'JConfig', 'closingtag' => false));
 821  
 822          if (!File::write($file, $configuration)) {
 823              throw new \RuntimeException(Text::_('COM_CONFIG_ERROR_WRITE_FAILED'));
 824          }
 825  
 826          // Attempt to make the file unwriteable.
 827          if (Path::isOwner($file) && !Path::setPermissions($file, '0444')) {
 828              $app->enqueueMessage(Text::_('COM_CONFIG_ERROR_CONFIGURATION_PHP_NOTUNWRITABLE'), 'notice');
 829          }
 830  
 831          return true;
 832      }
 833  
 834      /**
 835       * Method to store the permission values in the asset table.
 836       *
 837       * This method will get an array with permission key value pairs and transform it
 838       * into json and update the asset table in the database.
 839       *
 840       * @param   string  $permission  Need an array with Permissions (component, rule, value and title)
 841       *
 842       * @return  array|bool  A list of result data or false on failure.
 843       *
 844       * @since   3.5
 845       */
 846      public function storePermissions($permission = null)
 847      {
 848          $app  = Factory::getApplication();
 849          $user = Factory::getUser();
 850  
 851          if (is_null($permission)) {
 852              // Get data from input.
 853              $permission = array(
 854                  'component' => $app->input->Json->get('comp'),
 855                  'action'    => $app->input->Json->get('action'),
 856                  'rule'      => $app->input->Json->get('rule'),
 857                  'value'     => $app->input->Json->get('value'),
 858                  'title'     => $app->input->Json->get('title', '', 'RAW')
 859              );
 860          }
 861  
 862          // We are creating a new item so we don't have an item id so don't allow.
 863          if (substr($permission['component'], -6) === '.false') {
 864              $app->enqueueMessage(Text::_('JLIB_RULES_SAVE_BEFORE_CHANGE_PERMISSIONS'), 'error');
 865  
 866              return false;
 867          }
 868  
 869          // Check if the user is authorized to do this.
 870          if (!$user->authorise('core.admin', $permission['component'])) {
 871              $app->enqueueMessage(Text::_('JERROR_ALERTNOAUTHOR'), 'error');
 872  
 873              return false;
 874          }
 875  
 876          $permission['component'] = empty($permission['component']) ? 'root.1' : $permission['component'];
 877  
 878          // Current view is global config?
 879          $isGlobalConfig = $permission['component'] === 'root.1';
 880  
 881          // Check if changed group has Super User permissions.
 882          $isSuperUserGroupBefore = Access::checkGroup($permission['rule'], 'core.admin');
 883  
 884          // Check if current user belongs to changed group.
 885          $currentUserBelongsToGroup = in_array((int) $permission['rule'], $user->groups) ? true : false;
 886  
 887          // Get current user groups tree.
 888          $currentUserGroupsTree = Access::getGroupsByUser($user->id, true);
 889  
 890          // Check if current user belongs to changed group.
 891          $currentUserSuperUser = $user->authorise('core.admin');
 892  
 893          // If user is not Super User cannot change the permissions of a group it belongs to.
 894          if (!$currentUserSuperUser && $currentUserBelongsToGroup) {
 895              $app->enqueueMessage(Text::_('JLIB_USER_ERROR_CANNOT_CHANGE_OWN_GROUPS'), 'error');
 896  
 897              return false;
 898          }
 899  
 900          // If user is not Super User cannot change the permissions of a group it belongs to.
 901          if (!$currentUserSuperUser && in_array((int) $permission['rule'], $currentUserGroupsTree)) {
 902              $app->enqueueMessage(Text::_('JLIB_USER_ERROR_CANNOT_CHANGE_OWN_PARENT_GROUPS'), 'error');
 903  
 904              return false;
 905          }
 906  
 907          // If user is not Super User cannot change the permissions of a Super User Group.
 908          if (!$currentUserSuperUser && $isSuperUserGroupBefore && !$currentUserBelongsToGroup) {
 909              $app->enqueueMessage(Text::_('JLIB_USER_ERROR_CANNOT_CHANGE_SUPER_USER'), 'error');
 910  
 911              return false;
 912          }
 913  
 914          // If user is not Super User cannot change the Super User permissions in any group it belongs to.
 915          if ($isSuperUserGroupBefore && $currentUserBelongsToGroup && $permission['action'] === 'core.admin') {
 916              $app->enqueueMessage(Text::_('JLIB_USER_ERROR_CANNOT_DEMOTE_SELF'), 'error');
 917  
 918              return false;
 919          }
 920  
 921          try {
 922              /** @var Asset $asset */
 923              $asset  = Table::getInstance('asset');
 924              $result = $asset->loadByName($permission['component']);
 925  
 926              if ($result === false) {
 927                  $data = array($permission['action'] => array($permission['rule'] => $permission['value']));
 928  
 929                  $rules        = new Rules($data);
 930                  $asset->rules = (string) $rules;
 931                  $asset->name  = (string) $permission['component'];
 932                  $asset->title = (string) $permission['title'];
 933  
 934                  // Get the parent asset id so we have a correct tree.
 935                  /** @var Asset $parentAsset */
 936                  $parentAsset = Table::getInstance('Asset');
 937  
 938                  if (strpos($asset->name, '.') !== false) {
 939                      $assetParts = explode('.', $asset->name);
 940                      $parentAsset->loadByName($assetParts[0]);
 941                      $parentAssetId = $parentAsset->id;
 942                  } else {
 943                      $parentAssetId = $parentAsset->getRootId();
 944                  }
 945  
 946                  /**
 947                   * @todo: incorrect ACL stored
 948                   * When changing a permission of an item that doesn't have a row in the asset table the row a new row is created.
 949                   * This works fine for item <-> component <-> global config scenario and component <-> global config scenario.
 950                   * But doesn't work properly for item <-> section(s) <-> component <-> global config scenario,
 951                   * because a wrong parent asset id (the component) is stored.
 952                   * Happens when there is no row in the asset table (ex: deleted or not created on update).
 953                   */
 954  
 955                  $asset->setLocation($parentAssetId, 'last-child');
 956              } else {
 957                  // Decode the rule settings.
 958                  $temp = json_decode($asset->rules, true);
 959  
 960                  // Check if a new value is to be set.
 961                  if (isset($permission['value'])) {
 962                      // Check if we already have an action entry.
 963                      if (!isset($temp[$permission['action']])) {
 964                          $temp[$permission['action']] = array();
 965                      }
 966  
 967                      // Check if we already have a rule entry.
 968                      if (!isset($temp[$permission['action']][$permission['rule']])) {
 969                          $temp[$permission['action']][$permission['rule']] = array();
 970                      }
 971  
 972                      // Set the new permission.
 973                      $temp[$permission['action']][$permission['rule']] = (int) $permission['value'];
 974  
 975                      // Check if we have an inherited setting.
 976                      if ($permission['value'] === '') {
 977                          unset($temp[$permission['action']][$permission['rule']]);
 978                      }
 979  
 980                      // Check if we have any rules.
 981                      if (!$temp[$permission['action']]) {
 982                          unset($temp[$permission['action']]);
 983                      }
 984                  } else {
 985                      // There is no value so remove the action as it's not needed.
 986                      unset($temp[$permission['action']]);
 987                  }
 988  
 989                  $asset->rules = json_encode($temp, JSON_FORCE_OBJECT);
 990              }
 991  
 992              if (!$asset->check() || !$asset->store()) {
 993                  $app->enqueueMessage(Text::_('JLIB_UNKNOWN'), 'error');
 994  
 995                  return false;
 996              }
 997          } catch (\Exception $e) {
 998              $app->enqueueMessage($e->getMessage(), 'error');
 999  
1000              return false;
1001          }
1002  
1003          // All checks done.
1004          $result = array(
1005              'text'    => '',
1006              'class'   => '',
1007              'result'  => true,
1008          );
1009  
1010          // Show the current effective calculated permission considering current group, path and cascade.
1011  
1012          try {
1013              // The database instance
1014              $db = $this->getDatabase();
1015  
1016              // Get the asset id by the name of the component.
1017              $query = $db->getQuery(true)
1018                  ->select($db->quoteName('id'))
1019                  ->from($db->quoteName('#__assets'))
1020                  ->where($db->quoteName('name') . ' = :component')
1021                  ->bind(':component', $permission['component']);
1022  
1023              $db->setQuery($query);
1024  
1025              $assetId = (int) $db->loadResult();
1026  
1027              // Fetch the parent asset id.
1028              $parentAssetId = null;
1029  
1030              /**
1031               * @todo: incorrect info
1032               * When creating a new item (not saving) it uses the calculated permissions from the component (item <-> component <-> global config).
1033               * But if we have a section too (item <-> section(s) <-> component <-> global config) this is not correct.
1034               * Also, currently it uses the component permission, but should use the calculated permissions for a child of the component/section.
1035               */
1036  
1037              // If not in global config we need the parent_id asset to calculate permissions.
1038              if (!$isGlobalConfig) {
1039                  // In this case we need to get the component rules too.
1040                  $query->clear()
1041                      ->select($db->quoteName('parent_id'))
1042                      ->from($db->quoteName('#__assets'))
1043                      ->where($db->quoteName('id') . ' = :assetid')
1044                      ->bind(':assetid', $assetId, ParameterType::INTEGER);
1045  
1046                  $db->setQuery($query);
1047  
1048                  $parentAssetId = (int) $db->loadResult();
1049              }
1050  
1051              // Get the group parent id of the current group.
1052              $rule = (int) $permission['rule'];
1053              $query->clear()
1054                  ->select($db->quoteName('parent_id'))
1055                  ->from($db->quoteName('#__usergroups'))
1056                  ->where($db->quoteName('id') . ' = :rule')
1057                  ->bind(':rule', $rule, ParameterType::INTEGER);
1058  
1059              $db->setQuery($query);
1060  
1061              $parentGroupId = (int) $db->loadResult();
1062  
1063              // Count the number of child groups of the current group.
1064              $query->clear()
1065                  ->select('COUNT(' . $db->quoteName('id') . ')')
1066                  ->from($db->quoteName('#__usergroups'))
1067                  ->where($db->quoteName('parent_id') . ' = :rule')
1068                  ->bind(':rule', $rule, ParameterType::INTEGER);
1069  
1070              $db->setQuery($query);
1071  
1072              $totalChildGroups = (int) $db->loadResult();
1073          } catch (\Exception $e) {
1074              $app->enqueueMessage($e->getMessage(), 'error');
1075  
1076              return false;
1077          }
1078  
1079          // Clear access statistics.
1080          Access::clearStatics();
1081  
1082          // After current group permission is changed we need to check again if the group has Super User permissions.
1083          $isSuperUserGroupAfter = Access::checkGroup($permission['rule'], 'core.admin');
1084  
1085          // Get the rule for just this asset (non-recursive) and get the actual setting for the action for this group.
1086          $assetRule = Access::getAssetRules($assetId, false, false)->allow($permission['action'], $permission['rule']);
1087  
1088          // Get the group, group parent id, and group global config recursive calculated permission for the chosen action.
1089          $inheritedGroupRule = Access::checkGroup($permission['rule'], $permission['action'], $assetId);
1090  
1091          if (!empty($parentAssetId)) {
1092              $inheritedGroupParentAssetRule = Access::checkGroup($permission['rule'], $permission['action'], $parentAssetId);
1093          } else {
1094              $inheritedGroupParentAssetRule = null;
1095          }
1096  
1097          $inheritedParentGroupRule = !empty($parentGroupId) ? Access::checkGroup($parentGroupId, $permission['action'], $assetId) : null;
1098  
1099          // Current group is a Super User group, so calculated setting is "Allowed (Super User)".
1100          if ($isSuperUserGroupAfter) {
1101              $result['class'] = 'badge bg-success';
1102              $result['text'] = '<span class="icon-lock icon-white" aria-hidden="true"></span>' . Text::_('JLIB_RULES_ALLOWED_ADMIN');
1103          } else {
1104              // Not super user.
1105              // First get the real recursive calculated setting and add (Inherited) to it.
1106  
1107              // If recursive calculated setting is "Denied" or null. Calculated permission is "Not Allowed (Inherited)".
1108              if ($inheritedGroupRule === null || $inheritedGroupRule === false) {
1109                  $result['class'] = 'badge bg-danger';
1110                  $result['text']  = Text::_('JLIB_RULES_NOT_ALLOWED_INHERITED');
1111              } else {
1112                  // If recursive calculated setting is "Allowed". Calculated permission is "Allowed (Inherited)".
1113                  $result['class'] = 'badge bg-success';
1114                  $result['text']  = Text::_('JLIB_RULES_ALLOWED_INHERITED');
1115              }
1116  
1117              // Second part: Overwrite the calculated permissions labels if there is an explicit permission in the current group.
1118  
1119              /**
1120               * @todo: incorrect info
1121               * If a component has a permission that doesn't exists in global config (ex: frontend editing in com_modules) by default
1122               * we get "Not Allowed (Inherited)" when we should get "Not Allowed (Default)".
1123               */
1124  
1125              // If there is an explicit permission "Not Allowed". Calculated permission is "Not Allowed".
1126              if ($assetRule === false) {
1127                  $result['class'] = 'badge bg-danger';
1128                  $result['text']  = Text::_('JLIB_RULES_NOT_ALLOWED');
1129              } elseif ($assetRule === true) {
1130                  // If there is an explicit permission is "Allowed". Calculated permission is "Allowed".
1131                  $result['class'] = 'badge bg-success';
1132                  $result['text']  = Text::_('JLIB_RULES_ALLOWED');
1133              }
1134  
1135              // Third part: Overwrite the calculated permissions labels for special cases.
1136  
1137              // Global configuration with "Not Set" permission. Calculated permission is "Not Allowed (Default)".
1138              if (empty($parentGroupId) && $isGlobalConfig === true && $assetRule === null) {
1139                  $result['class'] = 'badge bg-danger';
1140                  $result['text']  = Text::_('JLIB_RULES_NOT_ALLOWED_DEFAULT');
1141              } elseif ($inheritedGroupParentAssetRule === false || $inheritedParentGroupRule === false) {
1142                  /**
1143                   * Component/Item with explicit "Denied" permission at parent Asset (Category, Component or Global config) configuration.
1144                   * Or some parent group has an explicit "Denied".
1145                   * Calculated permission is "Not Allowed (Locked)".
1146                   */
1147                  $result['class'] = 'badge bg-danger';
1148                  $result['text']  = '<span class="icon-lock icon-white" aria-hidden="true"></span>' . Text::_('JLIB_RULES_NOT_ALLOWED_LOCKED');
1149              }
1150          }
1151  
1152          // If removed or added super user from group, we need to refresh the page to recalculate all settings.
1153          if ($isSuperUserGroupBefore != $isSuperUserGroupAfter) {
1154              $app->enqueueMessage(Text::_('JLIB_RULES_NOTICE_RECALCULATE_GROUP_PERMISSIONS'), 'notice');
1155          }
1156  
1157          // If this group has child groups, we need to refresh the page to recalculate the child settings.
1158          if ($totalChildGroups > 0) {
1159              $app->enqueueMessage(Text::_('JLIB_RULES_NOTICE_RECALCULATE_GROUP_CHILDS_PERMISSIONS'), 'notice');
1160          }
1161  
1162          return $result;
1163      }
1164  
1165      /**
1166       * Method to send a test mail which is called via an AJAX request
1167       *
1168       * @return   boolean
1169       *
1170       * @since  3.5
1171       */
1172      public function sendTestMail()
1173      {
1174          // Set the new values to test with the current settings
1175          $app = Factory::getApplication();
1176          $user = Factory::getUser();
1177          $input = $app->input->json;
1178          $smtppass = $input->get('smtppass', null, 'RAW');
1179  
1180          $app->set('smtpauth', $input->get('smtpauth'));
1181          $app->set('smtpuser', $input->get('smtpuser', '', 'STRING'));
1182          $app->set('smtphost', $input->get('smtphost'));
1183          $app->set('smtpsecure', $input->get('smtpsecure'));
1184          $app->set('smtpport', $input->get('smtpport'));
1185          $app->set('mailfrom', $input->get('mailfrom', '', 'STRING'));
1186          $app->set('fromname', $input->get('fromname', '', 'STRING'));
1187          $app->set('mailer', $input->get('mailer'));
1188          $app->set('mailonline', $input->get('mailonline'));
1189  
1190          // Use smtppass only if it was submitted
1191          if ($smtppass !== null) {
1192              $app->set('smtppass', $smtppass);
1193          }
1194  
1195          $mail = Factory::getMailer();
1196  
1197          // Prepare email and try to send it
1198          $mailer = new MailTemplate('com_config.test_mail', $user->getParam('language', $app->get('language')), $mail);
1199          $mailer->addTemplateData(
1200              array(
1201                  'sitename' => $app->get('sitename'),
1202                  'method' => Text::_('COM_CONFIG_SENDMAIL_METHOD_' . strtoupper($mail->Mailer))
1203              )
1204          );
1205          $mailer->addRecipient($app->get('mailfrom'), $app->get('fromname'));
1206  
1207          try {
1208              $mailSent = $mailer->send();
1209          } catch (MailDisabledException | phpMailerException $e) {
1210              $app->enqueueMessage($e->getMessage(), 'error');
1211  
1212              return false;
1213          }
1214  
1215          if ($mailSent === true) {
1216              $methodName = Text::_('COM_CONFIG_SENDMAIL_METHOD_' . strtoupper($mail->Mailer));
1217  
1218              // If JMail send the mail using PHP Mail as fallback.
1219              if ($mail->Mailer !== $app->get('mailer')) {
1220                  $app->enqueueMessage(Text::sprintf('COM_CONFIG_SENDMAIL_SUCCESS_FALLBACK', $app->get('mailfrom'), $methodName), 'warning');
1221              } else {
1222                  $app->enqueueMessage(Text::sprintf('COM_CONFIG_SENDMAIL_SUCCESS', $app->get('mailfrom'), $methodName), 'message');
1223              }
1224  
1225              return true;
1226          }
1227  
1228          $app->enqueueMessage(Text::_('COM_CONFIG_SENDMAIL_ERROR'), 'error');
1229  
1230          return false;
1231      }
1232  }


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