screen = get_current_screen(); parent::__construct( array( 'singular' => 'campaign', 'plural' => 'campaigns', 'ajax' => false, ) ); } /** * Prepare items. * * @since 1.0.0 * @return void */ public function prepare_items() { wp_verify_nonce( '_wpnonce' ); $columns = $this->get_columns(); $hidden = $this->get_hidden_columns(); $sortable = $this->get_sortable_columns(); $this->_column_headers = array( $columns, $hidden, $sortable ); $per_page = $this->get_items_per_page( 'aicw_campaigns_per_page', 20 ); $paged = $this->get_pagenum(); $order_by = isset( $_GET['orderby'] ) ? sanitize_text_field( wp_unslash( $_GET['orderby'] ) ) : ''; $order = isset( $_GET['order'] ) ? sanitize_text_field( wp_unslash( $_GET['order'] ) ) : ''; $search = isset( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : ''; $args = array( 'post_type' => 'aicw_campaign', 'posts_per_page' => $per_page, 'paged' => $paged, 's' => $search, 'orderby' => $order_by, 'order' => $order, 'post_status' => 'any', ); /** * Filter the query arguments for the list table. * * @param array $args An associative array of arguments. * * @since 1.0.0 */ $args = apply_filters( 'aicw_campaigns_table_query_args', $args ); $this->items = aicw_get_campaigns( $args ); $total = aicw_get_campaigns( $args, true ); $this->prime_campaign_stats(); $this->set_pagination_args( array( 'total_items' => $total, 'per_page' => $per_page, ) ); } /** * Load pending/published post counts for all listed campaigns in one query. * * @since 2.3.0 * @return void */ protected function prime_campaign_stats() { global $wpdb; $ids = array_filter( wp_list_pluck( $this->items, 'ID' ) ); if ( empty( $ids ) ) { return; } $placeholders = implode( ',', array_fill( 0, count( $ids ), '%d' ) ); // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Single aggregated read for the current page of campaigns; placeholders are built above. $rows = $wpdb->get_results( $wpdb->prepare( "SELECT pm.meta_value AS campaign_id, SUM( p.post_type = 'aicw_post' ) AS pending, SUM( p.post_type <> 'aicw_post' ) AS published FROM {$wpdb->postmeta} pm INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id WHERE pm.meta_key = '_aicw_campaign_id' AND pm.meta_value IN ( {$placeholders} ) GROUP BY pm.meta_value", $ids ) ); // phpcs:enable foreach ( $rows as $row ) { $this->campaign_stats[ (int) $row->campaign_id ] = array( 'pending' => (int) $row->pending, 'published' => (int) $row->published, ); } } /** * No items found text. * * @since 1.0.0 * @return void */ public function no_items() { esc_html_e( 'No campaigns found.', 'ai-content-writer' ); } /** * Get the table columns * * @return array * @since 1.0.0 */ public function get_columns() { $columns = array( 'cb' => '', 'name' => __( 'Name', 'ai-content-writer' ), 'description' => __( 'Description', 'ai-content-writer' ), 'progress' => __( 'Progress', 'ai-content-writer' ), 'activity' => __( 'Activity', 'ai-content-writer' ), 'status' => __( 'Status', 'ai-content-writer' ), 'date' => __( 'Date', 'ai-content-writer' ), ); return $columns; } /** * Get hidden columns. */ public function get_hidden_columns() { return get_hidden_columns( get_current_screen() ); } /** * Get sortable columns. */ public function get_sortable_columns() { return array( 'name' => array( 'post_title', true ), 'status' => array( 'post_status', true ), ); } /** * Get primary columns name. or define the primary column name. */ public function get_primary_column_name() { return 'name'; } /** * Renders the checkbox column in the items list table. * * @param Object $item The current master key object. * * @return string Displays a checkbox. * @since 1.0.0 */ public function column_cb( $item ) { return sprintf( '', esc_attr( $item->ID ) ); } /** * Renders the master_key column in the items list table. * * @param Object $item The current master key object. * * @return string Displays the Master key. * @since 1.0.0 */ public function column_name( $item ) { $edit_url = add_query_arg( array( 'edit' => $item->ID ), admin_url( 'admin.php?page=aicw-campaigns' ) ); $delete_url = add_query_arg( array( 'ids' => $item->ID, 'action' => 'delete', ), admin_url( 'admin.php?page=aicw-campaigns' ) ); $item_title = sprintf( '%2$s', $edit_url, esc_html( $item->post_title ) ); // translators: %d: key id. $actions['ids'] = sprintf( __( 'ID: %d', 'ai-content-writer' ), esc_html( $item->ID ) ); $actions['edit'] = sprintf( '%2$s', $edit_url, __( 'Edit', 'ai-content-writer' ) ); $actions['delete'] = sprintf( '%2$s', wp_nonce_url( $delete_url, 'bulk-' . $this->_args['plural'] ), __( 'Delete', 'ai-content-writer' ) ); return sprintf( '%1$s %2$s', $item_title, $this->row_actions( $actions ) ); } /** * Get bulk actions. * * @since 1.0.0 * @return array */ public function get_bulk_actions() { return array( 'delete' => __( 'Delete', 'ai-content-writer' ), ); } /** * Display column description. * * @param Object $item Item. * * @since 1.0.0 * @return string Description. */ protected function column_description( $item ) { // Get the post content and trim it to 10 words. $description_excerpt = wp_trim_words( $item->post_content, 10, '...' ); return wp_kses_post( $description_excerpt ?? '—' ); } /** * Display column progress: published posts vs the campaign target. * * @param Object $item Item. * * @since 2.3.0 * @return string Progress. */ protected function column_progress( $item ) { $stats = isset( $this->campaign_stats[ $item->ID ] ) ? $this->campaign_stats[ $item->ID ] : array( 'pending' => 0, 'published' => 0, ); $target = absint( get_post_meta( $item->ID, '_aicw_campaign_target', true ) ); $published = $stats['published']; $percent = $target > 0 ? min( 100, round( ( $published / $target ) * 100 ) ) : 0; $label = sprintf( /* translators: 1: Published posts. 2: Campaign target. */ esc_html__( '%1$d / %2$d published', 'ai-content-writer' ), $published, $target ); if ( $stats['pending'] > 0 ) { $label .= '
' . sprintf( /* translators: %d: Number of posts in progress. */ esc_html__( '%d in progress', 'ai-content-writer' ), $stats['pending'] ) . ''; } return sprintf( '
%2$s', esc_attr( $percent ), $label ); } /** * Display column activity: last and next scheduled run. * * @param Object $item Item. * * @since 2.3.0 * @return string Activity. */ protected function column_activity( $item ) { $lines = array(); $last_run = strtotime( $item->post_modified_gmt . ' UTC' ); if ( $last_run && $item->post_modified_gmt !== $item->post_date_gmt ) { $lines[] = sprintf( /* translators: %s: Human-readable time difference. */ esc_html__( 'Last run: %s ago', 'ai-content-writer' ), human_time_diff( $last_run ) ); } $next_run = wp_next_scheduled( 'aicw_process_campaign', array( $item->ID ) ); if ( $next_run ) { $lines[] = sprintf( /* translators: %s: Human-readable time difference. */ esc_html__( 'Next run: in %s', 'ai-content-writer' ), human_time_diff( time(), $next_run ) ); } $lines[] = sprintf( '%2$s', esc_url( add_query_arg( 'campaign_id', $item->ID, admin_url( 'admin.php?page=aicw-logs' ) ) ), esc_html__( 'View logs', 'ai-content-writer' ) ); return implode( '
', $lines ); } /** * Display column status. * * @param Object $item Item. * * @since 1.0.0 * @return string Status. */ protected function column_status( $item ) { $status = $item->post_status; // If status is equal to publish then show the status as active. if ( 'publish' === $status ) { return esc_html__( 'Active', 'ai-content-writer' ); } $status = get_post_status_object( $status ); $status = $status->label; return esc_html( $status ?? '—' ); } /** * Display column date. * * @param Object $item Item. * * @since 1.0.0 * @return string Date. */ protected function column_date( $item ) { $value = '—'; $date = $item->post_date; if ( $date ) { $value = sprintf( '', esc_attr( $date ), esc_html( date_i18n( get_option( 'date_format' ) . ' | ' . get_option( 'time_format' ), strtotime( $date ) ) ) ); } return $value; } }