` follows it so WordPress puts admin notices after the whole * header instead of inside it (it moves notices to just after the first heading unless it finds that marker). * * @param string $title The h1 text (already translated). * @param string $eyebrow Text after "WP Bulk Replace", e.g. "Batch 11". Empty for none. * @param string[] $chips Meta chips, each already-escaped HTML (the batch page passes labelled values with ). * @param string $action An already-escaped link to show at the header's right (the page's main action), or ''. * @return string */ function wpbr_admin_header($title, $eyebrow = '', array $chips = array(), $action = '') { $eyebrow_text = __('WPBR Bulk Replace', 'wpbr-bulk-replace') . ($eyebrow !== '' ? ' ยท ' . $eyebrow : ''); $html = '
'; $html .= ''; $html .= '

' . esc_html($eyebrow_text) . '

'; $html .= '

' . esc_html($title) . '

'; if ($action !== '') { $html .= '
' . $action . '
'; } if ($chips) { $html .= '

'; foreach ($chips as $chip) { $html .= '' . $chip . ''; } $html .= '

'; } $html .= '

'; return $html; } /** What the header may contain: exactly the tags wpbr_admin_header() writes. */ function wpbr_admin_header_allowed_html() { return array( 'header' => array('class' => true), 'img' => array('class' => true, 'src' => true, 'alt' => true, 'width' => true, 'height' => true), 'p' => array('class' => true), 'h1' => array(), 'span' => array('class' => true), 'code' => array(), 'hr' => array('class' => true), 'div' => array('class' => true), 'a' => array('class' => true, 'href' => true), ); } /** Print the header, passed through wp_kses() with the allowed list above. */ function wpbr_admin_header_print($title, $eyebrow = '', array $chips = array(), $action = '') { echo wp_kses(wpbr_admin_header($title, $eyebrow, $chips, $action), wpbr_admin_header_allowed_html()); } /** * The glyph for each kind of status. A status always has a glyph AND a word, so colour is never the only signal. * * @return array */ function wpbr_status_glyphs() { return array( 'ok' => "\u{2713}", 'warn' => '!', 'error' => "\u{2715}", 'processing' => "\u{27F3}", 'paused' => "\u{2016}", 'idle' => "\u{25CB}", 'off' => "\u{2013}", 'unavailable' => "\u{2205}", 'conflict' => "\u{21C4}", 'info' => 'i', ); } /** * A status badge: a solid glyph block and the status word on a tint. * * Shared by every screen that shows a state (the Activity Log result, the licence state, a network job's status), so they read as * one thing. The glyph is decoration (`aria-hidden`); the label is what a screen reader hears. * * @param string $kind One of the keys of wpbr_status_glyphs(). Unknown kinds fall back to 'idle'. * @param string $label The visible, translated status word. * @return string HTML, escaped here. */ function wpbr_status_badge($kind, $label) { $glyphs = wpbr_status_glyphs(); $kind = isset($glyphs[$kind]) ? $kind : 'idle'; return '' . esc_html($label) . ''; } /** What a status badge may contain, for wp_kses(). */ function wpbr_status_badge_allowed_html() { return array('span' => array('class' => true, 'aria-hidden' => true)); } /** Print a status badge. */ function wpbr_status_badge_print($kind, $label) { echo wp_kses(wpbr_status_badge($kind, $label), wpbr_status_badge_allowed_html()); } function wpbr_admin_enqueue_style() { if (!wpbr_is_admin_screen()) { return; } wp_enqueue_style('wpbr-admin', wpbr_admin_style_url(), array(), WPBR_VERSION); } if (function_exists('add_action')) { add_action('admin_enqueue_scripts', 'wpbr_admin_enqueue_style'); }