>> */ function fpca_allowed_markup(): array { return [ 'th' => [ 'scope' => [], 'class' => [] ], 'a' => [ 'href' => [], 'class' => [] ], 'span' => [ 'class' => [], 'data-user-id' => [] ], 'img' => [ 'src' => [], 'width' => [], 'height' => [], 'alt' => [], 'class' => [] ], 'button' => [ 'type' => [], 'class' => [], 'title' => [] ], ]; } /** * Freshness score badge markup for the admin tables. * * Returns markup, so call sites run it through * wp_kses( ..., fpca_allowed_markup() ). Every interpolated value is * also escaped here, at the point of concatenation. * * @param int|null $score 0-100, or null when the post has never been scored. */ function fpca_score_badge( ?int $score ): string { if ( $score === null ) { return '—'; } if ( $score >= 67 ) { $cls = 'fpca-badge--fresh'; } elseif ( $score >= 34 ) { $cls = 'fpca-badge--stale'; } else { $cls = 'fpca-badge--urgent'; } return '' . esc_html( (string) $score ) . ''; } /** * Sort URL for a dashboard column header. * * Internal to fpca_th(); moved here alongside it rather than left in * dashboard.php, since function declarations can't be split across files by * caller. */ function fpca_sort_url( string $col, string $current_orderby, string $current_toggle ): string { return add_query_arg( [ 'page' => 'freshplugins-content-audit', 'orderby' => $col, 'order' => ( $current_orderby === $col ) ? $current_toggle : 'ASC', ], admin_url( 'admin.php' ) ); } /** * Column header with sort link, used by the dashboard table. * * Returns markup; call sites run it through * wp_kses( ..., fpca_allowed_markup() ). */ function fpca_th( string $col, string $label, string $orderby, string $order, string $toggle, string $extra_class = '' ): string { $url = fpca_sort_url( $col, $orderby, $toggle ); $classes = 'manage-column sortable'; if ( $extra_class ) { $classes .= ' ' . $extra_class; } $arrow = ''; if ( $orderby === $col ) { $classes .= $order === 'ASC' ? ' sorted asc' : ' sorted desc'; $arrow = $order === 'ASC' ? ' ▲' : ' ▼'; } return sprintf( '