';
echo '| ' . esc_html( $id === null ? '' : (string) $id ) . ' | ';
echo '' . esc_html( AdminTimeFormatter::format( (string) $createdAt ) ) . ' | ';
echo '' . esc_html( (string) $auditKind ) . ' | ';
echo '' . esc_html( (string) $eventType ) . ' | ';
echo '' . esc_html( (string) $action ) . ' | ';
echo '' . esc_html( (string) $result ) . ' | ';
echo '' . esc_html( self::formatActorOrTarget( $actor ) ) . ' | ';
echo '' . esc_html( self::formatActorOrTarget( $target ) ) . ' | ';
echo '' . esc_html( (string) $safeSummary ) . ' | ';
echo '' . esc_html( $requestId === null ? '' : (string) $requestId ) . ' | ';
echo '' . esc_html( $correlationId === null ? '' : (string) $correlationId ) . ' | ';
echo '' . esc_html( $jobId === null ? '' : (string) $jobId ) . ' | ';
echo '
';
}
/**
* Format an `actor` / `target` `{ type, id }` sub-object (or
* null) into a single human-readable cell value.
*
* Layout:
*
* - `null` → empty string.
* - `{ type: 'user', id: '42' }` → `"user:42"`.
* - `{ type: 'system', id: null }` → `"system"`.
* - `{ type: null, id: '42' }` → `"42"` (defensive, should
* not happen because the §12.4 projection collapses both-
* null to null in the first place).
*
* @param mixed $value Raw `actor` / `target` slot from the
* projected row.
*/
private static function formatActorOrTarget( $value ): string {
if ( ! is_array( $value ) ) {
return '';
}
$type = isset( $value['type'] ) && is_string( $value['type'] ) ? $value['type'] : null;
$id = isset( $value['id'] ) && is_string( $value['id'] ) ? $value['id'] : null;
if ( $type === null && $id === null ) {
return '';
}
if ( $type === null ) {
return (string) $id;
}
if ( $id === null ) {
return $type;
}
return $type . ':' . $id;
}
/**
* Render the textual `Page X of Y` pagination nav with prev /
* next anchors that preserve the current filter state.
*
* @param AuditEventListQuery $query Originating query.
* @param int $totalPages Total page count.
* @param int $totalItems Total item count.
* @param string $menuSlug Admin page slug for generated links.
* @param string $anchor Optional URL fragment to preserve tab focus.
*/
private function renderPagination( AuditEventListQuery $query, int $totalPages, int $totalItems, string $menuSlug = self::MENU_SLUG, string $anchor = '' ): void {
$current = $query->page();
echo '';
echo '';
echo esc_html( sprintf( '%d件', $totalItems ) );
echo '';
$this->renderPerPageLinks( $query, $menuSlug, $anchor );
if ( $totalPages > 1 ) {
echo ' ';
} else {
echo ' ' . esc_html( sprintf( '%d / %d', $current, max( 1, $totalPages ) ) ) . '';
}
echo '
';
}
/**
* Render 10/20/50 page-size links for audit events.
*
* @param AuditEventListQuery $query Current query.
* @param string $menuSlug Admin page slug for generated links.
* @param string $anchor Optional URL fragment to preserve tab focus.
*/
private function renderPerPageLinks( AuditEventListQuery $query, string $menuSlug, string $anchor ): void {
echo '