prefix . 'guardforge_login_log';
if ( isset( $_POST['guardforge_clear_login_log'] ) && check_admin_referer( 'guardforge_clear_login_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__( 'Login log cleared.', 'guardforge' ) . '
';
}
$per_page = 50;
$paged = max( 1, ( isset( $_GET['lpaged'] ) ? absint( wp_unslash( $_GET['lpaged'] ) ) : 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_r = ( isset( $_GET['result'] ) ? sanitize_text_field( wp_unslash( $_GET['result'] ) ) : '' ); // 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_ip = ( isset( $_GET['ip'] ) ? sanitize_text_field( wp_unslash( $_GET['ip'] ) ) : '' ); // 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_r ) { $guardforge_where[] = $wpdb->prepare( 'result = %s', $guardforge_filter_r ); }
if ( $guardforge_filter_ip ) { $guardforge_where[] = $wpdb->prepare( 'ip LIKE %s', '%' . $wpdb->esc_like( $guardforge_filter_ip ) . '%' ); }
$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 ?lpaged=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( 'login', $guardforge_row );
}
$guardforge_explainer->prime( $guardforge_events );
}
// GF-3: a successful login is not an event anyone needs explained — the
// button only earns its place on a row that is already a question. These
// are the three values class-bruteforce.php::log_attempt actually writes
// besides 'success' (see on_login_failed); a fourth show up here only if
// that vocabulary changes, and this list has to change with it.
$guardforge_explainable_results = [ 'failed', 'soft_locked', 'locked' ];
/* translators: %d is the total number of authentication attempts. */
$guardforge_subtitle = sprintf( __( 'Authentication attempts · total: %d', 'guardforge' ), $guardforge_total );
?>
|
|
|
|
|
|
|
| event_time ); ?> |
ip ); ?> |
username ); ?> |
result ); ?> |
user_agent ); ?> |
result, $guardforge_explainable_results, true ) ) : ?>
render_control( 'login', $guardforge_events[ (int) $guardforge_row->id ] ); ?>
" would, or every successful row
// in the page goes visibly shorter than the ones around it. ?>
|
render_explanation_row( $guardforge_events[ (int) $guardforge_row->id ], 6 ); } ?>