Language Change PR-6.2-dev Pending

User tests: Successful: Unsuccessful:

avatar obuisard
obuisard
12 Jun 2026

Pull Request resolves # .

  • I read the Generative AI policy and my contribution is either not created with the help of AI or is compatible with the policy and GNU/GPL 2 or later.

Summary of Changes

This PR aims at adding Health Check functionality to Joomla, in a dedicated dashboard.
It lays out the underlying initial implementation and aims at giving instructions to developers to create specific Health Check plugins.

image

Testing Instructions

There is one piece missing, it is the installation of a module healthcheck instance. It is incoming in this draft.
Right now, you need to use the 'discover' functionality to try this PR out.
Thank you for your patience.

Health Check Plugin Developer Guide

This guide explains how to build a healthcheck plugin for Joomla's Health Check module, using the existing usermaintenance plugin as the baseline example.

Relevant core code in this workspace:

  • Module data collection: administrator/modules/mod_healthcheck/src/Helper/HealthCheckHelper.php
  • Module rendering entry point: administrator/modules/mod_healthcheck/tmpl/default.php
  • Layout rendering helper: libraries/src/HTML/Helpers/HealthChecks.php
  • Layout templates:
    • layouts/joomla/healthchecks/icon.php
    • layouts/joomla/healthchecks/gauge.php
    • layouts/joomla/healthchecks/list.php
    • layouts/joomla/healthchecks/table.php
  • Example plugin (icons only): plugins/healthcheck/usermaintenance

1) How the Health Check plugin system works

  1. The module imports the healthcheck plugin group and dispatches events like onHealthcheckGetIcons, onHealthcheckGetGauges, onHealthcheckGetLists, and onHealthcheckGetTables.
  2. Each enabled plugin can subscribe to one or more events and append data into the event result argument.
  3. The module validates required fields and applies defaults (HealthCheckHelper methods getButtons(), getGauges(), getLists(), getTables()).
  4. The HTMLHelper::_('healthchecks.*', ...) helper renders each item through the matching layout file.

The module passes a context value to events. Your plugin should usually ignore events for other contexts.

2) Minimal plugin structure

Use the same structure as plugins/healthcheck/usermaintenance:

plugins/healthcheck/yourplugin/
  yourplugin.xml
  services/provider.php
  src/Extension/YourPlugin.php
  language/en-GB/plg_healthcheck_yourplugin.ini
  language/en-GB/plg_healthcheck_yourplugin.sys.ini
yourplugin.xml essentials
  • group="healthcheck"
  • plugin namespace
  • files and language declarations
  • optional plugin params (including a context text field)

The usermaintenance example defines:

<extension type="plugin" group="healthcheck" method="upgrade">
services/provider.php essentials

Register your plugin as PluginInterface with lazy loading, like plugins/healthcheck/usermaintenance/services/provider.php.

src/Extension/YourPlugin.php essentials
  • extend CMSPlugin
  • implement SubscriberInterface
  • return event subscriptions in getSubscribedEvents()

3) Event subscriptions and payload contracts

Event names
  • Icons: onHealthcheckGetIcons
  • Gauges: onHealthcheckGetGauges
  • Lists: onHealthcheckGetLists
  • Tables: onHealthcheckGetTables
Expected return pattern

Each event method should:

  1. Read current result: $result = $event->getArgument('result', []);
  2. Append one array of items: $result[] = $items;
  3. Write back: $event->setArgument('result', $result);

This matches the pattern in usermaintenance (onHealthcheckGetIcons).

Context guard pattern

Use the same guard style as usermaintenance:

$context = $event->getContext();

if ($context !== $this->params->get('context', 'general')) {
    return;
}

4) Icon items (button layout)

Icons are dispatched through onHealthcheckGetIcons and rendered by layouts/joomla/healthchecks/icon.php.

Required fields

From HealthCheckHelper::getButtons():

  • link (required)
  • one of text or name (required)
Common fields
  • icon (for icon class, used by layout)
  • image (optional image URL)
  • amount (numeric/string badge amount)
  • status (success, warning, error, etc.; affects color/filter)
  • id, class, target, title, onclick
  • group (defaults to general)
  • access (boolean or ACL pair array)
