primo commit
This commit is contained in:
		
							
								
								
									
										605
									
								
								plugins/user/token/src/Extension/Token.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										605
									
								
								plugins/user/token/src/Extension/Token.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,605 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * @package     Joomla.Plugin | ||||
|  * @subpackage  User.token | ||||
|  * | ||||
|  * @copyright   (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> | ||||
|  * @license     GNU General Public License version 2 or later; see LICENSE.txt | ||||
|  */ | ||||
|  | ||||
| namespace Joomla\Plugin\User\Token\Extension; | ||||
|  | ||||
| use Joomla\CMS\Crypt\Crypt; | ||||
| use Joomla\CMS\Form\Form; | ||||
| use Joomla\CMS\Plugin\CMSPlugin; | ||||
| use Joomla\CMS\Plugin\PluginHelper; | ||||
| use Joomla\CMS\User\UserFactoryAwareTrait; | ||||
| use Joomla\Database\DatabaseAwareTrait; | ||||
| use Joomla\Database\ParameterType; | ||||
| use Joomla\Utilities\ArrayHelper; | ||||
|  | ||||
| // phpcs:disable PSR1.Files.SideEffects | ||||
| \defined('_JEXEC') or die; | ||||
| // phpcs:enable PSR1.Files.SideEffects | ||||
|  | ||||
| /** | ||||
|  * An example custom terms and conditions plugin. | ||||
|  * | ||||
|  * @since  3.9.0 | ||||
|  */ | ||||
| final class Token extends CMSPlugin | ||||
| { | ||||
|     use DatabaseAwareTrait; | ||||
|     use UserFactoryAwareTrait; | ||||
|  | ||||
|     /** | ||||
|      * Joomla XML form contexts where we should inject our token management user interface. | ||||
|      * | ||||
|      * @var     array | ||||
|      * @since   4.0.0 | ||||
|      */ | ||||
|     private $allowedContexts = [ | ||||
|         'com_users.profile', | ||||
|         'com_users.user', | ||||
|     ]; | ||||
|  | ||||
|     /** | ||||
|      * The prefix of the user profile keys, without the dot. | ||||
|      * | ||||
|      * @var     string | ||||
|      * @since   4.0.0 | ||||
|      */ | ||||
|     private $profileKeyPrefix = 'joomlatoken'; | ||||
|  | ||||
|     /** | ||||
|      * Token length, in bytes. | ||||
|      * | ||||
|      * @var     integer | ||||
|      * @since   4.0.0 | ||||
|      */ | ||||
|     private $tokenLength = 32; | ||||
|  | ||||
|     /** | ||||
|      * Inject the Joomla token management panel's data into the User Profile. | ||||
|      * | ||||
|      * This method is called whenever Joomla is preparing the data for an XML form for display. | ||||
|      * | ||||
|      * @param   string  $context  Form context, passed by Joomla | ||||
|      * @param   mixed   $data     Form data | ||||
|      * | ||||
|      * @return  boolean | ||||
|      * @since   4.0.0 | ||||
|      */ | ||||
|     public function onContentPrepareData(string $context, &$data): bool | ||||
|     { | ||||
|         // Only do something if the api-authentication plugin with the same name is published | ||||
|         if (!PluginHelper::isEnabled('api-authentication', $this->_name)) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         // Check we are manipulating a valid form. | ||||
|         if (!\in_array($context, $this->allowedContexts)) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         // $data must be an object | ||||
|         if (!\is_object($data)) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         // We expect the numeric user ID in $data->id | ||||
|         if (!isset($data->id)) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         // Get the user ID | ||||
|         $userId = \intval($data->id); | ||||
|  | ||||
|         // Make sure we have a positive integer user ID | ||||
|         if ($userId <= 0) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         if (!$this->isInAllowedUserGroup($userId)) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         // Load plugin language files | ||||
|         $this->loadLanguage(); | ||||
|  | ||||
|         $data->{$this->profileKeyPrefix} = []; | ||||
|  | ||||
|         // Load the profile data from the database. | ||||
|         try { | ||||
|             $db    = $this->getDatabase(); | ||||
|             $query = $db->getQuery(true) | ||||
|                 ->select([ | ||||
|                         $db->quoteName('profile_key'), | ||||
|                         $db->quoteName('profile_value'), | ||||
|                     ]) | ||||
|                 ->from($db->quoteName('#__user_profiles')) | ||||
|                 ->where($db->quoteName('user_id') . ' = :userId') | ||||
|                 ->where($db->quoteName('profile_key') . ' LIKE :profileKey') | ||||
|                 ->order($db->quoteName('ordering')); | ||||
|  | ||||
|             $profileKey = $this->profileKeyPrefix . '.%'; | ||||
|             $query->bind(':userId', $userId, ParameterType::INTEGER); | ||||
|             $query->bind(':profileKey', $profileKey, ParameterType::STRING); | ||||
|  | ||||
|             $results = $db->setQuery($query)->loadRowList(); | ||||
|  | ||||
|             foreach ($results as $v) { | ||||
|                 $k = str_replace($this->profileKeyPrefix . '.', '', $v[0]); | ||||
|  | ||||
|                 $data->{$this->profileKeyPrefix}[$k] = $v[1]; | ||||
|             } | ||||
|         } catch (\Exception $e) { | ||||
|             // We suppress any database error. It means we get no token saved by default. | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Modify the data for display in the user profile view page in the frontend. | ||||
|          * | ||||
|          * It's important to note that we deliberately not register HTMLHelper methods to do the | ||||
|          * same (unlike e.g. the actionlogs system plugin) because the names of our fields are too | ||||
|          * generic and we run the risk of creating naming clashes. Instead, we manipulate the data | ||||
|          * directly. | ||||
|          */ | ||||
|         if (($context === 'com_users.profile') && ($this->getApplication()->getInput()->get('layout') !== 'edit')) { | ||||
|             $pluginData = $data->{$this->profileKeyPrefix} ?? []; | ||||
|             $enabled    = $pluginData['enabled'] ?? false; | ||||
|             $token      = $pluginData['token'] ?? ''; | ||||
|  | ||||
|             $pluginData['enabled'] = $this->getApplication()->getLanguage()->_('JDISABLED'); | ||||
|             $pluginData['token']   = ''; | ||||
|  | ||||
|             if ($enabled) { | ||||
|                 $algo                  = $this->getAlgorithmFromFormFile(); | ||||
|                 $pluginData['enabled'] = $this->getApplication()->getLanguage()->_('JENABLED'); | ||||
|                 $pluginData['token']   = $this->getTokenForDisplay($userId, $token, $algo); | ||||
|             } | ||||
|  | ||||
|             $data->{$this->profileKeyPrefix} = $pluginData; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Runs whenever Joomla is preparing a form object. | ||||
|      * | ||||
|      * @param   Form   $form  The form to be altered. | ||||
|      * @param   mixed  $data  The associated data for the form. | ||||
|      * | ||||
|      * @return  boolean | ||||
|      * | ||||
|      * @throws  \Exception  When $form is not a valid form object | ||||
|      * @since   4.0.0 | ||||
|      */ | ||||
|     public function onContentPrepareForm(Form $form, $data): bool | ||||
|     { | ||||
|         // Only do something if the api-authentication plugin with the same name is published | ||||
|         if (!PluginHelper::isEnabled('api-authentication', $this->_name)) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         // Check we are manipulating a valid form. | ||||
|         if (!\in_array($form->getName(), $this->allowedContexts)) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         // If we are on the save command, no data is passed to $data variable, we need to get it directly from request | ||||
|         $jformData = $this->getApplication()->getInput()->get('jform', [], 'array'); | ||||
|  | ||||
|         if ($jformData && !$data) { | ||||
|             $data = $jformData; | ||||
|         } | ||||
|  | ||||
|         if (\is_array($data)) { | ||||
|             $data = (object) $data; | ||||
|         } | ||||
|  | ||||
|         // Check if the user belongs to an allowed user group | ||||
|         $userId = (\is_object($data) && isset($data->id)) ? $data->id : 0; | ||||
|  | ||||
|         if (!empty($userId) && !$this->isInAllowedUserGroup($userId)) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         // Load plugin language files | ||||
|         $this->loadLanguage(); | ||||
|  | ||||
|         // Add the registration fields to the form. | ||||
|         Form::addFormPath(JPATH_PLUGINS . '/' . $this->_type . '/' . $this->_name . '/forms'); | ||||
|         $form->loadFile('token', false); | ||||
|  | ||||
|         // No token: no reset | ||||
|         $userTokenSeed = $this->getTokenSeedForUser($userId); | ||||
|         $currentUser   = $this->getApplication()->getIdentity(); | ||||
|  | ||||
|         if (empty($userTokenSeed)) { | ||||
|             $form->removeField('notokenforotherpeople', 'joomlatoken'); | ||||
|             $form->removeField('reset', 'joomlatoken'); | ||||
|             $form->removeField('token', 'joomlatoken'); | ||||
|             $form->removeField('enabled', 'joomlatoken'); | ||||
|         } else { | ||||
|             $form->removeField('saveme', 'joomlatoken'); | ||||
|         } | ||||
|  | ||||
|         if ($userId != $currentUser->id) { | ||||
|             $form->removeField('token', 'joomlatoken'); | ||||
|         } else { | ||||
|             $form->removeField('notokenforotherpeople', 'joomlatoken'); | ||||
|         } | ||||
|  | ||||
|         if (($userId != $currentUser->id) && empty($userTokenSeed)) { | ||||
|             $form->removeField('saveme', 'joomlatoken'); | ||||
|         } else { | ||||
|             $form->removeField('savemeforotherpeople', 'joomlatoken'); | ||||
|         } | ||||
|  | ||||
|         // Remove the Reset field when displaying the user profile form | ||||
|         if (($form->getName() === 'com_users.profile') && ($this->getApplication()->getInput()->get('layout') !== 'edit')) { | ||||
|             $form->removeField('reset', 'joomlatoken'); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Save the Joomla token in the user profile field | ||||
|      * | ||||
|      * @param   mixed    $data    The incoming form data | ||||
|      * @param   bool     $isNew   Is this a new user? | ||||
|      * @param   bool     $result  Has Joomla successfully saved the user? | ||||
|      * @param   ?string  $error   Error string | ||||
|      * | ||||
|      * @return  void | ||||
|      * @since   4.0.0 | ||||
|      */ | ||||
|     public function onUserAfterSave($data, bool $isNew, bool $result, ?string $error): void | ||||
|     { | ||||
|         if (!\is_array($data)) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $userId = ArrayHelper::getValue($data, 'id', 0, 'int'); | ||||
|  | ||||
|         if ($userId <= 0) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (!$result) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $noToken = false; | ||||
|  | ||||
|         // No Joomla token data. Set the $noToken flag which results in a new token being generated. | ||||
|         if (!isset($data[$this->profileKeyPrefix])) { | ||||
|             /** | ||||
|              * Is the user being saved programmatically, without passing the user profile | ||||
|              * information? In this case I do not want to accidentally try to generate a new token! | ||||
|              * | ||||
|              * We determine that by examining whether the Joomla token field exists. If it does but | ||||
|              * it wasn't passed when saving the user I know it's a programmatic user save and I have | ||||
|              * to ignore it. | ||||
|              */ | ||||
|             if ($this->hasTokenProfileFields($userId)) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             $noToken                       = true; | ||||
|             $data[$this->profileKeyPrefix] = []; | ||||
|         } | ||||
|  | ||||
|         if (isset($data[$this->profileKeyPrefix]['reset'])) { | ||||
|             $reset = $data[$this->profileKeyPrefix]['reset'] == 1; | ||||
|             unset($data[$this->profileKeyPrefix]['reset']); | ||||
|  | ||||
|             if ($reset) { | ||||
|                 $noToken = true; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // We may have a token already saved. Let's check, shall we? | ||||
|         if (!$noToken) { | ||||
|             $noToken       = true; | ||||
|             $existingToken = $this->getTokenSeedForUser($userId); | ||||
|  | ||||
|             if (!empty($existingToken)) { | ||||
|                 $noToken                                = false; | ||||
|                 $data[$this->profileKeyPrefix]['token'] = $existingToken; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // If there is no token or this is a new user generate a new token. | ||||
|         if ($noToken || $isNew) { | ||||
|             if ( | ||||
|                 isset($data[$this->profileKeyPrefix]['token']) | ||||
|                 && empty($data[$this->profileKeyPrefix]['token']) | ||||
|             ) { | ||||
|                 unset($data[$this->profileKeyPrefix]['token']); | ||||
|             } | ||||
|  | ||||
|             $default                       = $this->getDefaultProfileFieldValues(); | ||||
|             $data[$this->profileKeyPrefix] = array_merge($default, $data[$this->profileKeyPrefix]); | ||||
|         } | ||||
|  | ||||
|         // Remove existing Joomla Token user profile values | ||||
|         $db    = $this->getDatabase(); | ||||
|         $query = $db->getQuery(true) | ||||
|             ->delete($db->quoteName('#__user_profiles')) | ||||
|             ->where($db->quoteName('user_id') . ' = :userId') | ||||
|             ->where($db->quoteName('profile_key') . ' LIKE :profileKey'); | ||||
|  | ||||
|         $profileKey = $this->profileKeyPrefix . '.%'; | ||||
|         $query->bind(':userId', $userId, ParameterType::INTEGER); | ||||
|         $query->bind(':profileKey', $profileKey, ParameterType::STRING); | ||||
|  | ||||
|         $db->setQuery($query)->execute(); | ||||
|  | ||||
|         // If the user is not in the allowed user group don't save any new token information. | ||||
|         if (!$this->isInAllowedUserGroup($data['id'])) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Save the new Joomla Token user profile values | ||||
|         $order = 1; | ||||
|         $query = $db->getQuery(true) | ||||
|             ->insert($db->quoteName('#__user_profiles')) | ||||
|             ->columns([ | ||||
|                     $db->quoteName('user_id'), | ||||
|                     $db->quoteName('profile_key'), | ||||
|                     $db->quoteName('profile_value'), | ||||
|                     $db->quoteName('ordering'), | ||||
|                 ]); | ||||
|  | ||||
|         foreach ($data[$this->profileKeyPrefix] as $k => $v) { | ||||
|             $query->values($userId . ', ' | ||||
|                 . $db->quote($this->profileKeyPrefix . '.' . $k) | ||||
|                 . ', ' . $db->quote($v) | ||||
|                 . ', ' . ($order++)); | ||||
|         } | ||||
|  | ||||
|         $db->setQuery($query)->execute(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove the Joomla token when the user account is deleted from the database. | ||||
|      * | ||||
|      * This event is called after the user data is deleted from the database. | ||||
|      * | ||||
|      * @param   array    $user     Holds the user data | ||||
|      * @param   boolean  $success  True if user was successfully stored in the database | ||||
|      * @param   string   $msg      Message | ||||
|      * | ||||
|      * @return  void | ||||
|      * | ||||
|      * @throws  \Exception | ||||
|      * @since   4.0.0 | ||||
|      */ | ||||
|     public function onUserAfterDelete(array $user, bool $success, string $msg): void | ||||
|     { | ||||
|         if (!$success) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $userId = ArrayHelper::getValue($user, 'id', 0, 'int'); | ||||
|  | ||||
|         if ($userId <= 0) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         try { | ||||
|             $db    = $this->getDatabase(); | ||||
|             $query = $db->getQuery(true) | ||||
|                 ->delete($db->quoteName('#__user_profiles')) | ||||
|                 ->where($db->quoteName('user_id') . ' = :userId') | ||||
|                 ->where($db->quoteName('profile_key') . ' LIKE :profileKey'); | ||||
|  | ||||
|             $profileKey = $this->profileKeyPrefix . '.%'; | ||||
|             $query->bind(':userId', $userId, ParameterType::INTEGER); | ||||
|             $query->bind(':profileKey', $profileKey, ParameterType::STRING); | ||||
|  | ||||
|             $db->setQuery($query)->execute(); | ||||
|         } catch (\Exception $e) { | ||||
|             // Do nothing. | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns an array with the default profile field values. | ||||
|      * | ||||
|      * This is used when saving the form data of a user (new or existing) without a token already | ||||
|      * set. | ||||
|      * | ||||
|      * @return  array | ||||
|      * @since   4.0.0 | ||||
|      */ | ||||
|     private function getDefaultProfileFieldValues(): array | ||||
|     { | ||||
|         return [ | ||||
|             'token'   => base64_encode(Crypt::genRandomBytes($this->tokenLength)), | ||||
|             'enabled' => true, | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Retrieve the token seed string for the given user ID. | ||||
|      * | ||||
|      * @param   int  $userId  The numeric user ID to return the token seed string for. | ||||
|      * | ||||
|      * @return  string|null  Null if there is no token configured or the user doesn't exist. | ||||
|      * @since   4.0.0 | ||||
|      */ | ||||
|     private function getTokenSeedForUser(int $userId): ?string | ||||
|     { | ||||
|         try { | ||||
|             $db    = $this->getDatabase(); | ||||
|             $query = $db->getQuery(true) | ||||
|                 ->select($db->quoteName('profile_value')) | ||||
|                 ->from($db->quoteName('#__user_profiles')) | ||||
|                 ->where($db->quoteName('profile_key') . ' = :profileKey') | ||||
|                 ->where($db->quoteName('user_id') . ' = :userId'); | ||||
|  | ||||
|             $profileKey = $this->profileKeyPrefix . '.token'; | ||||
|             $query->bind(':profileKey', $profileKey, ParameterType::STRING); | ||||
|             $query->bind(':userId', $userId, ParameterType::INTEGER); | ||||
|  | ||||
|             return $db->setQuery($query)->loadResult(); | ||||
|         } catch (\Exception $e) { | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the configured user groups which are allowed to have access to tokens. | ||||
|      * | ||||
|      * @return  int[] | ||||
|      * @since   4.0.0 | ||||
|      */ | ||||
|     private function getAllowedUserGroups(): array | ||||
|     { | ||||
|         $userGroups = $this->params->get('allowedUserGroups', [8]); | ||||
|  | ||||
|         if (empty($userGroups)) { | ||||
|             return []; | ||||
|         } | ||||
|  | ||||
|         if (!\is_array($userGroups)) { | ||||
|             $userGroups = [$userGroups]; | ||||
|         } | ||||
|  | ||||
|         return $userGroups; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Is the user with the given ID in the allowed User Groups with access to tokens? | ||||
|      * | ||||
|      * @param   int  $userId  The user ID to check | ||||
|      * | ||||
|      * @return  boolean  False when doesn't belong to allowed user groups, user not found, or guest | ||||
|      * @since   4.0.0 | ||||
|      */ | ||||
|     private function isInAllowedUserGroup($userId) | ||||
|     { | ||||
|         $allowedUserGroups = $this->getAllowedUserGroups(); | ||||
|  | ||||
|         $user = $this->getUserFactory()->loadUserById($userId); | ||||
|  | ||||
|         if ($user->id != $userId) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if ($user->guest) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // No specifically allowed user groups: allow ALL user groups. | ||||
|         if (empty($allowedUserGroups)) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         $groups       = $user->getAuthorisedGroups(); | ||||
|         $intersection = array_intersect($groups, $allowedUserGroups); | ||||
|  | ||||
|         return !empty($intersection); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the token formatted suitably for the user to copy. | ||||
|      * | ||||
|      * @param   integer  $userId     The user id for token | ||||
|      * @param   string   $tokenSeed  The token seed data stored in the database | ||||
|      * @param   string   $algorithm  The hashing algorithm to use for the token (default: sha256) | ||||
|      * | ||||
|      * @return  string | ||||
|      * @since   4.0.0 | ||||
|      */ | ||||
|     private function getTokenForDisplay( | ||||
|         int $userId, | ||||
|         string $tokenSeed, | ||||
|         string $algorithm = 'sha256' | ||||
|     ): string { | ||||
|         if (empty($tokenSeed)) { | ||||
|             return ''; | ||||
|         } | ||||
|  | ||||
|         try { | ||||
|             $siteSecret = $this->getApplication()->get('secret'); | ||||
|         } catch (\Exception $e) { | ||||
|             $siteSecret = ''; | ||||
|         } | ||||
|  | ||||
|         // NO site secret? You monster! | ||||
|         if (empty($siteSecret)) { | ||||
|             return ''; | ||||
|         } | ||||
|  | ||||
|         $rawToken  = base64_decode($tokenSeed); | ||||
|         $tokenHash = hash_hmac($algorithm, $rawToken, $siteSecret); | ||||
|         $message   = base64_encode("$algorithm:$userId:$tokenHash"); | ||||
|  | ||||
|         if ($userId !== $this->getApplication()->getIdentity()->id) { | ||||
|             $message = ''; | ||||
|         } | ||||
|  | ||||
|         return $message; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the token algorithm as defined in the form file | ||||
|      * | ||||
|      * We use a simple RegEx match instead of loading the form for better performance. | ||||
|      * | ||||
|      * @return  string  The configured algorithm, 'sha256' as a fallback if none is found. | ||||
|      */ | ||||
|     private function getAlgorithmFromFormFile(): string | ||||
|     { | ||||
|         $algo = 'sha256'; | ||||
|  | ||||
|         $file     = JPATH_PLUGINS . '/' . $this->_type . '/' . $this->_name . '/forms/token.xml'; | ||||
|         $contents = @file_get_contents($file); | ||||
|  | ||||
|         if ($contents === false) { | ||||
|             return $algo; | ||||
|         } | ||||
|  | ||||
|         if (preg_match('/\s*algo=\s*"\s*([a-z0-9]+)\s*"/i', $contents, $matches) !== 1) { | ||||
|             return $algo; | ||||
|         } | ||||
|  | ||||
|         return $matches[1]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Does the user have the Joomla Token profile fields? | ||||
|      * | ||||
|      * @param   int|null  $userId  The user we're interested in | ||||
|      * | ||||
|      * @return  bool  True if the user has Joomla Token profile fields | ||||
|      */ | ||||
|     private function hasTokenProfileFields(?int $userId): bool | ||||
|     { | ||||
|         if (\is_null($userId) || ($userId <= 0)) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         $db = $this->getDatabase(); | ||||
|         $q  = $db->getQuery(true) | ||||
|             ->select('COUNT(*)') | ||||
|             ->from($db->quoteName('#__user_profiles')) | ||||
|             ->where($db->quoteName('user_id') . ' = ' . $userId) | ||||
|             ->where($db->quoteName('profile_key') . ' = ' . $db->quote($this->profileKeyPrefix . '.token')); | ||||
|  | ||||
|         try { | ||||
|             $numRows = $db->setQuery($q)->loadResult() ?? 0; | ||||
|         } catch (\Exception $e) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return $numRows > 0; | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user