'Term', //singular name of the listed records 'plural' => 'Terms', //plural name of the listed records 'ajax' => true //does this table support ajax? ]); } /** * Flatten a hierarchical terms array into a flat array. * * @param array $terms Array of term objects. * @param int $max_depth Maximum depth to traverse (default 10). * @return array Flattened array of term objects. */ function taxopress_flatten_terms_tree($terms, $max_depth = 7) { $flat = []; $map = []; $children_map = []; $visited = []; // Build a map of terms by ID and their children foreach ($terms as $term) { $map[$term->term_id] = $term; $children_map[$term->parent][] = $term; } // Initialize stack with root-level terms (no valid parent) $stack = []; foreach ($terms as $term) { if ($term->parent === 0 || !isset($map[$term->parent])) { $stack[] = ['term' => $term, 'depth' => 0]; } } // Iterative depth-first traversal while (!empty($stack)) { $node = array_pop($stack); $term = $node['term']; $depth = $node['depth']; if ($depth > $max_depth) { continue; } if (isset($visited[$term->term_id])) { continue; // Prevent cycles } $visited[$term->term_id] = true; $flat[] = $term; // Use pre-indexed children map if (!empty($children_map[$term->term_id])) { foreach (array_reverse($children_map[$term->term_id]) as $child) { $stack[] = ['term' => $child, 'depth' => $depth + 1]; } } } return $flat; } /** * Arrange terms in hierarchical order with depth info for dash prefixing. */ function taxopress_arrange_terms_hierarchically($terms) { $terms_by_id = []; $children = []; foreach ($terms as $term) { $terms_by_id[$term->term_id] = $term; $children[$term->parent][] = $term->term_id; } $ordered = []; $add_term = function($term_id, $depth) use (&$add_term, &$terms_by_id, &$children, &$ordered) { $term = $terms_by_id[$term_id]; $term->taxopress_depth = $depth; $ordered[] = $term; if (!empty($children[$term_id])) { foreach ($children[$term_id] as $child_id) { $add_term($child_id, $depth + 1); } } }; // Start with root terms foreach ($terms as $term) { if ($term->parent == 0 || !isset($terms_by_id[$term->parent])) { $add_term($term->term_id, 0); } } return $ordered; } public function get_all_terms($count = false) { $taxonomies = array_keys(get_all_taxopress_taxonomies_request()); $taxonomy_settings = taxopress_get_all_edited_taxonomy_data(); $search = (!empty($_REQUEST['s'])) ? sanitize_text_field($_REQUEST['s']) : ''; $items_per_page = $this->get_items_per_page('st_terms_per_page', 20); $page = $this->get_pagenum(); $offset = ($page - 1) * $items_per_page; $selected_post_type = (!empty($_REQUEST['terms_filter_post_type'])) ? [sanitize_text_field($_REQUEST['terms_filter_post_type'])] : ''; $selected_taxonomy = (!empty($_REQUEST['terms_filter_taxonomy'])) ? sanitize_text_field($_REQUEST['terms_filter_taxonomy']) : ''; $allowed_orderby = ['name', 'slug', 'taxonomy', 'count', 'id']; $allowed_order = ['asc', 'desc']; $requested_orderby = !empty($_REQUEST['orderby']) ? sanitize_key($_REQUEST['orderby']) : ''; $requested_order = !empty($_REQUEST['order']) ? strtolower(sanitize_text_field($_REQUEST['order'])) : ''; if (!in_array($requested_orderby, $allowed_orderby, true)) { $requested_orderby = ''; } if (!in_array($requested_order, $allowed_order, true)) { $requested_order = ''; } $order_setting = isset($taxonomy_settings[$selected_taxonomy]['order']) ? strtolower($taxonomy_settings[$selected_taxonomy]['order']) : 'desc'; $orderby_setting = isset($taxonomy_settings[$selected_taxonomy]['orderby']) ? $taxonomy_settings[$selected_taxonomy]['orderby'] : 'ID'; $orderby_setting = strtolower($orderby_setting); if ($orderby_setting === 'id' || $orderby_setting === 'term_id') { $orderby_setting = 'id'; } if ($requested_orderby) { $orderby_setting = $requested_orderby; } if ($requested_order) { $order_setting = $requested_order; } // If viewing via taxopress_terms_taxonomy, override to show all terms in that taxonomy if (!empty($_REQUEST['taxopress_terms_taxonomy'])) { $selected_taxonomy = sanitize_text_field($_REQUEST['taxopress_terms_taxonomy']); $taxonomies = [$selected_taxonomy]; $show_all_terms_in_taxonomy = false; } else { $show_all_terms_in_taxonomy = false; if (!empty($selected_taxonomy)) { $taxonomies = [$selected_taxonomy]; } } // Check if any taxonomy uses manual ordering $manual_order = false; foreach ($taxonomies as $taxonomy) { $order_setting = isset($taxonomy_settings[$taxonomy]['order']) ? $taxonomy_settings[$taxonomy]['order'] : 'desc'; $orderby_setting = isset($taxonomy_settings[$selected_taxonomy]['orderby']) ? $taxonomy_settings[$selected_taxonomy]['orderby'] : 'ID'; if ($requested_orderby) { $orderby_setting = $requested_orderby; } if ($requested_order) { $order_setting = $requested_order; } if ($order_setting === 'taxopress_term_order') { $manual_order = true; break; } } // If any taxonomy uses manual order, use the original per-taxonomy logic if ($manual_order) { $terms = []; foreach ($taxonomies as $taxonomy) { $custom_order = get_option('taxopress_term_order_' . $taxonomy, []); $order_setting = isset($taxonomy_settings[$taxonomy]['order']) ? $taxonomy_settings[$taxonomy]['order'] : 'desc'; $orderby_setting = isset($taxonomy_settings[$taxonomy]['orderby']) ? $taxonomy_settings[$taxonomy]['orderby'] : 'ID'; $use_custom_order = ($order_setting === 'taxopress_term_order'); $terms_attr = [ 'taxonomy' => [$taxonomy], 'post_types' => $selected_post_type, 'hide_empty' => false, 'pad_counts' => true, 'update_term_meta_cache' => true, 'search' => $search, ]; if (!$use_custom_order) { $terms_attr['orderby'] = $orderby_setting; $terms_attr['order'] = $order_setting; } // Only paginate after merging all terms $taxonomy_terms = get_terms($terms_attr); if (empty($taxonomy_terms) || is_wp_error($taxonomy_terms)) { continue; } if ($use_custom_order) { // Manual custom ordering $terms_by_id = []; $new_terms = []; $ordered_terms = []; foreach ($taxonomy_terms as $term) { $terms_by_id[$term->term_id] = $term; } // Terms not in custom order foreach ($terms_by_id as $term_id => $term) { if (!in_array($term_id, $custom_order)) { $new_terms[] = $term; } } // Ordered terms foreach ($custom_order as $term_id) { if (isset($terms_by_id[$term_id])) { $ordered_terms[] = $terms_by_id[$term_id]; } } // Merge: new (unordered) terms first, then custom-ordered ones $terms = array_merge($terms, $new_terms, $ordered_terms); } else { if ($orderby_setting === 'random') { shuffle($taxonomy_terms); if ($order_setting === 'desc') { $taxonomy_terms = array_reverse($taxonomy_terms); } } $terms = array_merge($terms, $taxonomy_terms); } } // Paginate after merging, unless showing all terms in taxonomy if (!$count) { $terms = array_slice($terms, $offset, $items_per_page); } // HIERARCHY SUPPORT if (!empty($selected_taxonomy)) { $terms = $this->taxopress_arrange_terms_hierarchically($terms); } else { $terms = $this->taxopress_flatten_terms_tree($terms); } return $terms; } // If no manual ordering, use the efficient all-in-one get_terms $terms_attr = [ 'taxonomy' => $taxonomies, 'post_types' => $selected_post_type, 'orderby' => $orderby_setting, 'order' => $order_setting, 'search' => $search, 'hide_empty' => false, 'pad_counts' => true, 'update_term_meta_cache' => true, ]; if ($count || $show_all_terms_in_taxonomy) { $terms_attr['number'] = 0; } else { $terms_attr['offset'] = $offset; $terms_attr['number'] = $items_per_page; } $terms = get_terms($terms_attr); if (empty($terms) || is_wp_error($terms)) { return []; } if (!empty($requested_orderby) && $requested_orderby === 'taxonomy') { usort($terms, function ($a, $b) use ($order_setting) { $cmp = strcmp($a->taxonomy, $b->taxonomy); return ($order_setting === 'desc') ? -$cmp : $cmp; }); } if ($orderby_setting === 'random') { shuffle($terms); if ($order_setting === 'desc') { $terms = array_reverse($terms); } } // HIERARCHY SUPPORT if (!empty($selected_taxonomy)) { $terms = $this->taxopress_arrange_terms_hierarchically($terms); } else { $terms = $this->taxopress_flatten_terms_tree($terms); } return $terms; } /** * Retrieve st_Terms data from the database * * @param int $per_page * @param int $page_number * * @return mixed */ public function get_st_Terms() { return $this->get_all_terms(); } /** * Returns the count of records in the database. * * @return null|string */ public function record_count() { return count($this->get_all_terms(true)); } /** * Show single row item * * @param array $item */ public function single_row($item) { $class = ['st-terms-tr']; $id = 'term-' . $item->term_id . ''; echo sprintf('
'taxopress-terms-search-submit']); ?>
_column_headers = $this->get_column_info(); $this->process_bulk_action(); /** * First, lets decide how many records per page to show */ $per_page = $this->get_items_per_page('st_terms_per_page', 20); /** * Fetch the data */ $data = $this->get_st_Terms(); /** * Pagination. */ $current_page = $this->get_pagenum(); $total_items = $this->record_count(); /** * Now we can add the data to the items property, where it can be used by the rest of the class. */ $this->items = $data; /** * We also have to register our pagination options & calculations. */ $this->set_pagination_args([ 'total_items' => $total_items, //calculate the total number of items 'per_page' => $per_page, //determine how many items to show on a page 'total_pages' => ceil($total_items / $per_page) //calculate the total number of pages ]); } /** * Generates and display row actions links for the list table. * * @param object $item The item being acted upon. * @param string $column_name Current column name. * @param string $primary Primary column name. * * @return string The row actions HTML, or an empty string if the current column is the primary column. */ protected function handle_row_actions($item, $column_name, $primary) { $taxonomy = get_taxonomy($item->taxonomy); //Build row actions $actions = []; if (current_user_can('edit_term', $item->term_id)) { $actions['edit'] = sprintf( '%s', add_query_arg( [ 'taxonomy' => $item->taxonomy, 'tag_ID' => $item->term_id, 'post_type' => isset($taxonomy->object_type[0]) ? $taxonomy->object_type[0] : 'post', ], admin_url('term.php') ), esc_html__('Edit', 'simple-tags') ); } // Only add other actions if not viewing via taxopress_terms_taxonomy if (empty($_REQUEST['taxopress_terms_taxonomy'])) { if (current_user_can('edit_term', $item->term_id)) { $actions['inline hide-if-no-js'] = sprintf( '', /* translators: %s: Taxonomy term name. */ esc_attr(sprintf(esc_html__('Quick edit “%s” inline', 'simple-tags'), $item->name)), esc_html__('Quick Edit', 'simple-tags') ); } if (current_user_can('edit_term', $item->term_id)) { $actions['remove_posts'] = sprintf( '%s', add_query_arg( [ 'page' => 'st_terms', 'action' => 'taxopress-remove-from-posts', 'taxopress_terms' => esc_attr($item->term_id), '_wpnonce' => wp_create_nonce('terms-action-request-nonce') ], admin_url('admin.php') ), esc_html__('Remove From All Posts', 'simple-tags') ); } if (current_user_can('delete_term', $item->term_id)) { $actions['delete'] = sprintf( '%s', add_query_arg( [ 'page' => 'st_terms', 'action' => 'taxopress-delete-terms', 'taxopress_terms' => esc_attr($item->term_id), '_wpnonce' => wp_create_nonce('terms-action-request-nonce') ], admin_url('admin.php') ), esc_html__('Delete', 'simple-tags') ); } if (is_taxonomy_viewable($item->taxonomy)) { $actions['view'] = sprintf( '%s', get_term_link($item->term_id), esc_html__('View', 'simple-tags') ); } $actions['copy_term'] = sprintf( '%s', add_query_arg( [ 'page' => 'st_terms', 'action' => 'taxopress-copy-term', 'taxopress_terms' => esc_attr($item->term_id), '_wpnonce' => wp_create_nonce('terms-action-request-nonce') ], admin_url('admin.php') ), esc_html__('Copy', 'simple-tags') ); $actions = apply_filters('taxopress_terms_row_actions', $actions, $item); } return $column_name === $primary ? $this->row_actions($actions, false) : ''; } /** * Method for synonyms column * * @param array $item * * @return string */ protected function column_synonyms($item) { $term_synonyms = taxopress_get_term_synonyms($item->term_id); if (!empty($term_synonyms)) { return join(', ', $term_synonyms); } else { return '-'; } } /** * Method for linked_terms column * * @param array $item * * @return string */ protected function column_linked_terms($item) { $term_linked_terms = taxopress_get_linked_terms($item->term_id); if (!empty($term_linked_terms)) { $term_linked_term_names = []; foreach ($term_linked_terms as $term_linked_term) { $linked_term_data = taxopress_get_linked_term_data($term_linked_term, $item->term_id); $term_linked_term_names[] = $linked_term_data->term_name . ' ('. $linked_term_data->term_taxonomy .')'; } return join(', ', $term_linked_term_names); } else { return '-'; } } /** * Method for name column * * @param array $item * * @return string */ protected function column_name($item) { $taxonomy = get_taxonomy($item->taxonomy); // Add dashes for hierarchy $depth = isset($item->taxopress_depth) ? (int)$item->taxopress_depth : 0; $dash = $depth > 0 ? str_repeat('— ', $depth) : ''; $title = sprintf( '%2$s%3$s', add_query_arg( [ 'taxonomy' => $item->taxonomy, 'tag_ID' => $item->term_id, 'post_type' => isset($taxonomy->object_type[0]) ? $taxonomy->object_type[0] : 'post', ], admin_url('term.php') ), $dash, esc_html($item->name) ); $title .= ' '; //for inline edit $qe_data = get_term($item->term_id, $item->taxonomy, OBJECT, 'edit'); $title .= '