Initial implementation of a Health Check system and dashboard#47947
Initial implementation of a Health Check system and dashboard#47947obuisard wants to merge 82 commits into
Conversation
| MOD_HEALTHCHECK_GAUGE_SVG_TITLE="%1$s gauge: %2$s %3$s" | ||
| MOD_HEALTHCHECK_GAUGE_SVG_DESC="A circular progress indicator showing %1$s %2$s out of a maximum of %3$s %2$s. This represents %4$s%% of the total range." | ||
| MOD_HEALTHCHECK_GAUGE_SR_SCORE="Score: %1$s %2$s out of %3$s %2$s." | ||
| MOD_HEALTHCHECK_GAUGE_SR_RANGE="This represents %1$s%% of the range from %2$s to %3$s." |
There was a problem hiding this comment.
It is needed for PHP sprintf escaping so we end with one % and not have PHP trying to parse it
There was a problem hiding this comment.
i am not convinced this is correct but its impossible to test
…nce.php Co-authored-by: Christian Heel <66922325+heelc29@users.noreply.github.com>
…nce.php Co-authored-by: Christian Heel <66922325+heelc29@users.noreply.github.com>
…nce.php Co-authored-by: Christian Heel <66922325+heelc29@users.noreply.github.com>
…nce.php Co-authored-by: Christian Heel <66922325+heelc29@users.noreply.github.com>
Please do so that this PR can be really tested - otherwise its not possible for someone to test the entirety of the PR |
Co-authored-by: Konstantin Kolos <k0nstantinkolos88@gmail.com>
| try { | ||
| $inactiveTimespan = (int) $this->params->get('inactiveTimespan', 180); // Days since the last login | ||
|
|
||
| $item['text'] = Text::_('PLG_HEALTHCHECK_USERMAINTENANCE_INACTIVE_LISTTEXT'); |
There was a problem hiding this comment.
none of the language strings in this file are present in the pr
|
inconsistency in the language strings
|
|
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 |
Commit e82837d renamed the per-check *_LISTTEXT / *_LISTNOTE keys to *_FIELD_LABEL / *_FIELD_DESC in the INI and the XML manifest, but left UserMaintenance.php still referencing the old *_LISTTEXT / *_LISTNOTE names. As a result the six user checks rendered raw language keys on the Health Check dashboard. Point the runtime text/note output at the renamed keys (no new strings needed; the renamed keys already exist). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Thanks for getting the ai to find the issue with the language strings |
|
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/ |
| PLG_HEALTHCHECK_USERMAINTENANCE_NONMFA_FIELD_LABEL="Active Users Without MFA" | ||
| PLG_HEALTHCHECK_USERMAINTENANCE_NEVERLOGGEDIN_FIELD_DESC="Users who created an account, but never actually logged in." | ||
| PLG_HEALTHCHECK_USERMAINTENANCE_NEVERLOGGEDIN_FIELD_LABEL="Never Logged In Users" | ||
| PLG_HEALTHCHECK_USERMAINTENANCE_ORPHAN_FIELD_DESC="Users who have no user group assigned." |
There was a problem hiding this comment.
Please don't add useless descriptions. This doesn't add anything that is not in the field label
| ?> | ||
| <li class="healthcheck-gauge"<?php echo $id; ?> | ||
| role="img" | ||
| tabindex="<?php echo $hasLink ? '-1' : '0'; ?>" |
There was a problem hiding this comment.
Setting tabindex -1 will make it unreachable by a screenreader
… Claude Opus 4.8 trailer
brianteeman
left a comment
There was a problem hiding this comment.
Based on the link option=com_users&view=users&filter[state]=1 this should be disabled not inactive users and the description was completely wrong
| PLG_HEALTHCHECK_USERMAINTENANCE="HealthCheck - UserMaintenance" | ||
| PLG_HEALTHCHECK_USERMAINTENANCE_ACTION_LABEL="Visit the user management" | ||
| PLG_HEALTHCHECK_USERMAINTENANCE_CHECKISDEACTIVATED="This check is deactivated in the plugin configuration." | ||
| PLG_HEALTHCHECK_USERMAINTENANCE_GETINACTIVE_ERROR="Error while trying to get the number of inactive users." |
There was a problem hiding this comment.
| PLG_HEALTHCHECK_USERMAINTENANCE_GETINACTIVE_ERROR="Error while trying to get the number of inactive users." | |
| PLG_HEALTHCHECK_USERMAINTENANCE_GETINACTIVE_ERROR="Error while trying to get the number of disabled users." |
| PLG_HEALTHCHECK_USERMAINTENANCE_INACTIVETIMESPAN_VALUE_M6="180" | ||
| PLG_HEALTHCHECK_USERMAINTENANCE_INACTIVETIMESPAN_VALUE_Y1="360" | ||
| PLG_HEALTHCHECK_USERMAINTENANCE_INACTIVE_FIELD_DESC="Users who did not login in the last few months." | ||
| PLG_HEALTHCHECK_USERMAINTENANCE_INACTIVE_FIELD_LABEL="Inactive Users" |
There was a problem hiding this comment.
| PLG_HEALTHCHECK_USERMAINTENANCE_INACTIVE_FIELD_LABEL="Inactive Users" | |
| PLG_HEALTHCHECK_USERMAINTENANCE_INACTIVE_FIELD_LABEL="Disabled Users" |
| PLG_HEALTHCHECK_USERMAINTENANCE_INACTIVETIMESPAN_VALUE_M3="90" | ||
| PLG_HEALTHCHECK_USERMAINTENANCE_INACTIVETIMESPAN_VALUE_M6="180" | ||
| PLG_HEALTHCHECK_USERMAINTENANCE_INACTIVETIMESPAN_VALUE_Y1="360" | ||
| PLG_HEALTHCHECK_USERMAINTENANCE_INACTIVE_FIELD_DESC="Users who did not login in the last few months." |
There was a problem hiding this comment.
| PLG_HEALTHCHECK_USERMAINTENANCE_INACTIVE_FIELD_DESC="Users who did not login in the last few months." | |
| PLG_HEALTHCHECK_USERMAINTENANCE_INACTIVE_FIELD_DESC="Users whose accounts are disabled." |
| } | ||
|
|
||
| const amount = parseInt(data.amount, 10) || 0; | ||
| amountEl.innerHTML = `<div>${amount}</div>`; |



