`. There is no score and no count in section headings.
* Styling lives in `assets/css/health.css`. The page is fully expanded without
* JavaScript; `inline_script()` only collapses the passing rows on load and
* drives the "Show all passing checks" toggle and the "Jump to check" links.
*/
final class Health_Renderer {
/** Style handle registered by whoever enqueues `assets/css/health.min.css`. */
const STYLE_HANDLE = 'adminoptim-health';
/** Handle of the (file-less) script that carries `inline_script()`. */
const SCRIPT_HANDLE = 'adminoptim-health';
/** Id of the wrapper around the check sections; the toggle's `aria-controls`. */
const LAYOUT_ID = 'adminoptim-health-layout';
/** Prefix of every row's DOM id, the target of "Jump to check". */
const ROW_ID_PREFIX = 'adminoptim-health-row-';
/** Most rows "Needs attention" lists: every Critical, then Recommended up to this total. */
const ATTENTION_LIMIT = 5;
/** Statuses whose rows show the current / recommended / reason lines. */
const DETAIL_STATUSES = [ 'critical', 'warning', 'unverified' ];
/**
* Dashicon per status. Decorative; the badge text carries the meaning.
*
* @var array
*/
const STATUS_ICONS = [
'ok' => 'yes-alt',
'warning' => 'warning',
'critical' => 'dismiss',
'info' => 'info-outline',
'off' => 'minus',
'unverified' => 'editor-help',
];
/**
* Dismiss / Restore form settings, or null when rows are read-only (the
* dashboard widget). Keys: `url`, `dismiss_action`, `restore_action`,
* `dismiss_nonce`, `restore_nonce`.
*
* @var array|null
*/
private static $controls = null;
/**
* Turn on the Dismiss / Restore controls for the rows rendered afterwards.
*
* @param array|null $controls Form settings, or null for none.
*
* @return void
*/
public static function set_dismiss_controls( ?array $controls ): void {
self::$controls = $controls;
}
/**
* Header strip: title, one-line description, "Last checked" and Refresh.
*
* @param array{title:string,description:string,generated_at:int,refresh_url:string} $args Header data.
*
* @return void
*/
public static function render_header( array $args ): void {
?>
' . esc_html(
sprintf(
/* translators: %d: number of Recommended items not listed. */
_n( '+ %d more recommended item in the sections below', '+ %d more recommended items in the sections below', $hidden, 'admin-optimizer' ),
$hidden
)
) . '
';
}
?>
$sections Sections.
*
* @return array
*/
private static function layout_order( array $sections ): array {
$buckets = [
1 => [],
2 => [],
3 => [],
4 => [],
];
foreach ( $sections as $section ) {
$column = (int) ( $section['column'] ?? 0 );
if ( 'table' === $section['layout'] ) {
$buckets[4][] = $section;
} else {
$buckets[ in_array( $column, [ 1, 2 ], true ) ? $column : 3 ][] = $section;
}
}
return array_merge( ...array_values( $buckets ) );
}
/**
* The vanilla JavaScript for the Site Health tab (no library, no asset file).
* Collapses the passing rows on load, drives the master toggle, and moves
* "Jump to check" links to the full row: scroll, focus, brief highlight. After
* Dismiss or Restore the page reloads at the row's anchor, so focus goes there.
*
* @return string
*/
public static function inline_script(): string {
return '(function(){'
. 'var layout=document.getElementById("' . self::LAYOUT_ID . '"),btn=document.querySelector(".adminoptim-health__toggle");'
. 'if(layout){layout.classList.add("is-passing-collapsed");}'
. 'if(layout&&btn){btn.hidden=false;btn.addEventListener("click",function(){'
. 'var open=layout.classList.toggle("is-passing-collapsed")===false;'
. 'btn.setAttribute("aria-expanded",open?"true":"false");btn.textContent=open?btn.dataset.hide:btn.dataset.show;});}'
. 'document.addEventListener("click",function(e){'
. 'var a=e.target.closest&&e.target.closest(".adminoptim-health__jump");if(!a){return;}'
. 'var t=document.getElementById(a.getAttribute("href").slice(1));if(!t){return;}'
. 'e.preventDefault();'
. 'var calm=window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches;'
. 't.scrollIntoView({behavior:calm?"auto":"smooth",block:"center"});'
. 't.focus({preventScroll:true});t.classList.add("is-highlighted");'
. 'setTimeout(function(){t.classList.remove("is-highlighted");},2000);});'
. '})();';
}
/**
* Render one section card.
*
* @param array $section Section data.
*
* @return void
*/
private static function render_section( array $section ): void {
$heading_id = 'adminoptim-health-' . $section['id'];
$is_table = 'table' === $section['layout'];
$collapsed = ! empty( $section['collapsed'] );
$tag = $collapsed ? 'details' : 'section';
?>
< class="adminoptim-health__section" aria-labelledby="">
>
' . esc_html( $section['description'] ) . '
';
}
}
/**
* A section's checks: the rows that always show, then (when there are
* passing rows) the strip and the Good / Info rows. The strip is a label
* only; the CSS class the script adds hides the passing list.
*
* @param array $rows Section rows, already sorted by status.
*
* @return void
*/
private static function render_checks( array $rows ): void {
$visible = [];
$good = [];
$info = [];
foreach ( $rows as $row ) {
if ( ! self::is_passing_row( $row ) ) {
$visible[] = $row;
} elseif ( 'ok' === $row['status'] ) {
$good[] = $row;
} else {
$info[] = $row;
}
}
// Problem rows first, then dismissed rows, then Off rows, then the locked Pro row (sort is stable).
$rank = static function ( array $row ): int {
if ( ! empty( $row['locked'] ) ) {
return 3;
}
return 'off' === $row['status'] ? 2 : ( ! empty( $row['dismissed'] ) ? 1 : 0 );
};
usort(
$visible,
static function ( array $a, array $b ) use ( $rank ): int {
return $rank( $a ) <=> $rank( $b );
}
);
if ( $visible ) {
self::render_rows( $visible );
}
if ( $good || $info ) {
self::render_passing_strip( $good, $info );
self::render_rows( array_merge( $good, $info ), 'adminoptim-health__rows adminoptim-health__rows--passing' );
}
}
/**
* The strip: "✓ 5 Good HTTPS, file editing, …" wrapping over as many lines
* as needed, with ", plus N Info" when Info rows exist. Text only.
*
* @param array $good Good rows.
* @param array $info Info rows.
*
* @return void
*/
private static function render_passing_strip( array $good, array $info ): void {
$lead = $good ? $good : $info;
$status = $good ? 'ok' : 'info';
$names = implode( ', ', wp_list_pluck( $lead, 'label' ) );
$count = count( $lead );
?>
$rows Rows.
* @param string $list_class List class.
* @param bool $attention Compact "Needs attention" form of each row.
*
* @return void
*/
public static function render_rows( array $rows, string $list_class = 'adminoptim-health__rows', bool $attention = false ): void {
echo '