Example (based on usermaintenance)
public function onHealthcheckGetIcons(HealthChecksEvent $event): void
{
    if ($event->getContext() !== $this->params->get('context', 'usermanagement')) {
        return;
    }

    $checks = [];

    $checks[] = [
        'link'   => 'index.php?option=com_users&view=users&filter[state]=1',
        'icon'   => 'fas fa-users-gear',
        'amount' => 12,
        'text'   => 'Inactive users',
        'id'     => 'plg_healthcheck_example_inactive',
        'status' => 'warning',
    ];

    $checks[] = [
        'link'   => 'index.php?option=com_users&view=users&filter[mfa]=0',
        'icon'   => 'fas fa-shield-halved',
        'amount' => 0,
        'text'   => 'Users without MFA',
        'status' => 'success',
    ];

    $result   = $event->getArgument('result', []);
    $result[] = $checks;
    $event->setArgument('result', $result);
}

5) Gauge items

Gauges are dispatched through onHealthcheckGetGauges and rendered by layouts/joomla/healthchecks/gauge.php.

Required fields

From HealthCheckHelper::getGauges():

  • score
  • unit
Useful optional fields
  • label, sublabel, note
  • score_min, score_max
  • score_threshold_warning, score_threshold_success
  • link
  • linktitle (used by layout)
  • group, access, class, id
Example
public function onHealthcheckGetGauges(HealthChecksEvent $event): void
{
    if ($event->getContext() !== $this->params->get('context', 'performance')) {
        return;
    }

    $gauges = [[
        'id'                      => 'plg_healthcheck_example_php_memory',
        'label'                   => 'PHP memory usage',
        'sublabel'                => 'Current process',
        'note'                    => 'Values over 80% should be reviewed.',
        'score'                   => 72,
        'unit'                    => '%',
        'score_min'               => 0,
        'score_max'               => 100,
        'score_threshold_warning' => 70,
        'score_threshold_success' => 90,
        'link'                    => 'index.php?option=com_config',
        'linktitle'               => 'Open Global Configuration',
        'status'                  => 'warning',
    ]];

    $result   = $event->getArgument('result', []);
    $result[] = $gauges;
    $event->setArgument('result', $result);
}

6) List items

Lists are dispatched through onHealthcheckGetLists and rendered by layouts/joomla/healthchecks/list.php.

Required fields

From HealthCheckHelper::getLists():

  • items (array)
Useful optional fields
  • type (ul, ol, or div)
  • class, id, itemClass
  • group, access
Example
public function onHealthcheckGetLists(HealthChecksEvent $event): void
{
    if ($event->getContext() !== $this->params->get('context', 'security')) {
        return;
    }

    $lists = [[
        'id'       => 'plg_healthcheck_example_security_tips',
        'class'    => 'list-group list-group-flush',
        'itemClass'=> 'list-group-item',
        'type'     => 'ul',
        'items'    => [
            'Enable MFA for all administrators',
            'Review inactive accounts monthly',
            'Remove users not assigned to any group',
        ],
    ]];

    $result   = $event->getArgument('result', []);
    $result[] = $lists;
    $event->setArgument('result', $result);
}

7) Table items

Tables are dispatched through onHealthcheckGetTables and rendered by layouts/joomla/healthchecks/table.php.

Required fields

From HealthCheckHelper::getTables():

  • columns (array)
  • data (array)
Column definition basics

Each column typically uses:

  • key (data key)
  • title (header)
  • optional type (text, badge, link, date, boolean, progress, icon, custom)
  • optional display keys (align, width, scope, cellClass, etc.)

type rendering is handled by HealthCheckHelper::renderTableCellContent().

Example
public function onHealthcheckGetTables(HealthChecksEvent $event): void
{
    if ($event->getContext() !== $this->params->get('context', 'usermanagement')) {
        return;
    }

    $tables = [[
        'id'      => 'plg_healthcheck_example_user_risk',
        'caption' => 'Users requiring attention',
        'class'   => 'table-sm',
        'columns' => [
            ['key' => 'name', 'title' => 'User'],
            ['key' => 'lastvisitDate', 'title' => 'Last login', 'type' => 'date'],
            ['key' => 'mfa', 'title' => 'MFA', 'type' => 'boolean'],
            ['key' => 'risk', 'title' => 'Risk', 'type' => 'badge', 'badgeClass' => static function ($value) {
                return $value === 'high' ? 'danger' : ($value === 'medium' ? 'warning' : 'success');
            }],
        ],
        'data' => [
            ['name' => 'Alice Admin', 'lastvisitDate' => '2026-05-14 09:05:00', 'mfa' => 1, 'risk' => 'low'],
            ['name' => 'Bob Manager', 'lastvisitDate' => '2026-01-07 11:21:00', 'mfa' => 0, 'risk' => 'high'],
        ],
    ]];

    $result   = $event->getArgument('result', []);
    $result[] = $tables;
    $event->setArgument('result', $result);
}

