Release of v5.0.3-alpha4

Fix database default fields to allow NULL. #1169. Fix the power list field to allow search. #1167. Expanded the Demo component in JCB v4 to include more advance features.
This commit is contained in:
2024-09-18 04:40:33 +02:00
parent fc8fabe298
commit 0603c39cc1
129 changed files with 7128 additions and 1449 deletions

View File

@@ -0,0 +1,23 @@
<?php
/**
* @package Joomla.Component.Builder
*
* @created 3rd September, 2020
* @author Llewellyn van der Merwe <https://dev.vdm.io>
* @git Joomla Component Builder <https://git.vdm.dev/joomla/Component-Builder>
* @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace VDM\Joomla\Componentbuilder\Utilities\Exception;
/**
* No User Id Found Exception
*
* @since 5.0.2
*/
class NoUserIdFoundException extends \InvalidArgumentException
{
}

View File

@@ -0,0 +1 @@
<html><body bgcolor="#FFFFFF"></body></html>

View File

@@ -0,0 +1,481 @@
<?php
/**
* @package Joomla.Component.Builder
*
* @created 3rd September, 2020
* @author Llewellyn van der Merwe <https://dev.vdm.io>
* @git Joomla Component Builder <https://git.vdm.dev/joomla/Component-Builder>
* @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace VDM\Joomla\Componentbuilder\Utilities;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Language\Text;
use Joomla\CMS\User\User;
use Joomla\CMS\User\UserHelper as JoomlaUserHelper;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
use VDM\Joomla\Utilities\Component\Helper as Component;
use VDM\Joomla\Utilities\ArrayHelper;
use VDM\Joomla\Utilities\StringHelper;
use VDM\Joomla\Componentbuilder\Utilities\Exception\NoUserIdFoundException;
use VDM\Component\Componentbuilder\Administrator\Helper\ComponentbuilderHelper;
/**
* Create & Update User [Save]
*
* @since 5.0.2
*/
abstract class UserHelper
{
/**
* Save user details by either creating a new user or updating an existing user.
*
* @param array $credentials User credentials including 'name', 'username', 'email', 'password', and 'password2'.
* @param int $autologin Flag to determine whether to auto-login the user after registration.
* @param array $params Parameters for user activation, password sending, and user registration allowance.
* @param int $mode Mode of registration: 1 = Site Registration, 0 = Admin Registration, 2 = Custom Helper Method.
*
* @return int User ID on success.
*
* @throws \InvalidArgumentException If required credentials are missing.
* @throws \RuntimeException If the user update or creation fails.
* @throws NoUserIdFoundException If the user is not found.
*
* @since 5.0.3
*/
public static function save(array $credentials, int $autologin = 0,
array $params = ['useractivation' => 0, 'sendpassword' => 1], int $mode = 1): int
{
// can not continue without an email
if (empty($credentials['email']))
{
throw new \InvalidArgumentException(Text::_('COM_COMPONENTBUILDER_CAN_NOT_SAVE_USER_WITHOUT_EMAIL_VALUE'));
}
// Ensure the 'username' key exists in the credentials array, set to an empty string if not provided.
$username = $credentials['username'] ?? $credentials['email'];
// If the user's ID is set and valid, handle the update logic.
if (!empty($credentials['id']) && $credentials['id'] > 0)
{
$userId = $credentials['id'];
$email = $credentials['email'];
// Fetch existing user by email and username.
$existingEmailUserId = static::getUserIdByEmail($email);
$existingUsernameId = static::getUserIdByUsername($username);
// Validate that we aren't attempting to update other users or reuse another user's email/username.
if (
($existingEmailUserId && $existingEmailUserId != $userId) ||
($existingUsernameId && $existingUsernameId != $userId) ||
($existingEmailUserId && $existingUsernameId && $existingEmailUserId != $existingUsernameId)
) {
throw new NoUserIdFoundException(
Text::sprintf(
'User ID mismatch detected when trying to save %s (%s) credentials.',
$username,
$email
)
);
}
// Update the existing user.
return static::update($credentials);
}
// Create a new user if no existing user is found.
return static::create($credentials, $autologin, $params, $mode);
}
/**
* Create a user and update the given table.
*
* @param array $credentials User credentials including 'name', 'username', 'email', 'password', and 'password2'.
* @param int $autologin Flag to determine whether to auto-login the user after registration.
* @param array $params Parameters for user activation, password sending, and user registration allowance.
* @param int $mode Mode of registration: 1 = Site Registration, 0 = Admin Registration, 2 = Custom Helper Method.
*
* @return int User ID on success.
*
* @throws \RuntimeException If user creation fails.
* @throws NoUserIdFoundException If the user is not found.
*
* @since 5.0.3
*/
public static function create(array $credentials, int $autologin = 0,
array $params = ['useractivation' => 0, 'sendpassword' => 1], int $mode = 1): int
{
$lang = Factory::getLanguage();
$lang->load('com_users', JPATH_SITE, 'en-GB', true);
// Handle custom registration mode
if ($mode === 2 && method_exists(ComponentbuilderHelper::class, 'registerUser'))
{
$params['autologin'] = $autologin;
$userId = ComponentbuilderHelper::registerUser($credentials, $params);
if (is_numeric($userId))
{
return $userId;
}
throw new NoUserIdFoundException(Text::_('COM_COMPONENTBUILDER_USER_CREATION_FAILED'));
}
// Check if we have params/config
if (ArrayHelper::check($params))
{
// Make changes to user config
foreach ($params as $param => $set)
{
// If you know of a better path, let me know
$params[$param] = Component::setParams($param, $set, 'com_users');
}
}
// Fallback to Site Registrations if mode is set to 2 but the method doesn't exist
$mode = $mode === 2 ? 1 : $mode;
// Load the appropriate user model
$model = static::getModelByMode($mode);
// Set default values for missing credentials
$credentials['username'] = $credentials['username'] ?? $credentials['email'];
// Prepare user data
$data = static::prepareUserData($credentials, $mode);
// Set form path (bug fix for Joomla)
static::setFormPathForUserClass($mode);
// Handle user creation
$userId = $mode === 1 ? $model->register($data) : static::adminRegister($model, $data);
// Check if we have params
if (ArrayHelper::check($params))
{
// Change user params/config back
foreach ($params as $param => $set)
{
// If you know of a better path, let me know
Component::setParams($param, $set, 'com_users');
}
}
if (!$userId)
{
$current_user = Factory::getApplication()->getIdentity();
// only allow those with access to Users to ignore errors
if ($current_user->authorise('core.manage', 'com_users'))
{
$userId = static::getUserIdByUsername($credentials['username']);
}
}
if (is_numeric($userId) && $userId > 0)
{
// Handle post-registration processes
return static::handlePostRegistration($userId, $autologin, $credentials);
}
$error_messages = '';
if (method_exists($model, 'getError'))
{
$errors = $model->getError();
if (!empty($errors))
{
if (is_array($errors))
{
$error_messages = '<br>' . implode('<br>', $errors);
}
elseif (is_string($errors))
{
$error_messages = '<br>' . $errors;
}
}
}
throw new NoUserIdFoundException(
Text::sprintf('COM_COMPONENTBUILDER_USER_S_S_CREATION_FAILEDS',
(string) $credentials['username'],
(string) $credentials['email'],
$error_messages
)
);
}
/**
* Update user details.
*
* @param array $userDetails Array containing user details to be updated.
*
* @return int Updated user ID on success.
*
* @throws \RuntimeException If user update fails.
*
* @since 5.0.3
*/
public static function update(array $userDetails): int
{
$lang = Factory::getLanguage();
$lang->load('com_users', JPATH_ADMINISTRATOR, 'en-GB', true);
$model = Component::getModel('User', 'Administrator', 'com_users');
// Set default values for missing credentials
$userDetails['username'] = $userDetails['username'] ?? $userDetails['email'];
// Prepare user data for update
$data = [
'id' => $userDetails['id'],
'username' => $userDetails['username'],
'name' => $userDetails['name'],
'email' => $userDetails['email'],
'password' => $userDetails['password'] ?? null,
'password2' => $userDetails['password2'] ?? null,
'block' => 0
];
// set groups if found
if (isset($userDetails['groups']) && ArrayHelper::check($userDetails['groups']))
{
$data['groups'] = $userDetails['groups'];
}
// Update the user
if ($model->save($data))
{
return $userDetails['id'];
}
$error_messages = '';
if (method_exists($model, 'getError'))
{
$errors = $model->getError();
if (!empty($errors))
{
if (is_array($errors))
{
$error_messages = '<br>' . implode('<br>', $errors);
}
elseif (is_string($errors))
{
$error_messages = '<br>' . $errors;
}
}
}
throw new \RuntimeException(
Text::sprintf('COM_COMPONENTBUILDER_UPDATE_OF_USER_S_S_FAILEDS',
(string) $userDetails['username'],
(string) $userDetails['email'],
(string) $error_messages
)
);
}
/**
* Method to get an instance of a user for the given id.
*
* @param int $id The id
*
* @return User
*
* @since 5.0.3
*/
public static function getUserById(int $id): User
{
return new User($id);
}
/**
* Retrieve the user ID by username.
*
* @param string $username The username to check.
*
* @return int|null The user ID if the user exists, null otherwise.
*
* @since 5.0.3
*/
public static function getUserIdByUsername(string $username): ?int
{
$userId = JoomlaUserHelper::getUserId($username);
return $userId ?: null;
}
/**
* Retrieve the user ID by email.
*
* @param string $email The email address to check.
*
* @return int|null The user ID if the user exists, null otherwise.
*
* @since 5.0.3
*/
public static function getUserIdByEmail(string $email): ?int
{
// Initialise some variables
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select($db->quoteName('id'))
->from($db->quoteName('#__users'))
->where($db->quoteName('email') . ' = :email')
->bind(':email', $email)
->setLimit(1);
$db->setQuery($query);
$userId = $db->loadResult();
return $userId ?: null;
}
/**
* Load the correct user model based on the registration mode.
*
* @param int $mode The registration mode.
*
* @return BaseDatabaseModel The appropriate user model.
*
* @since 5.0.3
*/
protected static function getModelByMode(int $mode): BaseDatabaseModel
{
if ($mode === 1)
{
return Component::getModel('Registration', 'Site', 'com_users');
}
return Component::getModel('User', 'Administrator', 'com_users');
}
/**
* Prepare user data array for registration or update.
*
* @param array $credentials User credentials.
* @param int $mode The registration mode.
*
* @return array The prepared user data array.
*
* @since 5.0.3
*/
protected static function prepareUserData(array $credentials, int $mode)
{
$data = [
'username' => $credentials['username'],
'name' => $credentials['name'],
'block' => 0
];
if ($mode === 1)
{
$data['email1'] = $credentials['email'];
}
else
{
$data['email'] = $credentials['email'];
$data['registerDate'] = Factory::getDate()->toSql();
}
if ($mode === 1 && empty($credentials['password']))
{
$credentials['password'] = StringHelper::random(10);
$credentials['password2'] = $credentials['password'];
}
if (!empty($credentials['password']) && !empty($credentials['password2']))
{
$data['password1'] = $credentials['password'];
$data['password2'] = $credentials['password2'];
}
if ($mode === 0 && isset($credentials['groups']) && ArrayHelper::check($credentials['groups']))
{
$data['groups'] = $credentials['groups'];
}
return $data;
}
/**
* Handle the registration process for admin mode.
*
* @param BaseDatabaseModel $model The user model.
* @param array $data The user data.
*
* @return int The ID of the created user.
*
* @since 5.0.3
*/
private static function adminRegister(BaseDatabaseModel $model, array $data): int
{
$model->save($data);
return $model->getState('user.id', 0);
}
/**
* Handle post-registration processes like auto-login.
*
* @param int $userId The ID of the created user.
* @param int $autologin Flag to determine whether to auto-login the user.
* @param array $credentials The user credentials.
*
* @return int The user ID on success.
*
* @since 5.0.3
*/
private static function handlePostRegistration(int $userId, int $autologin, array $credentials): int
{
// make sure user is it the correct groups
if ($userId > 0 && !empty($credentials['groups']))
{
try
{
JoomlaUserHelper::setUserGroups($userId, $credentials['groups']);
}
catch (\Exception $e)
{
// we might need say something
}
}
if ($autologin && !empty($credentials['password']))
{
try
{
Factory::getApplication()->login($credentials);
}
catch (\Exception $e)
{
// we might need to redirect here?
}
}
return $userId;
}
/**
* Address bug on \Joomla\CMS\MVC\Model\FormBehaviorTrait Line 76
* The use of JPATH_COMPONENT cause it to load the
* active component forms and fields, which breaks the registration model.
*
* @param int $mode
*
* @since 5.0.3
*/
private static function setFormPathForUserClass(int $mode): void
{
if ($mode == 1) // 1 = use of the Registration Model
{
// Get the form.
Form::addFormPath(JPATH_ROOT . '/components/com_users/forms');
}
}
}