Pull Request resolves # .
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.
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
healthcheckplugin for Joomla's Health Check module, using the existingusermaintenanceplugin as the baseline example.Relevant core code in this workspace:
administrator/modules/mod_healthcheck/src/Helper/HealthCheckHelper.phpadministrator/modules/mod_healthcheck/tmpl/default.phplibraries/src/HTML/Helpers/HealthChecks.phplayouts/joomla/healthchecks/icon.phplayouts/joomla/healthchecks/gauge.phplayouts/joomla/healthchecks/list.phplayouts/joomla/healthchecks/table.phpplugins/healthcheck/usermaintenance1) How the Health Check plugin system works
healthcheckplugin group and dispatches events likeonHealthcheckGetIcons,onHealthcheckGetGauges,onHealthcheckGetLists, andonHealthcheckGetTables.resultargument.HealthCheckHelpermethodsgetButtons(),getGauges(),getLists(),getTables()).HTMLHelper::_('healthchecks.*', ...)helper renders each item through the matching layout file.The module passes a
contextvalue to events. Your plugin should usually ignore events for other contexts.2) Minimal plugin structure
Use the same structure as
plugins/healthcheck/usermaintenance:yourplugin.xmlessentialsgroup="healthcheck"contexttext field)The
usermaintenanceexample defines:services/provider.phpessentialsRegister your plugin as
PluginInterfacewith lazy loading, likeplugins/healthcheck/usermaintenance/services/provider.php.src/Extension/YourPlugin.phpessentialsCMSPluginSubscriberInterfacegetSubscribedEvents()3) Event subscriptions and payload contracts
Event names
onHealthcheckGetIconsonHealthcheckGetGaugesonHealthcheckGetListsonHealthcheckGetTablesExpected return pattern
Each event method should:
$result = $event->getArgument('result', []);$result[] = $items;$event->setArgument('result', $result);This matches the pattern in
usermaintenance(onHealthcheckGetIcons).Context guard pattern
Use the same guard style as
usermaintenance:4) Icon items (button layout)
Icons are dispatched through
onHealthcheckGetIconsand rendered bylayouts/joomla/healthchecks/icon.php.Required fields
From
HealthCheckHelper::getButtons():link(required)textorname(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,onclickgroup(defaults togeneral)access(boolean or ACL pair array)Example (based on
usermaintenance)5) Gauge items
Gauges are dispatched through
onHealthcheckGetGaugesand rendered bylayouts/joomla/healthchecks/gauge.php.Required fields
From
HealthCheckHelper::getGauges():scoreunitUseful optional fields
label,sublabel,notescore_min,score_maxscore_threshold_warning,score_threshold_successlinklinktitle(used by layout)group,access,class,idExample
6) List items
Lists are dispatched through
onHealthcheckGetListsand rendered bylayouts/joomla/healthchecks/list.php.Required fields
From
HealthCheckHelper::getLists():items(array)Useful optional fields
type(ul,ol, ordiv)class,id,itemClassgroup,accessExample
7) Table items
Tables are dispatched through
onHealthcheckGetTablesand rendered bylayouts/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)type(text,badge,link,date,boolean,progress,icon,custom)align,width,scope,cellClass, etc.)typerendering is handled byHealthCheckHelper::renderTableCellContent().Example
8) Full subscriber example
You can implement only the events you need.
9) Grouping and access control
group: defaults togeneral; use it to classify data by module context strategy.access:true/falsefor quick allow/deny['core.manage', 'com_users', 'core.admin', 'com_users']Access is evaluated in
libraries/src/HTML/Helpers/HealthChecks.php(canAccess()).10) Testing checklist
healthcheck.mod_healthcheckis enabled in Administrator.contextto match your plugincontextparameter.11) Practical notes from current implementation
linktitlefor link title text.link_titlefor gauges; if you need guaranteed title output in the current layout, setlinktitlein your payload.12) How
healthcheck-filtersworksThe 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:
allhealthywarningcriticalSelecting a button shows only matching health-check items in the module.
What plugin authors should set
To make your items filter correctly, provide
statuson each item payload.Use these values:
successfor healthy itemswarningfor warning itemserrorfor critical itemsIf
statusis omitted, items default to the healthy group.Where filtering applies
Filtering currently applies to items rendered by these layouts:
layouts/joomla/healthchecks/icon.phplayouts/joomla/healthchecks/gauge.phplayouts/joomla/healthchecks/list.phplayouts/joomla/healthchecks/table.phpAll 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->healthywarning,warn,alert->warningerror,danger->criticalQuick usage example
Testing filter behavior
healthcheckplugin andmod_healthcheck.success,warning, anderrorstatuses.All,Healthy,Warning, andCritical.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