prefix . 'guardforge_audit_log';
if ( isset( $_POST['guardforge_clear_audit_log'] ) && check_admin_referer( 'guardforge_clear_audit_log' ) ) {
// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- plugin-owned table ($wpdb->prefix + a literal); a table name cannot be a placeholder, and this runs only on uninstall or behind the nonce-checked clear button.
$wpdb->query( "TRUNCATE TABLE {$guardforge_table}" );
echo '
✓ ' . esc_html__( 'Audit log cleared.', 'guardforge' ) . '
';
}
$per_page = 50;
$paged = max( 1, ( isset( $_GET['apaged'] ) ? absint( wp_unslash( $_GET['apaged'] ) ) : 1 ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- display-only: this screen's own filter or pager; it narrows what is listed and changes nothing.
$guardforge_filter_ev = ( isset( $_GET['event_type'] ) ? sanitize_text_field( wp_unslash( $_GET['event_type'] ) ) : '' ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- display-only: this screen's own filter or pager; it narrows what is listed and changes nothing.
$guardforge_filter_user = ( isset( $_GET['user_login'] ) ? sanitize_text_field( wp_unslash( $_GET['user_login'] ) ) : '' ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- display-only: this screen's own filter or pager; it narrows what is listed and changes nothing.
$guardforge_where = [];
if ( $guardforge_filter_ev ) { $guardforge_where[] = $wpdb->prepare( 'event_type LIKE %s', '%' . $wpdb->esc_like( $guardforge_filter_ev ) . '%' ); }
if ( $guardforge_filter_user ) { $guardforge_where[] = $wpdb->prepare( 'user_login LIKE %s', '%' . $wpdb->esc_like( $guardforge_filter_user ) . '%' ); }
$guardforge_wsql = $guardforge_where ? 'WHERE ' . implode( ' AND ', $guardforge_where ) : '';
$guardforge_total = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$guardforge_table} {$guardforge_wsql}" ); // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- plugin-owned table ($wpdb->prefix + a literal); $guardforge_wsql is built only from clauses each passed through $wpdb->prepare above.
$pages = (int) ceil( $guardforge_total / $per_page );
// Clamped before the OFFSET is worked out: a bookmarked ?apaged=9 that pruning
// has since put out of range would otherwise draw an empty table under a
// "Page 9 of 2" the operator cannot get out of.
$paged = min( $paged, max( 1, $pages ) );
// Newest first, and among events of the same second the newest row (highest
// id) first: event_time is a second wide, and with no last key MySQL may cut
// such a tie one way for page 1 and another for page 2, so a row would show
// on two pages and another on none.
// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name is prefixed constant, $guardforge_wsql uses $wpdb->prepare clauses, LIMIT/OFFSET are cast integers.
$guardforge_rows = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$guardforge_table} {$guardforge_wsql} ORDER BY event_time DESC, id DESC LIMIT %d OFFSET %d", $per_page, ( $paged - 1 ) * $per_page ) );
$guardforge_explainer = class_exists( 'GUARDFORGE_Event_Explainer' ) ? GUARDFORGE_Event_Explainer::instance() : null;
$guardforge_events = [];
if ( $guardforge_explainer ) {
foreach ( $guardforge_rows as $guardforge_row ) {
$guardforge_events[ (int) $guardforge_row->id ] = $guardforge_explainer->normalise( 'audit', $guardforge_row );
}
$guardforge_explainer->prime( $guardforge_events );
}
/* translators: %d is the total number of admin action events. */
$guardforge_subtitle = sprintf( __( 'Admin actions · total: %d', 'guardforge' ), $guardforge_total );
?>
✓ 1 ) {
echo ' ' . esc_html( sprintf(
/* translators: %d is how many times the chain restarted from a fresh genesis. */
_n(
'(chain restarted %d time — pre-upgrade or unhashed rows are in the mix; each restart is only expected once, at row 1.)',
'(chain restarted %d times — pre-upgrade or unhashed rows are in the mix; each restart is only expected once, at row 1.)',
$guardforge_verify_g,
'guardforge'
),
$guardforge_verify_g
) );
}
if ( $guardforge_verify_truncated ) {
echo ' ' . esc_html__( 'This table is larger than one check can walk within a single request, so only the oldest rows were checked this time — nothing broken was found among them. Lowering how long the audit log is kept (Settings) shrinks the table so a future check can cover all of it.', 'guardforge' );
}
?>
|
|
|
|
|
|
|
|
| event_time ); ?> |
user_login ?: '—' ); ?> |
event_type ); ?> |
object_type ); ?> |
object_name . ' ' . $guardforge_row->details ) ); ?> |
ip ); ?> |
render_control( 'audit', $guardforge_events[ (int) $guardforge_row->id ] ); ?> |
render_explanation_row( $guardforge_events[ (int) $guardforge_row->id ], 7 ); } ?>