8) Full subscriber example

public static function getSubscribedEvents(): array
{
    return [
        'onHealthcheckGetIcons'  => 'onHealthcheckGetIcons',
        'onHealthcheckGetGauges' => 'onHealthcheckGetGauges',
        'onHealthcheckGetLists'  => 'onHealthcheckGetLists',
        'onHealthcheckGetTables' => 'onHealthcheckGetTables',
    ];
}

You can implement only the events you need.

9) Grouping and access control

  • group: defaults to general; use it to classify data by module context strategy.
  • access:
    • true/false for quick allow/deny
    • or ACL checks as pairs, e.g. ['core.manage', 'com_users', 'core.admin', 'com_users']

Access is evaluated in libraries/src/HTML/Helpers/HealthChecks.php (canAccess()).

10) Testing checklist

  1. Install/enable your plugin in group healthcheck.
  2. Ensure the module mod_healthcheck is enabled in Administrator.
  3. Set module context to match your plugin context parameter.
  4. Confirm each subscribed event returns at least one valid item with required fields.
  5. Verify rendering in the module UI:
    • icons appear in the icon section
    • gauges render SVG meters
    • lists render item collections
    • tables render with expected cell types

11) Practical notes from current implementation

  • The usermaintenance plugin currently demonstrates the icon event only.
  • Gauge layout reads linktitle for link title text.
  • The helper default includes link_title for gauges; if you need guaranteed title output in the current layout, set linktitle in your payload.
  • Any missing required field causes that item to be filtered out before rendering.

12) How healthcheck-filters works

The filter bar is part of the module template (administrator/modules/mod_healthcheck/tmpl/default.php), not the plugin itself.

What filters are available

The module renders four filter buttons:

  • all
  • healthy
  • warning
  • critical

Selecting a button shows only matching health-check items in the module.

What plugin authors should set

To make your items filter correctly, provide status on each item payload.

Use these values:

  • success for healthy items
  • warning for warning items
  • error for critical items

If status is omitted, items default to the healthy group.

Where filtering applies

Filtering currently applies to items rendered by these layouts:

  • layouts/joomla/healthchecks/icon.php
  • layouts/joomla/healthchecks/gauge.php
  • layouts/joomla/healthchecks/list.php
  • layouts/joomla/healthchecks/table.php

All filterable items are tagged with data-healthcheck-status, and the module script (media_source/mod_healthcheck/js/healthcheck-filter.js) uses that value to show/hide items.

Status aliases accepted by the module filter

The filter normalization accepts these aliases:

  • success, ok, info -> healthy
  • warning, warn, alert -> warning
  • error, danger -> critical
Quick usage example
$checks[] = [
    'link'   => 'index.php?option=com_users&view=users&filter[mfa]=0',
    'icon'   => 'fas fa-shield-halved',
    'amount' => 3,
    'text'   => 'Users without MFA',
    'status' => 'warning', // appears when the Warning filter is selected
];
Testing filter behavior
  1. Enable your healthcheck plugin and mod_healthcheck.
  2. Return items with a mix of success, warning, and error statuses.
  3. In Administrator, switch between All, Healthy, Warning, and Critical.
  4. Confirm only matching items remain visible for each filter.

Link to documentations

Please select:

  • Documentation link for guide.joomla.org:

  • No documentation changes for guide.joomla.org needed

  • Pull Request link for manual.joomla.org:

  • No documentation changes for manual.joomla.org needed

avatar obuisard obuisard - open - 12 Jun 2026
avatar obuisard obuisard - change - 12 Jun 2026
Status New Pending
avatar joomla-cms-bot joomla-cms-bot - change - 12 Jun 2026
Category Administration com_cpanel com_menus Language & Strings Modules Layout Libraries JavaScript Front End Plugins
avatar obuisard obuisard - change - 12 Jun 2026
Labels Added: Language Change PR-6.2-dev
avatar brianteeman
brianteeman - comment - 12 Jun 2026

