Hooks_Capture_Entity::get_table_name(), 'singular' => Hooks_Capture_Entity::get_table_name(), 'ajax' => false, ) ); } /** * Inits class hooks. That is called every time - not in some specific environment set. * * @return void * * @since 4.5.0 */ public static function init() { \add_filter( 'advan_cron_hooks', array( __CLASS__, 'add_cron_job' ) ); } /** * Adds a cron job for truncating the records in the hooks capture table * * @param array $crons - The array with all the crons associated with the plugin. * * @return array * * @since 4.5.0 */ public static function add_cron_job( $crons ) { if ( -1 !== (int) Settings::get_option( 'advana_hooks_capture_clear' ) ) { $crons[ ADVAN_PREFIX . 'hooks_capture_clear' ] = array( 'time' => Settings::get_option( 'advana_hooks_capture_clear' ), 'hook' => array( __CLASS__, 'truncate_hooks_capture_table' ), 'args' => array(), ); } return $crons; } /** * Truncates the hooks capture table from CRON job * * @return void * * @since 4.5.0 */ public static function truncate_hooks_capture_table() { Common_Table::truncate_table( null, Hooks_Capture_Entity::get_table_name() ); } /** * Adds the module to the main plugin menu * * @return void * * @since 4.5.0 */ public static function menu_add() { $hooks_hook = \add_submenu_page( Logs_List::MENU_SLUG, ADVAN_INNER_NAME, \esc_html__( 'Hooks Capture', '0-day-analytics' ), 'manage_options', self::HOOKS_CAPTURE_MENU_SLUG, array( Hooks_Capture_View::class, 'analytics_hooks_capture_page' ), 10 ); self::add_screen_options( $hooks_hook ); \add_filter( 'manage_' . $hooks_hook . '_columns', array( self::class, 'manage_columns' ) ); \add_action( 'load-' . $hooks_hook, array( Settings::class, 'aadvana_common_help' ) ); \add_action( 'load-' . $hooks_hook, array( self::class, 'process_actions_load' ) ); } /** * Handle actions on the early page load hook to avoid header issues. * * @return void * * @since 4.5.0 */ public static function process_actions_load() { if ( ! \current_user_can( 'manage_options' ) ) { return; } $table = new self( '' ); $table->handle_table_actions(); } /** * Prepares the list of items for displaying. * * @since 4.5.0 */ public function prepare_items() { $per_page = self::get_screen_option_per_page(); $current_page = $this->get_pagenum(); if ( 1 < $current_page ) { $offset = $per_page * ( $current_page - 1 ); } else { $offset = 0; } $search_string = self::escaped_search_input(); $wpdb_table = $this->get_table_name(); $orderby = ( isset( $_GET['orderby'] ) && '' !== trim( $_GET['orderby'] ) ) ? \esc_sql( \wp_unslash( $_GET['orderby'] ) ) : 'id'; $order = ( isset( $_GET['order'] ) && '' !== trim( $_GET['order'] ) ) ? \esc_sql( \wp_unslash( $_GET['order'] ) ) : 'DESC'; // Filter by hook type. $hook_type = ''; if ( isset( $_REQUEST['hook_type'] ) && ! empty( $_REQUEST['hook_type'] ) ) { $hook_type = \sanitize_text_field( \wp_unslash( $_REQUEST['hook_type'] ) ); } // Filter by trigger source. $trigger_source = ''; if ( isset( $_REQUEST['trigger_source'] ) && ! empty( $_REQUEST['trigger_source'] ) ) { $trigger_source = \sanitize_text_field( \wp_unslash( $_REQUEST['trigger_source'] ) ); } // Filter by date from. $date_from = ''; if ( isset( $_REQUEST['date_from'] ) && ! empty( $_REQUEST['date_from'] ) ) { $date_from = \sanitize_text_field( \wp_unslash( $_REQUEST['date_from'] ) ); } // Filter by date to. $date_to = ''; if ( isset( $_REQUEST['date_to'] ) && ! empty( $_REQUEST['date_to'] ) ) { $date_to = \sanitize_text_field( \wp_unslash( $_REQUEST['date_to'] ) ); } // Filter by group. $group_filter = 0; if ( isset( $_REQUEST['group_filter'] ) && ! empty( $_REQUEST['group_filter'] ) ) { $group_filter = absint( $_REQUEST['group_filter'] ); } $items = $this->fetch_table_data( array( 'search_string' => $search_string, 'offset' => $offset, 'per_page' => $per_page, 'wpdb_table' => $wpdb_table, 'orderby' => $orderby, 'order' => $order, 'hook_type' => $hook_type, 'trigger_source' => $trigger_source, 'date_from' => $date_from, 'date_to' => $date_to, 'group_filter' => $group_filter, ) ); $columns = self::manage_columns( array() ); $hidden = \get_user_option( 'manage' . WP_Helper::get_wp_screen()->id . 'columnshidden', false ); if ( ! $hidden ) { $hidden = array(); } $sortable = $this->get_sortable_columns(); $this->_column_headers = array( $columns, $hidden, $sortable ); $this->items = $items; $this->set_pagination_args( array( 'total_items' => $this->count, 'per_page' => self::get_screen_option_per_page(), 'total_pages' => ceil( $this->count / self::get_screen_option_per_page() ), ) ); } /** * Get a list of columns. * * @since 4.5.0 * * @return array */ public function get_columns() { return self::manage_columns( array() ); } /** * Get a list of sortable columns. * * @since 4.5.0 * * @return array */ public function get_sortable_columns() { $columns = array_keys( Hooks_Capture_Entity::get_column_names_admin() ); $sortable_columns = array(); foreach ( $columns as $value ) { $sortable_columns[ $value ] = array( $value, false ); } return $sortable_columns; } /** * Text displayed when no data is available * * @since 4.5.0 * * @return void */ public function no_items() { \esc_html_e( 'No hooks captured yet.', '0-day-analytics' ); } /** * Fetch table data from the WordPress database. * * @param array $args - The arguments collected / passed. * * @since 4.5.0 * * @return array */ public function fetch_table_data( array $args = array() ) { global $wpdb; $parsed_args = \wp_parse_args( $args, array( 'offset' => 0, 'search_string' => self::escaped_search_input(), 'per_page' => self::get_screen_option_per_page(), 'wpdb_table' => $this->get_table_name(), 'orderby' => 'id', 'order' => 'DESC', 'count' => false, 'hook_type' => '', 'trigger_source' => '', 'group_filter' => 0, 'date_from' => '', 'date_to' => '', ) ); $search_string = \sanitize_text_field( \wp_unslash( $parsed_args['search_string'] ) ); $offset = (int) $parsed_args['offset']; $per_page = (int) $parsed_args['per_page']; $wpdb_table = \sanitize_text_field( \wp_unslash( $parsed_args['wpdb_table'] ) ); $orderby = \sanitize_text_field( \wp_unslash( $parsed_args['orderby'] ) ); $order = \sanitize_text_field( \wp_unslash( $parsed_args['order'] ) ); $hook_type = \sanitize_text_field( \wp_unslash( $parsed_args['hook_type'] ) ); $trigger_source = \sanitize_text_field( \wp_unslash( $parsed_args['trigger_source'] ) ); $group_filter = (int) $parsed_args['group_filter']; $date_from = \sanitize_text_field( \wp_unslash( $parsed_args['date_from'] ) ); $date_to = \sanitize_text_field( \wp_unslash( $parsed_args['date_to'] ) ); $order = self::get_order( $order ); $orderby = self::get_order_by( $orderby ); $where_sql_parts = array(); $where_args = array(); // Search filter. if ( ! empty( $search_string ) ) { $where_sql_parts[] = '(hook_name LIKE %s OR trigger_source LIKE %s OR user_login LIKE %s)'; $where_args[] = '%' . $wpdb->esc_like( $search_string ) . '%'; $where_args[] = '%' . $wpdb->esc_like( $search_string ) . '%'; $where_args[] = '%' . $wpdb->esc_like( $search_string ) . '%'; } // Hook type filter. if ( ! empty( $hook_type ) && 'all' !== $hook_type ) { $where_sql_parts[] = 'hook_type = %s'; $where_args[] = $hook_type; } // Trigger source filter. if ( ! empty( $trigger_source ) && 'all' !== $trigger_source ) { $where_sql_parts[] = 'trigger_source = %s'; $where_args[] = $trigger_source; } // Group filter. if ( ! empty( $group_filter ) && $group_filter > 0 ) { $hooks_management_results = Hooks_Management_Entity::get_results( $wpdb->prepare( 'SELECT id FROM `' . Hooks_Management_Entity::get_table_name() . '` WHERE group_id = %d', $group_filter ) ); if ( ! empty( $hooks_management_results ) ) { $hooks_management_ids = array_column( $hooks_management_results, 'id' ); $placeholders = implode( ',', array_fill( 0, count( $hooks_management_ids ), '%d' ) ); $where_sql_parts[] = 'hooks_management_id IN (' . $placeholders . ')'; $where_args = array_merge( $where_args, $hooks_management_ids ); } } // Date from filter. if ( ! empty( $date_from ) ) { $date_from_timestamp = strtotime( $date_from . ' 00:00:00' ); if ( false !== $date_from_timestamp ) { $where_sql_parts[] = 'date_added >= %d'; $where_args[] = $date_from_timestamp; } } // Date to filter. if ( ! empty( $date_to ) ) { $date_to_timestamp = strtotime( $date_to . ' 23:59:59' ); if ( false !== $date_to_timestamp ) { $where_sql_parts[] = 'date_added <= %d'; $where_args[] = $date_to_timestamp; } } $where_sql = ''; if ( ! empty( $where_sql_parts ) ) { $where_sql = ' WHERE ' . implode( ' AND ', $where_sql_parts ); } // Count query. $count_sql = 'SELECT COUNT(*) FROM `' . $wpdb_table . '`' . $where_sql; if ( ! empty( $where_args ) ) { // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared $this->count = (int) Hooks_Capture_Entity::get_var( $wpdb->prepare( $count_sql, $where_args ) ); } else { // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared $this->count = (int) Hooks_Capture_Entity::get_var( $count_sql ); } if ( $parsed_args['count'] ) { return array(); } // Main query. $sql = 'SELECT * FROM `' . $wpdb_table . '`' . $where_sql . ' ORDER BY ' . $orderby . ' ' . $order . ' LIMIT %d OFFSET %d'; $query_args = array_merge( $where_args, array( $per_page, $offset ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared $results = Hooks_Capture_Entity::get_results( $wpdb->prepare( $sql, $query_args ) ); return is_array( $results ) ? $results : array(); } /** * Extra table nav controls. * * @param string $which Position of the nav (top/bottom). * * @return void * * @since 4.5.0 */ public function extra_tablenav( $which ) { if ( 'top' !== $which ) { if ( 'top' !== $which ) { ?>
' . \esc_html( $item[ $column_name ] ) . '';
// Make hook name a link to hooks management if hooks_management_id is available.
if ( ! empty( $item['hooks_management_id'] ) ) {
$edit_url = \network_admin_url( 'admin.php?page=advan_hooks_management&action=edit&id=' . absint( $item['hooks_management_id'] ) );
$hook_name = '' . \esc_html( $item[ $column_name ] ) . '';
}
// Check for post-related hooks and add post type information.
$post_type_info = '';
if ( self::is_post_related_hook( $item[ $column_name ] ) && ! empty( $item['parameters'] ) ) {
$post_type = self::extract_post_type_from_parameters( $item['parameters'] );
if ( $post_type ) {
$post_type_info = ' (' . \esc_html( $post_type ) . ')';
}
}
$display = $hook_label ? '' . \esc_html( $hook_label ) . $post_type_info . ' ' . $hook_name : $hook_name;
// Add row actions.
$actions = array(
'view' => sprintf(
'%s',
$id,
__( 'View Details', '0-day-analytics' )
),
'delete' => sprintf(
'%s',
esc_attr( self::HOOKS_CAPTURE_MENU_SLUG ),
$id,
\wp_create_nonce( 'delete_capture_' . $id ),
esc_js( __( 'Are you sure you want to delete this capture log?', '0-day-analytics' ) ),
__( 'Delete', '0-day-analytics' )
),
);
// Add disable hook action if applicable.
$actions = self::add_disable_hook_action( $actions, $item );
return sprintf(
'%s %s',
$display,
$this->row_actions( $actions )
);
case 'hook_type':
return '' . \esc_html( ucfirst( $item[ $column_name ] ) ) . '';
case 'execution_time':
return isset( $item[ $column_name ] ) ? \esc_html( number_format( (float) $item[ $column_name ], 6 ) ) : '';
case 'memory_usage':
return isset( $item[ $column_name ] ) ? \esc_html( size_format( (int) $item[ $column_name ] ) ) : '';
case 'is_cli':
return ! empty( $item[ $column_name ] ) ? '' : '';
case 'count':
$count = isset( $item[ $column_name ] ) ? (int) $item[ $column_name ] : 1;
if ( $count > 1 ) {
return '' . \esc_html( $count ) . '';
}
return \esc_html( $count );
case 'parameters':
return Hook_Parameter_Renderer::render_parameters(
$item['hook_name'],
isset( $item['parameters'] ) ? $item['parameters'] : ''
);
default:
// Default rendering for any other columns.
return isset( $item[ $column_name ] ) ? \esc_html( $item[ $column_name ] ) : '';
}
}
/**
* Handle table actions.
*
* @return void
*
* @since 4.5.0
*/
public function handle_table_actions() {
if ( ! \current_user_can( 'manage_options' ) ) {
return;
}
$action = $this->current_action();
if ( 'delete' === $action ) {
// Handle single delete from row actions.
if ( isset( $_REQUEST['id'] ) && ! is_array( $_REQUEST['id'] ) ) {
$id = absint( $_REQUEST['id'] );
$nonce = isset( $_REQUEST['_wpnonce'] ) ? \sanitize_text_field( \wp_unslash( $_REQUEST['_wpnonce'] ) ) : '';
if ( ! \wp_verify_nonce( $nonce, 'delete_capture_' . $id ) ) {
return;
}
if ( $id ) {
Hooks_Capture_Entity::delete_by_id( $id );
\wp_safe_redirect( \add_query_arg( array( 'deleted' => 1 ), \remove_query_arg( array( 'action', 'id', '_wpnonce' ) ) ) );
exit;
}
} else {
// Handle bulk delete.
$nonce = isset( $_REQUEST['_wpnonce'] ) ? \sanitize_text_field( \wp_unslash( $_REQUEST['_wpnonce'] ) ) : '';
if ( ! \wp_verify_nonce( $nonce, 'bulk-' . $this->_args['plural'] ) ) {
return;
}
$ids = isset( $_REQUEST['id'] ) ? (array) $_REQUEST['id'] : array();
$ids = array_map( 'absint', $ids );
if ( ! empty( $ids ) ) {
foreach ( $ids as $id ) {
Hooks_Capture_Entity::delete_by_id( $id );
}
\wp_safe_redirect( \add_query_arg( array( 'deleted' => count( $ids ) ), \wp_get_referer() ) );
exit;
}
}
}
}
/**
* Generates content for a single row of the table with group-based border coloring.
*
* @param object|array $item - The current item.
*
* @since 4.5.0
*/
public function single_row( $item ) {
$classes = '';
if ( isset( $item['hooks_management_id'] ) && ! empty( $item['hooks_management_id'] ) ) {
$hooks_management_record = Hooks_Management_Entity::load( 'id=%d', array( (int) $item['hooks_management_id'] ) );
if ( $hooks_management_record && isset( $hooks_management_record['group_id'] ) && ! empty( $hooks_management_record['group_id'] ) ) {
$classes .= ' group-' . (int) $hooks_management_record['group_id'];
}
}
echo '