* @package WP OAuth Server */ defined( 'ABSPATH' ) or die( 'No script kiddies please!' ); class WO_Table extends WP_List_Table { /** * Constructor, we override the parent to pass our own arguments * We usually focus on three parameters: singular and plural labels, as well as whether the class supports AJAX. */ public function __construct() { parent::__construct( array( 'singular' => 'wp_list_text_link', // Singular label 'plural' => 'wp_list_test_links', // plural label, also this well be one of the table css class 'ajax' => false, // We won't support Ajax for this table ) ); } /** * Add extra markup in the toolbars before or after the list. * * @param string $which , helps you decide if you add the markup after (bottom) or before (top) the list */ public function extra_tablenav( $which ) { if ( $which == 'top' ) { return false; } if ( $which == 'bottom' ) { return false; } } /** * Overide default functionality to remove _nonce field. * * @return [type] [description] */ public function display_tablenav( $which ) { ?>
bulk_actions( $which ); ?>
extra_tablenav( $which ); $this->pagination( $which ); ?>
__( 'Name' ), 'client_id' => __( 'Client ID' ), // 'client_secret' => __( 'Redirect URI' ) ); } /** * Decide which columns to activate the sorting functionality on. * * @return array $sortable, the array of columns that can be sorted by the user */ public function get_sortable_columns() { return $sortable = array( // 'name' => array('name'), // 'user_id'=>array('user_id') ); } /** * Prepare the table with different parameters, pagination, columns and table elements. * * @Updated 4.0.2. does not include user generated clients anymore via original query */ public function prepare_items() { global $wpdb, $_wp_column_headers; $screen = get_current_screen(); // 🔐 SECURITY FIX: Use prepared statements to prevent SQL injection $query = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}posts WHERE post_type = %s AND post_name NOT LIKE %s", 'wo_client', 'user_generated_%' ); $totalitems = $wpdb->query( $query ); $perpage = 5; // 🔐 SECURITY FIX: Validate pagination to prevent DoS attacks $paged = isset($_GET['paged']) ? intval($_GET['paged']) : 1; $paged = max(1, min(1000, $paged)); // Limit between 1-1000 pages $totalpages = ceil( $totalitems / $perpage ); $this->set_pagination_args( array( 'total_items' => $totalitems, 'total_pages' => $totalpages, 'per_page' => $perpage, ) ); $columns = $this->get_columns(); $hidden = array(); $sortable = $this->get_sortable_columns(); $this->_column_headers = array( $columns, $hidden, $sortable ); $results = $wpdb->get_results( $query ); $this->items = $results; } /** * Display the rows of records in the table. * * @return string, echo the markup of the rows */ public function display_rows() { // Get the records registered in the prepare_items method $records = $this->items; // Get the columns registered in the get_columns and get_sortable_columns methods list( $columns, $hidden ) = $this->get_column_info(); // Loop for each record if ( ! empty( $records ) ) { foreach ( $records as $rec ) { // Open the line echo ''; foreach ( $columns as $column_name => $column_display_name ) { // Style attributes for each col $class = "class='$column_name column-$column_name'"; $style = ''; if ( in_array( $column_name, $hidden ) ) { $style = ' style="display:none;"'; } $attributes = $class . $style; switch ( $column_name ) { case 'title': $edit_link = admin_url( 'admin.php?page=wo_edit_client&id=' . $rec->ID ); echo '' . esc_html( $rec->post_title ) . '
' . __( 'Edit', 'wp-oauth' ) . ' | ID").'\')" href="#">' . __( 'Delete', 'wp-oauth' ) . ' '; break; // case "user_id": echo ''.stripslashes($rec->user_id).''; break; case 'client_id': echo '' . get_post_meta( $rec->ID, 'client_id', true ) . ''; break; } } // Close the line echo ''; } } } }