diff --git a/modules/mod_users_latest/mod_users_latest.php b/modules/mod_users_latest/mod_users_latest.php
deleted file mode 100644
index a15c7b3f92dcd..0000000000000
--- a/modules/mod_users_latest/mod_users_latest.php
+++ /dev/null
@@ -1,19 +0,0 @@
-
- * @license GNU General Public License version 2 or later; see LICENSE.txt
- */
-
-defined('_JEXEC') or die;
-
-use Joomla\CMS\Helper\ModuleHelper;
-use Joomla\Module\UsersLatest\Site\Helper\UsersLatestHelper;
-
-$shownumber = $params->get('shownumber', 5);
-$names = UsersLatestHelper::getUsers($params);
-
-require ModuleHelper::getLayoutPath('mod_users_latest', $params->get('layout', 'default'));
diff --git a/modules/mod_users_latest/mod_users_latest.xml b/modules/mod_users_latest/mod_users_latest.xml
index ea02cef6197bd..d4b1fd442e10e 100644
--- a/modules/mod_users_latest/mod_users_latest.xml
+++ b/modules/mod_users_latest/mod_users_latest.xml
@@ -11,7 +11,7 @@
MOD_USERS_LATEST_XML_DESCRIPTION
Joomla\Module\UsersLatest
- mod_users_latest.php
+ services
src
tmpl
diff --git a/modules/mod_users_latest/services/provider.php b/modules/mod_users_latest/services/provider.php
new file mode 100644
index 0000000000000..5f25766b1b754
--- /dev/null
+++ b/modules/mod_users_latest/services/provider.php
@@ -0,0 +1,41 @@
+
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+
+defined('_JEXEC') or die;
+
+use Joomla\CMS\Extension\Service\Provider\HelperFactory;
+use Joomla\CMS\Extension\Service\Provider\Module;
+use Joomla\CMS\Extension\Service\Provider\ModuleDispatcherFactory;
+use Joomla\DI\Container;
+use Joomla\DI\ServiceProviderInterface;
+
+/**
+ * The users latest module service provider.
+ *
+ * @since __DEPLOY_VERSION__
+ */
+return new class () implements ServiceProviderInterface {
+ /**
+ * Registers the service provider with a DI container.
+ *
+ * @param Container $container The DI container.
+ *
+ * @return void
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function register(Container $container): void
+ {
+ $container->registerServiceProvider(new ModuleDispatcherFactory('\\Joomla\\Module\\UsersLatest'));
+ $container->registerServiceProvider(new HelperFactory('\\Joomla\\Module\\UsersLatest\\Site\\Helper'));
+
+ $container->registerServiceProvider(new Module());
+ }
+};
diff --git a/modules/mod_users_latest/src/Dispatcher/Dispatcher.php b/modules/mod_users_latest/src/Dispatcher/Dispatcher.php
new file mode 100644
index 0000000000000..a349fa84ed314
--- /dev/null
+++ b/modules/mod_users_latest/src/Dispatcher/Dispatcher.php
@@ -0,0 +1,45 @@
+
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+
+namespace Joomla\Module\UsersLatest\Site\Dispatcher;
+
+use Joomla\CMS\Dispatcher\AbstractModuleDispatcher;
+use Joomla\CMS\Helper\HelperFactoryAwareInterface;
+use Joomla\CMS\Helper\HelperFactoryAwareTrait;
+
+// phpcs:disable PSR1.Files.SideEffects
+\defined('_JEXEC') or die;
+// phpcs:enable PSR1.Files.SideEffects
+
+/**
+ * Dispatcher class for mod_users_latest
+ *
+ * @since __DEPLOY_VERSION__
+ */
+class Dispatcher extends AbstractModuleDispatcher implements HelperFactoryAwareInterface
+{
+ use HelperFactoryAwareTrait;
+
+ /**
+ * Returns the layout data.
+ *
+ * @return array
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ protected function getLayoutData(): array
+ {
+ $data = parent::getLayoutData();
+
+ $data['names'] = $this->getHelperFactory()->getHelper('UsersLatestHelper')->getUsers($data['params'], $this->getApplication());
+
+ return $data;
+ }
+}
diff --git a/modules/mod_users_latest/src/Helper/UsersLatestHelper.php b/modules/mod_users_latest/src/Helper/UsersLatestHelper.php
index be582ef87bb7f..bd47aac698daa 100644
--- a/modules/mod_users_latest/src/Helper/UsersLatestHelper.php
+++ b/modules/mod_users_latest/src/Helper/UsersLatestHelper.php
@@ -10,8 +10,11 @@
namespace Joomla\Module\UsersLatest\Site\Helper;
-use Joomla\CMS\Factory;
+use Joomla\CMS\Application\SiteApplication;
use Joomla\CMS\Language\Text;
+use Joomla\Database\DatabaseAwareInterface;
+use Joomla\Database\DatabaseAwareTrait;
+use Joomla\Registry\Registry;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
@@ -22,25 +25,30 @@
*
* @since 1.6
*/
-class UsersLatestHelper
+class UsersLatestHelper implements DatabaseAwareInterface
{
+ use DatabaseAwareTrait;
+
/**
* Get users sorted by activation date
*
- * @param \Joomla\Registry\Registry $params module parameters
+ * @param Registry $params Object holding the models parameters
+ * @param SiteApplication $app The app
*
* @return array The array of users
*
- * @since 1.6
+ * @since __DEPLOY_VERSION__
*/
- public static function getUsers($params)
+ public function getUsers(Registry $params, SiteApplication $app): array
{
- $db = Factory::getDbo();
+ // Get the Dbo and User object
+ $db = $this->getDatabase();
+ $user = $app->getIdentity();
+
$query = $db->getQuery(true)
->select($db->quoteName(['a.id', 'a.name', 'a.username', 'a.registerDate']))
->order($db->quoteName('a.registerDate') . ' DESC')
->from($db->quoteName('#__users', 'a'));
- $user = Factory::getUser();
if (!$user->authorise('core.admin') && $params->get('filter_groups', 0) == 1) {
$groups = $user->getAuthorisedGroups();
@@ -61,7 +69,7 @@ public static function getUsers($params)
try {
return (array) $db->loadObjectList();
} catch (\RuntimeException $e) {
- Factory::getApplication()->enqueueMessage(Text::_('JERROR_AN_ERROR_HAS_OCCURRED'), 'error');
+ $app->enqueueMessage(Text::_('JERROR_AN_ERROR_HAS_OCCURRED'), 'error');
return [];
}
diff --git a/tests/cypress/integration/site/modules/mod_users_latest/Default.cy.js b/tests/cypress/integration/site/modules/mod_users_latest/Default.cy.js
new file mode 100644
index 0000000000000..1d2083edf870d
--- /dev/null
+++ b/tests/cypress/integration/site/modules/mod_users_latest/Default.cy.js
@@ -0,0 +1,11 @@
+describe('Test that the users latest module', () => {
+ it('can load in frontend and displays the latest registered users', () => {
+ cy.db_createUser({ username: 'automatedtestuser' })
+ .then(() => cy.db_createModule({ module: 'mod_users_latest' }))
+ .then(() => {
+ cy.visit('/');
+
+ cy.contains('automatedtestuser');
+ });
+ });
+});
diff --git a/tests/cypress/support/commands/db.js b/tests/cypress/support/commands/db.js
index fe7a870343006..efc24c5f985ba 100644
--- a/tests/cypress/support/commands/db.js
+++ b/tests/cypress/support/commands/db.js
@@ -60,6 +60,26 @@ Cypress.Commands.add('db_createModule', (module) => {
);
});
+Cypress.Commands.add('db_createUser', (user) => {
+ const defaultUserOptions = {
+ name: 'test user',
+ username: 'test',
+ email: 'test@example.com',
+ password: '098f6bcd4621d373cade4e832627b4f6', // Is the md5 of the word 'test'
+ block: 0,
+ registerDate: '2023-03-01 20:00:00',
+ params: ''
+ };
+ user = { ...defaultUserOptions, ...user };
+
+ const groupId = user.group_id ?? 2; // Default the group id to registered
+ delete user.group_id;
+
+ return cy.task('queryDB', createInsertQuery('users', user)).then((info) => {
+ cy.task('queryDB', "INSERT INTO #__user_usergroup_map (user_id, group_id) VALUES ('" + info.insertId + "', '" + groupId + "')").then(() => info);
+ });
+});
+
/**
* Returns an insert query for the given database and fields.
*