You need to decide on the terminology and be consistent with its use

  • Health Check
  • HealthCheck
  • Healthcheck
avatar obuisard
obuisard - comment - 12 Jun 2026
  • Healthcheck

I sorted it out a bit:
Health Check: the name
Health Check: the module
health check: the noun
HealthCheck: the plugin group

avatar joomla-cms-bot joomla-cms-bot - change - 12 Jun 2026
Category Administration com_cpanel com_menus Language & Strings Modules Layout Libraries JavaScript Front End Plugins Administration com_cpanel com_menus Language & Strings Modules SQL Installation Postgresql Layout Libraries JavaScript Front End Plugins
avatar brianteeman
brianteeman - comment - 12 Jun 2026

dont forget the sql for updates

avatar brianteeman
brianteeman - comment - 12 Jun 2026

Nothing happens when I click on the buttons

image
avatar brianteeman
brianteeman - comment - 12 Jun 2026

Nothing happens when I click on the filter buttons

image
avatar brianteeman
brianteeman - comment - 12 Jun 2026

Inactive Users

the count says 0 but I have multiple users that are not activated

when you click on the button it takes you to a filter list of disabled users. thats not the same thing as non activated users

avatar brianteeman
brianteeman - comment - 12 Jun 2026

Please follow the style guide for labels. They should be Capitalised

image
avatar brianteeman
brianteeman - comment - 12 Jun 2026

The reason that the filters dont work as reported #47947 (comment) is that neither the js not the css files are being created in the media folder.

With the new build system you have to explicitly tell the build scripts to create the js and css for the module. With the old build system you could just create the media source folder but now you have to be explicit. (dont ask me why)

So you need to add mod_healthcheck here

avatar brianteeman
brianteeman - comment - 12 Jun 2026
image

When you hover over a button it is moved -1px with css.

.healthcheck-filters .btn:hover {
  transform: translateY(-1px);
  box-shadow: 0 2px 4px #0000001a;
}

I am assuming that this is to give a pseudo real button effect when you click on it. No where else in the admin ui do we do this - but maybe one of the css gurus will correct me - personally I woujld just remove it as its ugly when you hover across the buttons as shown below.

chrome_HFXJ6CPDrz.mp4
avatar brianteeman
brianteeman - comment - 12 Jun 2026

The usermaintenance plugin currently demonstrates the icon event only.

Makes it very hard to test the other functionality

avatar obuisard
obuisard - comment - 12 Jun 2026

Nothing happens when I click on the filter buttons

image

Yes, I tested it and realised the media files are not loaded or even present under /media. I don't understand why.

avatar obuisard
obuisard - comment - 12 Jun 2026

Nothing happens when I click on the filter buttons

image

Yes, I tested it and realised the media files are not loaded or even present under /media. I don't understand why.
Oh, I understand, you explained in another comment :-)

avatar obuisard
obuisard - comment - 12 Jun 2026

Nothing happens when I click on the filter buttons

image

Yes, I tested it and realised the media files are not loaded or even present under /media. I don't understand why.
Oh, I understand now, you explained in another comment :-)

avatar obuisard
obuisard - comment - 12 Jun 2026

Nothing happens when I click on the filter buttons

image

Yes, I tested it and realised the media files are not loaded or even present under /media. I don't understand why.
Oh, I understand now, you explained in another comment :-)
I was missing that piece of the puzzle...

avatar joomla-cms-bot joomla-cms-bot - change - 12 Jun 2026
Category Administration com_cpanel com_menus Language & Strings Modules Layout Libraries JavaScript Front End Plugins SQL Installation Postgresql Administration com_cpanel com_menus Language & Strings Modules JavaScript Repository SQL Installation Postgresql Layout Libraries
avatar obuisard
obuisard - comment - 12 Jun 2026

The usermaintenance plugin currently demonstrates the icon event only.

Makes it very hard to test the other functionality

We have other plugins in the work that show more of the layouts that are available.
We still wanted to have a basic implementation with this PR.

avatar obuisard
obuisard - comment - 12 Jun 2026

When you hover over a button it is moved -1px with css.

.healthcheck-filters .btn:hover {
  transform: translateY(-1px);
  box-shadow: 0 2px 4px #0000001a;
}

