[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/plugins/system/privacyconsent/ -> privacyconsent.php (source)

   1  <?php
   2  
   3  /**
   4   * @package     Joomla.Plugin
   5   * @subpackage  System.privacyconsent
   6   *
   7   * @copyright   (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
   8   * @license     GNU General Public License version 2 or later; see LICENSE.txt
   9  
  10   * @phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
  11   */
  12  
  13  use Joomla\CMS\Application\ApplicationHelper;
  14  use Joomla\CMS\Cache\Cache;
  15  use Joomla\CMS\Factory;
  16  use Joomla\CMS\Form\Form;
  17  use Joomla\CMS\Form\FormHelper;
  18  use Joomla\CMS\Language\Associations;
  19  use Joomla\CMS\Language\Text;
  20  use Joomla\CMS\Mail\Exception\MailDisabledException;
  21  use Joomla\CMS\Mail\MailTemplate;
  22  use Joomla\CMS\Plugin\CMSPlugin;
  23  use Joomla\CMS\Router\Route;
  24  use Joomla\CMS\Uri\Uri;
  25  use Joomla\CMS\User\UserHelper;
  26  use Joomla\Component\Actionlogs\Administrator\Model\ActionlogModel;
  27  use Joomla\Component\Messages\Administrator\Model\MessageModel;
  28  use Joomla\Database\Exception\ExecutionFailureException;
  29  use Joomla\Database\ParameterType;
  30  use Joomla\Utilities\ArrayHelper;
  31  use PHPMailer\PHPMailer\Exception as phpmailerException;
  32  
  33  // phpcs:disable PSR1.Files.SideEffects
  34  \defined('_JEXEC') or die;
  35  // phpcs:enable PSR1.Files.SideEffects
  36  
  37  /**
  38   * An example custom privacyconsent plugin.
  39   *
  40   * @since  3.9.0
  41   */
  42  class PlgSystemPrivacyconsent extends CMSPlugin
  43  {
  44      /**
  45       * Load the language file on instantiation.
  46       *
  47       * @var    boolean
  48       * @since  3.9.0
  49       */
  50      protected $autoloadLanguage = true;
  51  
  52      /**
  53       * Application object.
  54       *
  55       * @var    \Joomla\CMS\Application\CMSApplication
  56       * @since  3.9.0
  57       */
  58      protected $app;
  59  
  60      /**
  61       * Database object.
  62       *
  63       * @var    \Joomla\Database\DatabaseDriver
  64       * @since  3.9.0
  65       */
  66      protected $db;
  67  
  68      /**
  69       * Adds additional fields to the user editing form
  70       *
  71       * @param   Form   $form  The form to be altered.
  72       * @param   mixed  $data  The associated data for the form.
  73       *
  74       * @return  boolean
  75       *
  76       * @since   3.9.0
  77       */
  78      public function onContentPrepareForm(Form $form, $data)
  79      {
  80          // Check we are manipulating a valid form - we only display this on user registration form and user profile form.
  81          $name = $form->getName();
  82  
  83          if (!in_array($name, ['com_users.profile', 'com_users.registration'])) {
  84              return true;
  85          }
  86  
  87          // We only display this if user has not consented before
  88          if (is_object($data)) {
  89              $userId = $data->id ?? 0;
  90  
  91              if ($userId > 0 && $this->isUserConsented($userId)) {
  92                  return true;
  93              }
  94          }
  95  
  96          // Add the privacy policy fields to the form.
  97          FormHelper::addFieldPrefix('Joomla\\Plugin\\System\\PrivacyConsent\\Field');
  98          FormHelper::addFormPath(__DIR__ . '/forms');
  99          $form->loadFile('privacyconsent');
 100  
 101          $privacyType = $this->params->get('privacy_type', 'article');
 102          $privacyId   = ($privacyType == 'menu_item') ? $this->getPrivacyItemId() : $this->getPrivacyArticleId();
 103          $privacynote = $this->params->get('privacy_note');
 104  
 105          // Push the privacy article ID into the privacy field.
 106          $form->setFieldAttribute('privacy', $privacyType, $privacyId, 'privacyconsent');
 107          $form->setFieldAttribute('privacy', 'note', $privacynote, 'privacyconsent');
 108      }
 109  
 110      /**
 111       * Method is called before user data is stored in the database
 112       *
 113       * @param   array    $user   Holds the old user data.
 114       * @param   boolean  $isNew  True if a new user is stored.
 115       * @param   array    $data   Holds the new user data.
 116       *
 117       * @return  boolean
 118       *
 119       * @since   3.9.0
 120       * @throws  InvalidArgumentException on missing required data.
 121       */
 122      public function onUserBeforeSave($user, $isNew, $data)
 123      {
 124          // // Only check for front-end user creation/update profile
 125          if ($this->app->isClient('administrator')) {
 126              return true;
 127          }
 128  
 129          $userId = ArrayHelper::getValue($user, 'id', 0, 'int');
 130  
 131          // User already consented before, no need to check it further
 132          if ($userId > 0 && $this->isUserConsented($userId)) {
 133              return true;
 134          }
 135  
 136          // Check that the privacy is checked if required ie only in registration from frontend.
 137          $option = $this->app->input->get('option');
 138          $task   = $this->app->input->post->get('task');
 139          $form   = $this->app->input->post->get('jform', [], 'array');
 140  
 141          if (
 142              $option == 'com_users' && in_array($task, array('registration.register', 'profile.save'))
 143              && empty($form['privacyconsent']['privacy'])
 144          ) {
 145              throw new InvalidArgumentException(Text::_('PLG_SYSTEM_PRIVACYCONSENT_FIELD_ERROR'));
 146          }
 147  
 148          return true;
 149      }
 150  
 151      /**
 152       * Saves user privacy confirmation
 153       *
 154       * @param   array    $data    entered user data
 155       * @param   boolean  $isNew   true if this is a new user
 156       * @param   boolean  $result  true if saving the user worked
 157       * @param   string   $error   error message
 158       *
 159       * @return  void
 160       *
 161       * @since   3.9.0
 162       */
 163      public function onUserAfterSave($data, $isNew, $result, $error): void
 164      {
 165          // Only create an entry on front-end user creation/update profile
 166          if ($this->app->isClient('administrator')) {
 167              return;
 168          }
 169  
 170          // Get the user's ID
 171          $userId = ArrayHelper::getValue($data, 'id', 0, 'int');
 172  
 173          // If user already consented before, no need to check it further
 174          if ($userId > 0 && $this->isUserConsented($userId)) {
 175              return;
 176          }
 177  
 178          $option = $this->app->input->get('option');
 179          $task   = $this->app->input->post->get('task');
 180          $form   = $this->app->input->post->get('jform', [], 'array');
 181  
 182          if (
 183              $option == 'com_users'
 184              && in_array($task, ['registration.register', 'profile.save'])
 185              && !empty($form['privacyconsent']['privacy'])
 186          ) {
 187              $userId = ArrayHelper::getValue($data, 'id', 0, 'int');
 188  
 189              // Get the user's IP address
 190              $ip = $this->app->input->server->get('REMOTE_ADDR', '', 'string');
 191  
 192              // Get the user agent string
 193              $userAgent = $this->app->input->server->get('HTTP_USER_AGENT', '', 'string');
 194  
 195              // Create the user note
 196              $userNote = (object) [
 197                  'user_id' => $userId,
 198                  'subject' => 'PLG_SYSTEM_PRIVACYCONSENT_SUBJECT',
 199                  'body'    => Text::sprintf('PLG_SYSTEM_PRIVACYCONSENT_BODY', $ip, $userAgent),
 200                  'created' => Factory::getDate()->toSql(),
 201              ];
 202  
 203              try {
 204                  $this->db->insertObject('#__privacy_consents', $userNote);
 205              } catch (Exception $e) {
 206                  // Do nothing if the save fails
 207              }
 208  
 209              $userId = ArrayHelper::getValue($data, 'id', 0, 'int');
 210  
 211              $message = [
 212                  'action'      => 'consent',
 213                  'id'          => $userId,
 214                  'title'       => $data['name'],
 215                  'itemlink'    => 'index.php?option=com_users&task=user.edit&id=' . $userId,
 216                  'userid'      => $userId,
 217                  'username'    => $data['username'],
 218                  'accountlink' => 'index.php?option=com_users&task=user.edit&id=' . $userId,
 219              ];
 220  
 221              /** @var ActionlogModel $model */
 222              $model = $this->app->bootComponent('com_actionlogs')->getMVCFactory()->createModel('Actionlog', 'Administrator');
 223              $model->addLog([$message], 'PLG_SYSTEM_PRIVACYCONSENT_CONSENT', 'plg_system_privacyconsent', $userId);
 224          }
 225      }
 226  
 227      /**
 228       * Remove all user privacy consent information for the given user ID
 229       *
 230       * Method is called after user data is deleted from the database
 231       *
 232       * @param   array    $user     Holds the user data
 233       * @param   boolean  $success  True if user was successfully stored in the database
 234       * @param   string   $msg      Message
 235       *
 236       * @return  void
 237       *
 238       * @since   3.9.0
 239       */
 240      public function onUserAfterDelete($user, $success, $msg): void
 241      {
 242          if (!$success) {
 243              return;
 244          }
 245  
 246          $userId = ArrayHelper::getValue($user, 'id', 0, 'int');
 247  
 248          if ($userId) {
 249              // Remove user's consent
 250              try {
 251                  $query = $this->db->getQuery(true)
 252                      ->delete($this->db->quoteName('#__privacy_consents'))
 253                      ->where($this->db->quoteName('user_id') . ' = :userid')
 254                      ->bind(':userid', $userId, ParameterType::INTEGER);
 255                  $this->db->setQuery($query);
 256                  $this->db->execute();
 257              } catch (Exception $e) {
 258                  $this->_subject->setError($e->getMessage());
 259              }
 260          }
 261      }
 262  
 263      /**
 264       * If logged in users haven't agreed to privacy consent, redirect them to profile edit page, ask them to agree to
 265       * privacy consent before allowing access to any other pages
 266       *
 267       * @return  void
 268       *
 269       * @since   3.9.0
 270       */
 271      public function onAfterRoute()
 272      {
 273          // Run this in frontend only
 274          if ($this->app->isClient('administrator')) {
 275              return;
 276          }
 277  
 278          $userId = Factory::getUser()->id;
 279  
 280          // Check to see whether user already consented, if not, redirect to user profile page
 281          if ($userId > 0) {
 282              // If user consented before, no need to check it further
 283              if ($this->isUserConsented($userId)) {
 284                  return;
 285              }
 286  
 287              $option = $this->app->input->getCmd('option');
 288              $task   = $this->app->input->get('task');
 289              $view   = $this->app->input->getString('view', '');
 290              $layout = $this->app->input->getString('layout', '');
 291              $id     = $this->app->input->getInt('id');
 292  
 293              $privacyArticleId = $this->getPrivacyArticleId();
 294  
 295              /*
 296               * If user is already on edit profile screen or view privacy article
 297               * or press update/apply button, or logout, do nothing to avoid infinite redirect
 298               */
 299              $allowedUserTasks = [
 300                  'profile.save', 'profile.apply', 'user.logout', 'user.menulogout',
 301                  'method', 'methods', 'captive', 'callback'
 302              ];
 303              $isAllowedUserTask = in_array($task, $allowedUserTasks)
 304                  || substr($task, 0, 8) === 'captive.'
 305                  || substr($task, 0, 8) === 'methods.'
 306                  || substr($task, 0, 7) === 'method.'
 307                  || substr($task, 0, 9) === 'callback.';
 308  
 309              if (
 310                  ($option == 'com_users' && $isAllowedUserTask)
 311                  || ($option == 'com_content' && $view == 'article' && $id == $privacyArticleId)
 312                  || ($option == 'com_users' && $view == 'profile' && $layout == 'edit')
 313              ) {
 314                  return;
 315              }
 316  
 317              // Redirect to com_users profile edit
 318              $this->app->enqueueMessage($this->getRedirectMessage(), 'notice');
 319              $link = 'index.php?option=com_users&view=profile&layout=edit';
 320              $this->app->redirect(Route::_($link, false));
 321          }
 322      }
 323  
 324      /**
 325       * Event to specify whether a privacy policy has been published.
 326       *
 327       * @param   array  &$policy  The privacy policy status data, passed by reference, with keys "published", "editLink" and "articlePublished".
 328       *
 329       * @return  void
 330       *
 331       * @since   3.9.0
 332       */
 333      public function onPrivacyCheckPrivacyPolicyPublished(&$policy)
 334      {
 335          // If another plugin has already indicated a policy is published, we won't change anything here
 336          if ($policy['published']) {
 337              return;
 338          }
 339  
 340          $articleId = (int) $this->params->get('privacy_article');
 341  
 342          if (!$articleId) {
 343              return;
 344          }
 345  
 346          // Check if the article exists in database and is published
 347          $query = $this->db->getQuery(true)
 348              ->select($this->db->quoteName(['id', 'state']))
 349              ->from($this->db->quoteName('#__content'))
 350              ->where($this->db->quoteName('id') . ' = :id')
 351              ->bind(':id', $articleId, ParameterType::INTEGER);
 352          $this->db->setQuery($query);
 353  
 354          $article = $this->db->loadObject();
 355  
 356          // Check if the article exists
 357          if (!$article) {
 358              return;
 359          }
 360  
 361          // Check if the article is published
 362          if ($article->state == 1) {
 363              $policy['articlePublished'] = true;
 364          }
 365  
 366          $policy['published'] = true;
 367          $policy['editLink']  = Route::_('index.php?option=com_content&task=article.edit&id=' . $articleId);
 368      }
 369  
 370      /**
 371       * Returns the configured redirect message and falls back to the default version.
 372       *
 373       * @return  string  redirect message
 374       *
 375       * @since   3.9.0
 376       */
 377      private function getRedirectMessage()
 378      {
 379          $messageOnRedirect = trim($this->params->get('messageOnRedirect', ''));
 380  
 381          if (empty($messageOnRedirect)) {
 382              return Text::_('PLG_SYSTEM_PRIVACYCONSENT_REDIRECT_MESSAGE_DEFAULT');
 383          }
 384  
 385          return $messageOnRedirect;
 386      }
 387  
 388      /**
 389       * Method to check if the given user has consented yet
 390       *
 391       * @param   integer  $userId  ID of uer to check
 392       *
 393       * @return  boolean
 394       *
 395       * @since   3.9.0
 396       */
 397      private function isUserConsented($userId)
 398      {
 399          $userId = (int) $userId;
 400          $db     = $this->db;
 401          $query  = $db->getQuery(true);
 402  
 403          $query->select('COUNT(*)')
 404              ->from($db->quoteName('#__privacy_consents'))
 405              ->where($db->quoteName('user_id') . ' = :userid')
 406              ->where($db->quoteName('subject') . ' = ' . $db->quote('PLG_SYSTEM_PRIVACYCONSENT_SUBJECT'))
 407              ->where($db->quoteName('state') . ' = 1')
 408              ->bind(':userid', $userId, ParameterType::INTEGER);
 409          $db->setQuery($query);
 410  
 411          return (int) $db->loadResult() > 0;
 412      }
 413  
 414      /**
 415       * Get privacy article ID. If the site is a multilingual website and there is associated article for the
 416       * current language, ID of the associated article will be returned
 417       *
 418       * @return  integer
 419       *
 420       * @since   3.9.0
 421       */
 422      private function getPrivacyArticleId()
 423      {
 424          $privacyArticleId = $this->params->get('privacy_article');
 425  
 426          if ($privacyArticleId > 0 && Associations::isEnabled()) {
 427              $privacyAssociated = Associations::getAssociations('com_content', '#__content', 'com_content.item', $privacyArticleId);
 428              $currentLang = Factory::getLanguage()->getTag();
 429  
 430              if (isset($privacyAssociated[$currentLang])) {
 431                  $privacyArticleId = $privacyAssociated[$currentLang]->id;
 432              }
 433          }
 434  
 435          return $privacyArticleId;
 436      }
 437  
 438      /**
 439       * Get privacy menu item ID. If the site is a multilingual website and there is associated menu item for the
 440       * current language, ID of the associated menu item will be returned.
 441       *
 442       * @return  integer
 443       *
 444       * @since   4.0.0
 445       */
 446      private function getPrivacyItemId()
 447      {
 448          $itemId = $this->params->get('privacy_menu_item');
 449  
 450          if ($itemId > 0 && Associations::isEnabled()) {
 451              $privacyAssociated = Associations::getAssociations('com_menus', '#__menu', 'com_menus.item', $itemId, 'id', '', '');
 452              $currentLang = Factory::getLanguage()->getTag();
 453  
 454              if (isset($privacyAssociated[$currentLang])) {
 455                  $itemId = $privacyAssociated[$currentLang]->id;
 456              }
 457          }
 458  
 459          return $itemId;
 460      }
 461  
 462      /**
 463       * The privacy consent expiration check code is triggered after the page has fully rendered.
 464       *
 465       * @return  void
 466       *
 467       * @since   3.9.0
 468       */
 469      public function onAfterRender()
 470      {
 471          if (!$this->params->get('enabled', 0)) {
 472              return;
 473          }
 474  
 475          $cacheTimeout = (int) $this->params->get('cachetimeout', 30);
 476          $cacheTimeout = 24 * 3600 * $cacheTimeout;
 477  
 478          // Do we need to run? Compare the last run timestamp stored in the plugin's options with the current
 479          // timestamp. If the difference is greater than the cache timeout we shall not execute again.
 480          $now  = time();
 481          $last = (int) $this->params->get('lastrun', 0);
 482  
 483          if ((abs($now - $last) < $cacheTimeout)) {
 484              return;
 485          }
 486  
 487          // Update last run status
 488          $this->params->set('lastrun', $now);
 489  
 490          $paramsJson = $this->params->toString('JSON');
 491          $db         = $this->db;
 492          $query      = $db->getQuery(true)
 493              ->update($db->quoteName('#__extensions'))
 494              ->set($db->quoteName('params') . ' = :params')
 495              ->where($db->quoteName('type') . ' = ' . $db->quote('plugin'))
 496              ->where($db->quoteName('folder') . ' = ' . $db->quote('system'))
 497              ->where($db->quoteName('element') . ' = ' . $db->quote('privacyconsent'))
 498              ->bind(':params', $paramsJson);
 499  
 500          try {
 501              // Lock the tables to prevent multiple plugin executions causing a race condition
 502              $db->lockTable('#__extensions');
 503          } catch (Exception $e) {
 504              // If we can't lock the tables it's too risky to continue execution
 505              return;
 506          }
 507  
 508          try {
 509              // Update the plugin parameters
 510              $result = $db->setQuery($query)->execute();
 511              $this->clearCacheGroups(['com_plugins'], [0, 1]);
 512          } catch (Exception $exc) {
 513              // If we failed to execute
 514              $db->unlockTables();
 515              $result = false;
 516          }
 517  
 518          try {
 519              // Unlock the tables after writing
 520              $db->unlockTables();
 521          } catch (Exception $e) {
 522              // If we can't lock the tables assume we have somehow failed
 523              $result = false;
 524          }
 525  
 526          // Abort on failure
 527          if (!$result) {
 528              return;
 529          }
 530  
 531          // Delete the expired privacy consents
 532          $this->invalidateExpiredConsents();
 533  
 534          // Remind for privacy consents near to expire
 535          $this->remindExpiringConsents();
 536      }
 537  
 538      /**
 539       * Method to send the remind for privacy consents renew
 540       *
 541       * @return  integer
 542       *
 543       * @since   3.9.0
 544       */
 545      private function remindExpiringConsents()
 546      {
 547          // Load the parameters.
 548          $expire   = (int) $this->params->get('consentexpiration', 365);
 549          $remind   = (int) $this->params->get('remind', 30);
 550          $now      = Factory::getDate()->toSql();
 551          $period   = '-' . ($expire - $remind);
 552          $db       = $this->db;
 553          $query    = $db->getQuery(true);
 554  
 555          $query->select($db->quoteName(['r.id', 'r.user_id', 'u.email']))
 556              ->from($db->quoteName('#__privacy_consents', 'r'))
 557              ->join('LEFT', $db->quoteName('#__users', 'u'), $db->quoteName('u.id') . ' = ' . $db->quoteName('r.user_id'))
 558              ->where($db->quoteName('subject') . ' = ' . $db->quote('PLG_SYSTEM_PRIVACYCONSENT_SUBJECT'))
 559              ->where($db->quoteName('remind') . ' = 0')
 560              ->where($query->dateAdd($db->quote($now), $period, 'DAY') . ' > ' . $db->quoteName('created'));
 561  
 562          try {
 563              $users = $db->setQuery($query)->loadObjectList();
 564          } catch (ExecutionFailureException $exception) {
 565              return false;
 566          }
 567  
 568          $app      = Factory::getApplication();
 569          $linkMode = $app->get('force_ssl', 0) == 2 ? Route::TLS_FORCE : Route::TLS_IGNORE;
 570  
 571          foreach ($users as $user) {
 572              $token       = ApplicationHelper::getHash(UserHelper::genRandomPassword());
 573              $hashedToken = UserHelper::hashPassword($token);
 574  
 575              // The mail
 576              try {
 577                  $templateData = [
 578                      'sitename' => $app->get('sitename'),
 579                      'url'      => Uri::root(),
 580                      'tokenurl' => Route::link('site', 'index.php?option=com_privacy&view=remind&remind_token=' . $token, false, $linkMode, true),
 581                      'formurl'  => Route::link('site', 'index.php?option=com_privacy&view=remind', false, $linkMode, true),
 582                      'token'    => $token,
 583                  ];
 584  
 585                  $mailer = new MailTemplate('plg_system_privacyconsent.request.reminder', $app->getLanguage()->getTag());
 586                  $mailer->addTemplateData($templateData);
 587                  $mailer->addRecipient($user->email);
 588  
 589                  $mailResult = $mailer->send();
 590  
 591                  if ($mailResult === false) {
 592                      return false;
 593                  }
 594  
 595                  $userId = (int) $user->id;
 596  
 597                  // Update the privacy_consents item to not send the reminder again
 598                  $query->clear()
 599                      ->update($db->quoteName('#__privacy_consents'))
 600                      ->set($db->quoteName('remind') . ' = 1')
 601                      ->set($db->quoteName('token') . ' = :token')
 602                      ->where($db->quoteName('id') . ' = :userid')
 603                      ->bind(':token', $hashedToken)
 604                      ->bind(':userid', $userId, ParameterType::INTEGER);
 605                  $db->setQuery($query);
 606  
 607                  try {
 608                      $db->execute();
 609                  } catch (RuntimeException $e) {
 610                      return false;
 611                  }
 612              } catch (MailDisabledException | phpmailerException $exception) {
 613                  return false;
 614              }
 615          }
 616      }
 617  
 618      /**
 619       * Method to delete the expired privacy consents
 620       *
 621       * @return  boolean
 622       *
 623       * @since   3.9.0
 624       */
 625      private function invalidateExpiredConsents()
 626      {
 627          // Load the parameters.
 628          $expire = (int) $this->params->get('consentexpiration', 365);
 629          $now    = Factory::getDate()->toSql();
 630          $period = '-' . $expire;
 631          $db     = $this->db;
 632          $query  = $db->getQuery(true);
 633  
 634          $query->select($db->quoteName(['id', 'user_id']))
 635              ->from($db->quoteName('#__privacy_consents'))
 636              ->where($query->dateAdd($db->quote($now), $period, 'DAY') . ' > ' . $db->quoteName('created'))
 637              ->where($db->quoteName('subject') . ' = ' . $db->quote('PLG_SYSTEM_PRIVACYCONSENT_SUBJECT'))
 638              ->where($db->quoteName('state') . ' = 1');
 639  
 640          $db->setQuery($query);
 641  
 642          try {
 643              $users = $db->loadObjectList();
 644          } catch (RuntimeException $e) {
 645              return false;
 646          }
 647  
 648          // Do not process further if no expired consents found
 649          if (empty($users)) {
 650              return true;
 651          }
 652  
 653          // Push a notification to the site's super users
 654          /** @var MessageModel $messageModel */
 655          $messageModel = $this->app->bootComponent('com_messages')->getMVCFactory()->createModel('Message', 'Administrator');
 656  
 657          foreach ($users as $user) {
 658              $userId = (int) $user->id;
 659              $query = $db->getQuery(true)
 660                  ->update($db->quoteName('#__privacy_consents'))
 661                  ->set($db->quoteName('state') . ' = 0')
 662                  ->where($db->quoteName('id') . ' = :userid')
 663                  ->bind(':userid', $userId, ParameterType::INTEGER);
 664              $db->setQuery($query);
 665  
 666              try {
 667                  $db->execute();
 668              } catch (RuntimeException $e) {
 669                  return false;
 670              }
 671  
 672              $messageModel->notifySuperUsers(
 673                  Text::_('PLG_SYSTEM_PRIVACYCONSENT_NOTIFICATION_USER_PRIVACY_EXPIRED_SUBJECT'),
 674                  Text::sprintf('PLG_SYSTEM_PRIVACYCONSENT_NOTIFICATION_USER_PRIVACY_EXPIRED_MESSAGE', Factory::getUser($user->user_id)->username)
 675              );
 676          }
 677  
 678          return true;
 679      }
 680      /**
 681       * Clears cache groups. We use it to clear the plugins cache after we update the last run timestamp.
 682       *
 683       * @param   array  $clearGroups   The cache groups to clean
 684       * @param   array  $cacheClients  The cache clients (site, admin) to clean
 685       *
 686       * @return  void
 687       *
 688       * @since    3.9.0
 689       */
 690      private function clearCacheGroups(array $clearGroups, array $cacheClients = [0, 1])
 691      {
 692          foreach ($clearGroups as $group) {
 693              foreach ($cacheClients as $client_id) {
 694                  try {
 695                      $options = [
 696                          'defaultgroup' => $group,
 697                          'cachebase'    => $client_id ? JPATH_ADMINISTRATOR . '/cache' :
 698                              Factory::getApplication()->get('cache_path', JPATH_SITE . '/cache'),
 699                      ];
 700  
 701                      $cache = Cache::getInstance('callback', $options);
 702                      $cache->clean();
 703                  } catch (Exception $e) {
 704                      // Ignore it
 705                  }
 706              }
 707          }
 708      }
 709  }


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