breznflow/includes/Admin/WorkflowListTable.php
Michael fd83e4810b BreznFlow 1.0.0 — WordPress.org submission
Initial public release of BreznFlow, an n8n workflow renderer for WordPress.
Fully PHPCS-compliant (WordPress Coding Standards), security-hardened,
and ready for WordPress.org plugin review.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 11:27:36 +00:00

204 lines
5.5 KiB
PHP

<?php
/**
* Workflow list table for the admin dashboard.
*
* @package BreznFlow
* @since 1.0.0
*/
namespace BreznFlow\Admin;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'WP_List_Table' ) ) {
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
}
/**
* Extends WP_List_Table to display workflows in the admin.
*
* @since 1.0.0
*/
class WorkflowListTable extends \WP_List_Table {
/**
* Constructor — sets table configuration.
*
* @since 1.0.0
*/
public function __construct() {
parent::__construct(
array(
'singular' => 'workflow',
'plural' => 'workflows',
'ajax' => false,
)
);
}
/**
* Returns the list of table columns.
*
* @return array<string, string>
*/
public function get_columns(): array {
return array(
'cb' => '<input type="checkbox" />',
'title' => __( 'Title', 'breznflow' ),
'nodes' => __( 'Nodes', 'breznflow' ),
'ai' => __( 'AI', 'breznflow' ),
'mode' => __( 'Mode', 'breznflow' ),
'views' => __( 'Views', 'breznflow' ),
'shortcode' => __( 'Shortcode', 'breznflow' ),
'date' => __( 'Date', 'breznflow' ),
);
}
/**
* Returns sortable columns configuration.
*
* @return array<string, array<int, mixed>>
*/
protected function get_sortable_columns(): array {
return array(
'title' => array( 'title', false ),
'nodes' => array( 'nodes', false ),
'views' => array( 'views', false ),
'date' => array( 'date', false ),
);
}
/**
* Returns the bulk action choices.
*
* @return array<string, string>
*/
protected function get_bulk_actions(): array {
return array(
'delete' => __( 'Delete', 'breznflow' ),
);
}
/**
* Queries workflows and sets up pagination.
*
* @since 1.0.0
* @return void
*/
public function prepare_items(): void {
$per_page = 20;
$current_page = $this->get_pagenum();
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$orderby = isset( $_GET['orderby'] ) ? sanitize_key( $_GET['orderby'] ) : 'date';
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$order_raw = isset( $_GET['order'] ) ? strtolower( sanitize_text_field( wp_unslash( $_GET['order'] ) ) ) : '';
$order = 'asc' === $order_raw ? 'ASC' : 'DESC';
$args = array(
'post_type' => 'breznflow_workflow',
'post_status' => array( 'publish', 'draft' ),
'posts_per_page' => $per_page,
'paged' => $current_page,
'orderby' => $orderby,
'order' => $order,
);
$query = new \WP_Query( $args );
$this->set_pagination_args(
array(
'total_items' => $query->found_posts,
'per_page' => $per_page,
)
);
$this->items = $query->posts;
$this->_column_headers = array(
$this->get_columns(),
array(),
$this->get_sortable_columns(),
);
}
/**
* Renders a non-special column value for a row.
*
* @param \WP_Post $item The current post object.
* @param string $column_name The current column name.
* @return string
*/
protected function column_default( $item, $column_name ): string {
switch ( $column_name ) {
case 'nodes':
return (string) (int) get_post_meta( $item->ID, '_breznflow_node_count', true );
case 'ai':
$has_ai = (int) get_post_meta( $item->ID, '_breznflow_has_ai_nodes', true );
return $has_ai ? '<span class="breznflow-badge-ai" title="' . esc_attr__( 'Contains AI nodes', 'breznflow' ) . '">AI</span>' : '—';
case 'mode':
$mode = get_post_meta( $item->ID, '_breznflow_default_mode', true );
return esc_html( $mode ? $mode : 'visual' );
case 'views':
return (string) (int) get_post_meta( $item->ID, '_breznflow_view_count', true );
case 'shortcode':
$sc = '[breznflow id="' . $item->ID . '"]';
return '<code>' . esc_html( $sc ) . '</code> '
. '<button class="button button-small breznflow-copy-sc" data-sc="' . esc_attr( $sc ) . '">'
. esc_html__( 'Copy', 'breznflow' )
. '</button>';
case 'date':
return esc_html( get_the_date( 'Y-m-d', $item ) );
default:
return '';
}
}
/**
* Renders the title column with edit/delete row actions.
*
* @param \WP_Post $item The current post object.
* @return string
*/
protected function column_title( $item ): string {
$edit_url = add_query_arg(
array(
'page' => 'breznflow-add',
'step' => '2',
'post_id' => $item->ID,
),
admin_url( 'admin.php' )
);
$delete_url = wp_nonce_url(
add_query_arg(
array(
'action' => 'delete',
'post' => $item->ID,
'page' => 'breznflow',
),
admin_url( 'admin.php' )
),
'breznflow_delete_' . $item->ID
);
$title = esc_html( $item->post_title );
$status = 'publish' === $item->post_status ? '' : ' <span class="post-state">(' . esc_html__( 'Draft', 'breznflow' ) . ')</span>';
$actions = array(
'edit' => '<a href="' . esc_url( $edit_url ) . '">' . esc_html__( 'Edit', 'breznflow' ) . '</a>',
'delete' => '<a href="' . esc_url( $delete_url ) . '" class="submitdelete">' . esc_html__( 'Delete', 'breznflow' ) . '</a>',
);
return '<strong><a href="' . esc_url( $edit_url ) . '">' . $title . '</a>' . $status . '</strong>'
. $this->row_actions( $actions );
}
/**
* Renders the checkbox column for bulk actions.
*
* @param \WP_Post $item The current post object.
* @return string
*/
protected function column_cb( $item ): string {
return '<input type="checkbox" name="workflow[]" value="' . esc_attr( (string) $item->ID ) . '" />';
}
}