I am assuming that this is to give a pseudo real button effect when you click on it. No where else in the admin ui do we do this - but maybe one of the css gurus will correct me - personally I woujld just remove it as its ugly when you hover across the buttons as shown below.

Right on, I removed it, it is indeed not consistent with other buttons.

avatar brianteeman
brianteeman - comment - 13 Jun 2026

The usermaintenance plugin currently demonstrates the icon event only.

Makes it very hard to test the other functionality

We have other plugins in the work that show more of the layouts that are available. We still wanted to have a basic implementation with this PR.

without those plugins (or something even just for testing purposes) its not possible to report a successful test of that part of the pr

avatar obuisard
obuisard - comment - 13 Jun 2026

without those plugins (or something even just for testing purposes) its not possible to report a successful test of that part of the pr

In that case, I would suggest we complement the Users Maintenance plugin to use all layouts.
Add an optional gauge showing the amount of inactive users compared to the total number of users, for instance, data in a table layout and in a list layout.

avatar obuisard
obuisard - comment - 13 Jun 2026

without those plugins (or something even just for testing purposes) its not possible to report a successful test of that part of the pr

In that case, I would suggest we complement the Users Maintenance plugin to use all layouts.
Add an optional gauge showing the amount of inactive users compared to the total number of users, for instance, data in a table layout and in a list layout.

Better, I think I will create an additional plugin that highlights all cases, that will not be included in the core.

avatar joomla-cms-bot joomla-cms-bot - change - 13 Jun 2026
Category Administration com_cpanel com_menus Language & Strings Modules Layout Libraries JavaScript SQL Installation Postgresql Repository SQL Administration com_admin Postgresql com_cpanel com_menus Language & Strings Modules JavaScript Repository Installation Layout Libraries
avatar brianteeman
brianteeman - comment - 15 Jun 2026

Better, I think I will create an additional plugin that highlights all cases, that will not be included in the core.

Please do so that this PR can be really tested - otherwise its not possible for someone to test the entirety of the PR

avatar chmst
chmst - comment - 15 Jun 2026
grafik

The color contrast for review not sufficient. It is a long existing error, but not visible on other places. (Except during update/migration checks)

avatar chmst
chmst - comment - 15 Jun 2026
grafik

The color contrast for review is not sufficient. This color (warning) It is a long existing error in atum, but not visible on other places. (Except during update/migration checks)

avatar obuisard
obuisard - comment - 18 Jun 2026
grafik The color contrast for review is not sufficient. This color (warning) It is a long existing error in atum, but not visible on other places. (Except during update/migration checks)

It looks like we should open a PR for a template fix.

avatar brianteeman
brianteeman - comment - 19 Jun 2026

inconsistency in the language strings

Score: 68 % out of 100 %. This represents 68.0% of the range from 0 to 100. Status: Good performance with room for improvement.

  • here we can see that there is a space between the value and the % which should not be there
  • the first instance is to 0 decimal places and the second is to one devimal place
  • as its a % it doesnt even need to say "out of 100%" as it cant be anything else
  • as its a % then the entire "This represents 68.0% of the range from 0 to 100" is redundant
avatar brianteeman
brianteeman - comment - 19 Jun 2026

using link text and title and aria-label is not a good idea. It is generally bad of accessibility espec with screen readers. what are you trying to achieve by doing this

avatar brianteeman
brianteeman - comment - 19 Jun 2026

Thanks for getting the ai to find the issue with the language strings

avatar brianteeman
brianteeman - comment - 19 Jun 2026

This makes extensive use of (often invisible) links with both title and aria-labels to explain the purpose of the link with different levels of information. This can be tricky as screen readers will use one or both which is overkill.

Titles are also invisible to keyboard users and mobile users. On the gauges it's not even obvious that they have a link behind them.

Ideally every link should have anchor text which describe the link and an aria-labels only used where the anchor text is insufficient.

Additional reading https://www.deque.com/blog/text-links-practices-screen-readers/

avatar brianteeman
brianteeman - comment - 19 Jun 2026

Please follow the style guide - labels should be capitalised

image
avatar obuisard
obuisard - comment - 22 Jun 2026

Please follow the style guide - labels should be capitalised

image

This has been fixed last week after your initial comment.

avatar obuisard
obuisard - comment - 22 Jun 2026

Please follow the style guide - labels should be capitalised

image

Thanks Brian. This has been fixed last week after your initial comment.

Add a Comment

Login with GitHub to post a comment