publish ?? 0 ); $sync_count = MetaForge_JSONSyncManager::count_pending_sync(); $all_class = ( $current === 'all' || empty( $current ) ) ? 'class="current"' : ''; $views['all'] = sprintf( '%s (%d)', admin_url( 'admin.php?page=metaforge' ), $all_class, __( 'All', 'metaforge-engine' ), $all_count ); if ( $sync_count > 0 || $current === 'sync' ) { $sync_class = ( $current === 'sync' ) ? 'class="current"' : ''; $views['sync'] = sprintf( '%s (%d)', admin_url( 'admin.php?page=metaforge&status=sync' ), $sync_class, __( 'Sync Available', 'metaforge-engine' ), $sync_count ); } return $views; } public function get_columns() { return array( 'cb' => '', 'title' => 'Title', 'key' => 'Group Key', 'post_type' => 'Location', 'fields' => 'Fields', ); } protected function get_primary_column_name() { return 'title'; } public function get_sortable_columns() { return array( 'title' => array( 'group_name', false ), 'post_type' => array( 'post_type', false ), 'fields' => array( 'fields', false ), ); } public function get_bulk_actions() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only status filter. $status = isset( $_GET['status'] ) ? sanitize_key( wp_unslash( $_GET['status'] ) ) : 'all'; if ( $status === 'sync' ) { return array( 'bulk-sync' => 'Sync Selected', 'bulk-delete-sync' => 'Delete Selected', ); } return array( 'bulk-delete' => 'Delete', ); } public function process_bulk_action() { if ( 'bulk-delete' === $this->current_action() ) { check_admin_referer( 'metaforge_bulk_field_groups', 'metaforge_bulk_field_groups_nonce' ); if ( ! current_user_can( 'manage_metaforge' ) ) { wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'metaforge-engine' ) ); } $delete_ids = isset( $_GET['bulk-delete'] ) ? array_map( 'intval', wp_unslash( (array) $_GET['bulk-delete'] ) ) : array(); if ( ! empty( $delete_ids ) ) { foreach ( $delete_ids as $id ) { MetaForge_JSONSyncManager::delete_group( intval( $id ) ); wp_delete_post( intval( $id ), true ); } MetaForge_Loader::clear_cache(); wp_safe_redirect( admin_url( 'admin.php?page=metaforge&deleted=true' ) ); exit; } } elseif ( 'bulk-sync' === $this->current_action() ) { check_admin_referer( 'metaforge_bulk_field_groups', 'metaforge_bulk_field_groups_nonce' ); if ( ! current_user_can( 'manage_metaforge' ) ) { wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'metaforge-engine' ) ); } $keys = isset( $_GET['bulk-delete'] ) ? array_map( 'sanitize_text_field', wp_unslash( (array) $_GET['bulk-delete'] ) ) : array(); if ( ! empty( $keys ) ) { $count = MetaForge_JSONSyncManager::sync_groups( $keys ); $remaining = MetaForge_JSONSyncManager::count_pending_sync(); $redirect_url = ( $remaining > 0 ) ? admin_url( 'admin.php?page=metaforge&status=sync&synced=' . $count ) : admin_url( 'admin.php?page=metaforge&synced=' . $count ); wp_safe_redirect( $redirect_url ); exit; } } elseif ( 'bulk-delete-sync' === $this->current_action() ) { check_admin_referer( 'metaforge_bulk_field_groups', 'metaforge_bulk_field_groups_nonce' ); if ( ! current_user_can( 'manage_metaforge' ) ) { wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'metaforge-engine' ) ); } $keys = isset( $_GET['bulk-delete'] ) ? array_map( 'sanitize_text_field', wp_unslash( (array) $_GET['bulk-delete'] ) ) : array(); if ( ! empty( $keys ) ) { $del_count = 0; foreach ( $keys as $k ) { if ( MetaForge_JSONSyncManager::delete_sync_file_by_key( $k ) ) { ++$del_count; } } $remaining = MetaForge_JSONSyncManager::count_pending_sync(); $redirect_url = ( $remaining > 0 ) ? admin_url( 'admin.php?page=metaforge&status=sync&sync_deleted=' . $del_count ) : admin_url( 'admin.php?page=metaforge&sync_deleted=' . $del_count ); wp_safe_redirect( $redirect_url ); exit; } } } public function column_default( $item, $column_name ) { return isset( $item[ $column_name ] ) ? esc_html( $item[ $column_name ] ) : ''; } public function column_cb( $item ) { return sprintf( '', esc_attr( $item['index'] ) ); } public function column_title( $item ) { $title = ! empty( $item['group_name'] ) ? esc_html( $item['group_name'] ) : '(no title)'; $drag_handle = ''; if ( ! empty( $item['is_sync_item'] ) ) { $sync_url = wp_nonce_url( admin_url( 'admin.php?page=metaforge&action=sync_one&key=' . rawurlencode( $item['index'] ) ), 'metaforge_sync_one' ); $delete_sync_url = wp_nonce_url( admin_url( 'admin.php?page=metaforge&action=delete_sync_file&key=' . rawurlencode( $item['index'] ) ), 'metaforge_delete_sync_file' ); $actions = array( 'sync' => 'Sync Now', 'delete' => 'Delete JSON', ); return sprintf( '
' . esc_html( $key ) . '';
}
public function column_post_type( $item ) {
$locations = array();
if ( ! empty( $item['location_rules'] ) ) {
foreach ( $item['location_rules'] as $rule_group ) {
foreach ( $rule_group as $rule ) {
$param = $rule['param'] ?? '';
$value = $rule['value'] ?? '';
if ( empty( $value ) ) {
continue;
}
if ( $param === 'post_type' ) {
$locations[] = ucfirst( $value );
} elseif ( $param === 'options_page' ) {
$locations[] = 'Options: ' . ucwords( str_replace( array( '_', '-' ), ' ', $value ) );
} elseif ( $param === 'page_template' ) {
$theme_templates = function_exists( 'wp_get_theme' ) ? wp_get_theme()->get_page_templates() : array();
$tpl_label = $theme_templates[ $value ] ?? '';
if ( ! $tpl_label ) {
foreach ( $theme_templates as $f => $n ) {
if ( basename( $f ) === basename( $value ) ) {
$tpl_label = $n;
break;
}
}
}
if ( ! $tpl_label ) {
$tpl_label = ucwords( str_replace( array( '_', '-' ), ' ', basename( $value, '.php' ) ) );
}
$locations[] = 'Template: ' . $tpl_label;
} elseif ( $param === 'post_status' ) {
$locations[] = 'Status: ' . ucfirst( $value );
} elseif ( $param === 'page_type' ) {
$page_type_labels = array(
'front_page' => 'Front Page',
'posts_page' => 'Posts Page',
'top_level' => 'Top Level Page',
'parent' => 'Parent Page',
'child' => 'Child Page',
);
$locations[] = $page_type_labels[ $value ] ?? ( 'Page: ' . ucwords( str_replace( array( '_', '-' ), ' ', $value ) ) );
} elseif ( $param === 'page' || $param === 'post' ) {
$post_title = get_the_title( absint( $value ) );
$locations[] = ucfirst( $param ) . ': ' . ( $post_title ? $post_title : ( '#' . absint( $value ) ) );
} elseif ( $param === 'taxonomy' ) {
$locations[] = 'Taxonomy: ' . ucwords( str_replace( array( '_', '-' ), ' ', $value ) );
} elseif ( $param === 'user_role' ) {
$locations[] = 'Role: ' . ucfirst( $value );
} else {
$locations[] = ucwords( str_replace( array( '_', '-' ), ' ', $param ) ) . ': ' . ucwords( str_replace( array( '_', '-' ), ' ', $value ) );
}
}
}
}
if ( empty( $locations ) ) {
return '—';
}
$html = '';
foreach ( array_unique( $locations ) as $loc ) {
$html .= '' . esc_html( $loc ) . '';
}
return $html;
}
public function column_fields( $item ) {
$count = count( $item['fields'] );
$tag_class = $count > 0 ? 'mfe-tag-blue' : 'mfe-tag-gray';
return ''
. $count . ' field' . ( $count !== 1 ? 's' : '' ) . '';
}
public function prepare_items() {
$this->process_bulk_action();
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- WP List Table standard status filter parameter.
$status = isset( $_GET['status'] ) ? sanitize_key( wp_unslash( $_GET['status'] ) ) : 'all';
$data = array();
if ( $status === 'sync' ) {
$pending = MetaForge_JSONSyncManager::get_sync_status();
foreach ( $pending as $key => $item ) {
$config = $item['json_config'];
$config['index'] = $key;
$config['menu_order'] = 0;
$config['group_name'] = $config['group_name'] . ' (' . ( $item['type'] === 'new' ? 'New' : 'Update' ) . ')';
$config['is_sync_item'] = true;
$config['sync_type'] = $item['type'];
$data[] = $config;
}
} else {
$posts = get_posts(
array(
'post_type' => 'metaforge_group',
'posts_per_page' => -1,
'post_status' => 'publish',
'orderby' => 'menu_order',
'order' => 'ASC',
)
);
foreach ( $posts as $post ) {
$group = get_post_meta( $post->ID, '_metaforge_group_config', true );
if ( ! is_array( $group ) ) {
$group = array(
'group_name' => $post->post_title,
'fields' => array(),
'location_rules' => array(),
);
}
$group['index'] = $post->ID;
$group['menu_order'] = (int) $post->menu_order;
$group['group_name'] = ! empty( $group['group_name'] ) ? $group['group_name'] : $post->post_title;
$data[] = $group;
}
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- WP List Table search parameter.
$search = isset( $_REQUEST['s'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['s'] ) ) : '';
if ( $search ) {
$data = array_filter(
$data,
function ( $group ) use ( $search ) {
return stripos( $group['group_name'], $search ) !== false;
}
);
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- WP List Table sorting parameters.
$orderby = isset( $_GET['orderby'] ) ? sanitize_key( wp_unslash( $_GET['orderby'] ) ) : 'menu_order';
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- WP List Table sorting parameters.
$order = isset( $_GET['order'] ) ? sanitize_key( wp_unslash( $_GET['order'] ) ) : 'asc';
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- WP List Table sorting parameter check.
if ( ! isset( $_GET['orderby'] ) ) {
// Default: use menu_order (already sorted by get_posts above)
// Just keep the current order — no additional usort needed
} else {
usort(
$data,
function ( $a, $b ) use ( $orderby, $order ) {
if ( $orderby === 'fields' ) {
$valA = count( $a['fields'] );
$valB = count( $b['fields'] );
} else {
$valA = isset( $a[ $orderby ] ) ? $a[ $orderby ] : '';
$valB = isset( $b[ $orderby ] ) ? $b[ $orderby ] : '';
}
$result = strnatcasecmp( strval( $valA ), strval( $valB ) );
return ( $order === 'asc' ) ? $result : -$result;
}
);
}
$per_page = $this->get_items_per_page( 'metaforge_field_groups_per_page', 20 );
$current_page = $this->get_pagenum();
$total_items = count( $data );
$data = array_slice( $data, ( ( $current_page - 1 ) * $per_page ), $per_page );
$this->items = $data;
$this->_column_headers = array(
$this->get_columns(),
get_hidden_columns( $this->screen ),
$this->get_sortable_columns(),
'title',
);
$this->set_pagination_args(
array(
'total_items' => $total_items,
'per_page' => $per_page,
'total_pages' => ceil( $total_items / $per_page ),
)
);
}
public function no_items() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$status = isset( $_GET['status'] ) ? sanitize_key( wp_unslash( $_GET['status'] ) ) : 'all';
if ( $status === 'sync' ) {
esc_html_e( 'No field groups available to sync.', 'metaforge-engine' );
} else {
esc_html_e( 'No field groups found.', 'metaforge-engine' );
}
}
}
}