⭐ ' . esc_html__( 'Go Pro', 'webcasata-filterx' ) . '', 'manage_options', 'webcasata-filterx-pro', array( __CLASS__, 'page_pro' ) );
}
/* ------------------------------------------------------------------ */
/* Ajax: get taxonomies for a post type */
/* ------------------------------------------------------------------ */
/* ------------------------------------------------------------------ */
/* Shared: ACF-aware taxonomy lookup for a post type */
/* ------------------------------------------------------------------ */
/**
* Return all taxonomy objects associated with a post type,
* including those registered by ACF which are missed by
* get_object_taxonomies() alone.
*
* @param string $post_type Post type slug.
* @param array $selected Previously-saved taxonomy slugs (always included).
* @return array[] Each: [ 'name', 'label', 'checked' ]
*/
private static function get_taxonomies_for_post_type( $post_type, $selected = array() ) {
$skip = array( 'nav_menu', 'link_category', 'post_format', 'wp_theme', 'wp_template_part_area' );
// --- Build slug list ---
// 1. Native WP registration
$pt_taxonomies = (array) get_object_taxonomies( $post_type, 'names' );
// 2. Walk $wp_taxonomies for any registration that lists this post type
global $wp_taxonomies;
if ( is_array( $wp_taxonomies ) ) {
foreach ( $wp_taxonomies as $slug => $obj ) {
if ( isset( $obj->object_type ) && in_array( $post_type, (array) $obj->object_type, true ) ) {
$pt_taxonomies[] = $slug;
}
}
}
// 3. ACF taxonomy config (handles ACF-registered taxonomies)
if ( function_exists( 'acf_get_taxonomies' ) ) {
// ACF Pro 6.x
$acf_taxes = acf_get_taxonomies();
if ( is_array( $acf_taxes ) ) {
foreach ( $acf_taxes as $acf_tax ) {
$types = $acf_tax['object_type'] ?? ( $acf_tax['post_types'] ?? array() );
if ( in_array( $post_type, (array) $types, true ) ) {
$pt_taxonomies[] = sanitize_key( $acf_tax['taxonomy'] ?? $acf_tax['key'] ?? '' );
}
}
}
} else {
// ACF Free / older: read from acf-taxonomy CPT meta
$acf_posts = get_posts( array(
'post_type' => 'acf-taxonomy',
'posts_per_page' => -1,
'post_status' => 'publish',
'fields' => 'ids',
) );
foreach ( $acf_posts as $acf_id ) {
$config = get_post_meta( $acf_id, 'taxonomy', true );
$tax_slug = '';
$tax_types = array();
if ( is_array( $config ) ) {
$tax_slug = $config['taxonomy'] ?? ( $config['key'] ?? '' );
$tax_types = $config['object_type'] ?? ( $config['post_types'] ?? array() );
}
// Fallback: ACF stores the slug in the post name / title
if ( ! $tax_slug ) {
$acf_post = get_post( $acf_id );
$tax_slug = $acf_post ? $acf_post->post_name : '';
// And read object types from key-value post meta
$raw_types = get_post_meta( $acf_id, 'object_type', true );
if ( ! $raw_types ) {
$raw_types = get_post_meta( $acf_id, 'post_types', true );
}
if ( is_string( $raw_types ) ) {
$tax_types = explode( ',', $raw_types );
} elseif ( is_array( $raw_types ) ) {
$tax_types = $raw_types;
}
}
if ( $tax_slug && in_array( $post_type, array_map( 'trim', (array) $tax_types ), true ) ) {
$pt_taxonomies[] = sanitize_key( $tax_slug );
}
}
}
// Deduplicate and clean
$pt_taxonomies = array_unique( array_filter( $pt_taxonomies ) );
// Allow theme/plugin developers to modify the list
$pt_taxonomies = apply_filters( 'filterx_post_type_taxonomies', $pt_taxonomies, $post_type );
// --- Build output array ---
$all_taxonomies = get_taxonomies( array(), 'objects' );
$output = array();
foreach ( $all_taxonomies as $tax ) {
if ( in_array( $tax->name, $skip, true ) ) {
continue;
}
$belongs = in_array( $tax->name, $pt_taxonomies, true );
$was_saved = in_array( $tax->name, (array) $selected, true );
if ( ! $belongs && ! $was_saved ) {
continue;
}
$output[] = array(
'name' => $tax->name,
'label' => $tax->labels->name,
'checked' => $was_saved,
);
}
return $output;
}
public static function ajax_get_taxonomies() {
check_ajax_referer( 'filterx_admin_nonce', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( 'Permission denied', 403 );
}
$post_type = sanitize_key( $_POST['post_type'] ?? '' );
$selected = isset( $_POST['selected'] ) ? array_map( 'sanitize_key', (array) $_POST['selected'] ) : array();
$output = self::get_taxonomies_for_post_type( $post_type, $selected );
wp_send_json_success( $output );
}
/* ------------------------------------------------------------------ */
/* Dashboard */
/* ------------------------------------------------------------------ */
public static function page_dashboard() {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'You do not have permission to access this page.', 'webcasata-filterx' ) );
}
$items = FilterX_Post_Type::get_all_items();
$notice = '';
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only notice display
if ( isset( $_GET['filterx_saved'] ) ) { $notice = '
' . esc_html__( 'Search item saved.', 'webcasata-filterx' ) . '
'; }
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only notice display
if ( isset( $_GET['filterx_deleted'] ) ) { $notice = '' . esc_html__( 'Search item deleted.', 'webcasata-filterx' ) . '
'; }
?>
|
|
|
|
|
|
|
ID );
$shortcode = FilterX_Post_Type::get_shortcode( $item->ID );
$edit_url = admin_url( 'admin.php?page=filterx-new&filterx_id=' . $item->ID );
$del_nonce = wp_create_nonce( 'filterx_delete_item_' . $item->ID );
$del_url = admin_url( 'admin-post.php?action=filterx_delete_item&filterx_id=' . $item->ID . '&_wpnonce=' . $del_nonce );
?>
|
post_title ); ?>
|
|
' . esc_html( $tax ) . ' ';
endforeach;
else : echo '—';
endif; ?>
|
|
|
|
|
post_title : '';
$is_edit = $post_id > 0;
$post_types = get_post_types( array( 'public' => true ), 'objects' );
// For edit mode: pre-load taxonomies for the saved post type.
// Uses the same comprehensive ACF-aware lookup as ajax_get_taxonomies().
$existing_taxonomies = array();
if ( $is_edit && ! empty( $settings['post_type'] ) ) {
$existing_taxonomies = self::get_taxonomies_for_post_type(
$settings['post_type'],
$settings['taxonomies']
);
}
$pagination_options = array(
'numbers' => array( 'label' => __( 'Numbers', 'webcasata-filterx' ), 'pro' => false, 'desc' => __( 'Standard numbered page links', 'webcasata-filterx' ), 'icon' => '⋯' ),
'load_more' => array( 'label' => __( 'Load More', 'webcasata-filterx' ), 'pro' => false, 'desc' => __( 'Button to append the next batch of posts', 'webcasata-filterx' ), 'icon' => '↓' ),
'infinite' => array( 'label' => __( 'Infinite Scroll', 'webcasata-filterx' ),'pro' => true, 'desc' => __( 'Auto-load posts as user scrolls', 'webcasata-filterx' ), 'icon' => '∞' ),
'hybrid' => array( 'label' => __( 'Hybrid Scroll', 'webcasata-filterx' ), 'pro' => true, 'desc' => __( 'Infinite scroll + Load More combined', 'webcasata-filterx' ), 'icon' => '⇅' ),
);
$is_pro = apply_filters( 'filterx_is_pro', false );
$form_action = admin_url( 'admin-post.php' );
$nonce = wp_create_nonce( 'filterx_save_item' );
$shortcode = $is_edit ? FilterX_Post_Type::get_shortcode( $post_id ) : '';
?>
' . esc_html__( 'Settings saved.', 'webcasata-filterx' ) . '
';
}
?>
$t['overridden'] ) );
$theme_dir_exists = is_dir( $theme_dir_abs );
$debug_url = add_query_arg( 'filterx_debug_templates', '1', home_url( '/' ) );
?>
/webcasata-filterx/loop/card.php
✓
| |
| |
| ✓ Yes' ) : wp_kses_post( '✗ Not found' ); ?> |
| | |
|
⭐ Your theme' ) : wp_kses_post( 'Plugin default' ); ?> |
|
'Security check failed.' ), 403 );
}
$search_id = absint( $_POST['search_id'] ?? 0 );
if ( ! $search_id ) {
wp_send_json_error( array( 'message' => 'Invalid search ID.' ), 400 );
}
$settings = FilterX_Post_Type::get_settings( $search_id );
$search_query = sanitize_text_field( wp_unslash( $_POST['search_query'] ?? '' ) );
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized,WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- sanitized key-by-key below
$active_tax_raw = isset( $_POST['tax_query'] ) ? (array) wp_unslash( $_POST['tax_query'] ) : array();
$active_filters = array(); // taxonomy => term_slug
foreach ( $active_tax_raw as $tax => $slug ) {
$tax = sanitize_key( $tax );
$slug = sanitize_text_field( $slug );
if ( $tax && $slug ) {
$active_filters[ $tax ] = $slug;
}
}
$counts = array(); // taxonomy => [ term_slug => count ]
foreach ( $settings['taxonomies'] as $taxonomy ) {
$taxonomy = sanitize_key( $taxonomy );
$terms = get_terms( array( 'taxonomy' => $taxonomy, 'hide_empty' => true ) );
if ( is_wp_error( $terms ) || empty( $terms ) ) {
$counts[ $taxonomy ] = array();
continue;
}
$term_counts = array();
foreach ( $terms as $term ) {
// Build query args with all OTHER filters active, then check count for this term
$args = array(
'post_type' => $settings['post_type'],
'post_status' => 'publish',
'posts_per_page' => 1,
'no_found_rows' => false,
'fields' => 'ids',
);
if ( ! empty( $search_query ) ) {
$args['s'] = $search_query;
}
// Build tax_query: all active filters EXCEPT this taxonomy + this term
$tax_query_parts = array( 'relation' => 'AND' );
foreach ( $active_filters as $active_tax => $active_slug ) {
if ( $active_tax === $taxonomy ) {
continue; // skip self — we're testing each term of THIS taxonomy
}
$tax_query_parts[] = array(
'taxonomy' => $active_tax,
'field' => 'slug',
'terms' => array( $active_slug ),
'operator' => 'IN',
);
}
// Add this specific term
$tax_query_parts[] = array(
'taxonomy' => $taxonomy,
'field' => 'slug',
'terms' => array( $term->slug ),
'operator' => 'IN',
);
if ( count( $tax_query_parts ) > 1 ) {
$args['tax_query'] = $tax_query_parts;
}
$args = apply_filters( 'filterx_query_args', $args, $settings );
$q = new WP_Query( $args );
$term_counts[ $term->slug ] = (int) $q->found_posts;
}
$counts[ $taxonomy ] = $term_counts;
}
wp_send_json_success( array( 'counts' => $counts ) );
}
public static function handle_save_settings() {
if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Permission denied.', 'webcasata-filterx' ) ); }
if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'filterx_save_settings' ) ) { wp_die( esc_html__( 'Security check failed.', 'webcasata-filterx' ) ); }
$opts = array(
'default_columns' => min( 6, max( 1, absint( $_POST['default_columns'] ?? 3 ) ) ),
'default_posts_per_page' => min( 100, max( 1, absint( $_POST['default_posts_per_page'] ?? 6 ) ) ),
'card_css_class' => sanitize_html_class( wp_unslash( $_POST['card_css_class'] ?? '' ) ),
'date_format' => sanitize_text_field( wp_unslash( $_POST['date_format'] ?? '' ) ),
'load_acf_style' => ! empty( $_POST['load_acf_style'] ),
'show_sort_order' => ! empty( $_POST['show_sort_order'] ),
'show_filter_tabs' => ! empty( $_POST['show_filter_tabs'] ),
'show_term_count' => ! empty( $_POST['show_term_count'] ),
'smart_filters' => ! empty( $_POST['smart_filters'] ),
);
update_option( 'webcasata_filterx_global_settings', $opts );
wp_safe_redirect( admin_url( 'admin.php?page=filterx-settings&filterx_settings_saved=1' ) );
exit;
}
}