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( '
%s %s
', $title, $this->row_actions( $actions ) ); } $plain_title = ! empty( $item['group_name'] ) ? $item['group_name'] : '(no title)'; $edit_url = '?page=metaforge&action=edit&index=' . $item['index']; $duplicate_url = wp_nonce_url( admin_url( 'admin.php?page=metaforge&action=duplicate&index=' . $item['index'] ), 'metaforge_duplicate_group' ); $delete_url = wp_nonce_url( admin_url( 'admin.php?page=metaforge&action=delete&index=' . $item['index'] ), 'metaforge_delete_group_get' ); $actions = array( 'edit' => 'Edit', 'duplicate' => 'Duplicate', 'delete' => 'Delete', ); return sprintf( '
%s
%s %s
', $drag_handle, esc_url( $edit_url ), $title, $this->row_actions( $actions ) ); } public function single_row( $item ) { echo ''; $this->single_row_columns( $item ); echo ''; } public function column_key( $item ) { $key = ! empty( $item['group_key'] ) ? $item['group_key'] : ( ! empty( $item['config']['group_key'] ) ? $item['config']['group_key'] : ( 'group_' . substr( md5( $item['group_name'] . ( $item['index'] ?? '' ) ), 0, 12 ) ) ); return '' . 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' ); } } } }