*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'Sp_Smart_Post_Blocks_Query' ) ) {
/**
* Sp_Smart_Post_Blocks_Query class.
*/
class Sp_Smart_Post_Blocks_Query {
/**
* This plugin's instance.
*
* @var Query
*/
private static $instance;
/**
* Main Query Instance.
*
* Insures that only one instance of Blocks exists in memory at any one
* time. Also prevents needing to define globals all over the place.
*
* @static
* @return object|Query The one true Blocks
*/
public static function instance() {
if ( ! isset( self::$instance ) ) {
self::$instance = new Sp_Smart_Post_Blocks_Query();
self::$instance->init();
}
return self::$instance;
}
/**
* Initialize function
*
* @return void
*/
public function init() {
add_action( 'wp_ajax_sp_smart_post_block_post_query', array( $this, 'sp_smart_post_block_post_query' ) );
add_action( 'wp_ajax_nopriv_sp_smart_post_block_post_query', array( $this, 'sp_smart_post_block_post_query' ) );
// query functions for get all post.
if ( is_admin() ) {
add_action( 'wp_ajax_sp_smart_post_block_all_post_query', array( $this, 'sp_smart_post_block_all_post_query' ) );
add_action( 'wp_ajax_nopriv_sp_smart_post_block_all_post_query', array( $this, 'sp_smart_post_block_all_post_query' ) );
}
// metadata query.
add_action( 'wp_ajax_sp_smart_post_block_meta_data_query', array( $this, 'sp_smart_post_block_meta_data_query' ) );
add_action( 'wp_ajax_nopriv_sp_smart_post_block_meta_data_query', array( $this, 'sp_smart_post_block_meta_data_query' ) );
}
/**
* Handles the frontend post query for the Smart Post Block.
*
* @param array $query_data The query data for fetching posts.
* @param string $block_id Optional. The block ID.
* @return array An array containing post data and total pages.
*/
public static function post_query_frontend( $query_data, $block_id = '' ) {
$config = self::prepare_query_config( $query_data, $block_id );
self::setup_image_filters( $config['image_srcset'] );
$post_query = \SmartPostShow\Blocks\PostQueryHandler::query( $query_data, 'normal', $block_id );
$the_query = $post_query['the_query'];
$total_pages = $post_query['total_pages'] ?? 1;
$all_post_ids = $post_query['post_ids'] ?? array();
$post_data = self::process_query_posts( $the_query, $config );
self::cleanup_image_filters( $config['image_srcset'] );
return array( $post_data, $total_pages, $all_post_ids );
}
/**
* Prepare configuration data from query parameters.
*
* @param array $query_data The query data.
* @param string $block_id The block ID.
* @return array Configuration array.
*/
private static function prepare_query_config( $query_data, $block_id = '' ) {
$config = array(
'post_type' => $query_data['postType'] ?? 'post',
'date_format_type' => $query_data['metaDateFormat'] ?? 'default',
'custom_date_format' => $query_data['metaDateCustomDateFormat'] ?? '',
'image_size' => $query_data['imageSize'] ?? '',
'unique_id' => $query_data['uniqueId'] ?? '',
'enable_seo_description' => $query_data['seoMetaShow'] ?? false,
'active_seo_plugin' => $query_data['activeSeoPlugin'] ?? '',
'exclude_post_without_image' => $query_data['excludePostWithoutImagePosts'] ?? false,
'image_srcset' => ! empty( $query_data['imageSrcset'] ) ? (bool) $query_data['imageSrcset'] : false,
'image_lazy_enable' => $query_data['imageLazyLoad'] ?? false,
'taxonomy_type' => $query_data['catTabCategoryType'] ?? '',
);
$config['date_format'] = self::get_date_format( $config['date_format_type'], $config['custom_date_format'] );
$config['image_lazy_load'] = $config['image_lazy_enable'] ? 'lazy' : 'eager';
$config['multiple_post_type'] = self::prepare_multiple_post_types( $config['post_type'], $query_data );
// Handle pagination.
if ( $query_data['paginationEnable'] ?? false ) {
$paged_var = 'paged_' . $block_id;
$query_data['currentPage'] = isset( $_GET[ $paged_var ] ) ? intval( $_GET[ $paged_var ] ) : $query_data['currentPage'] ?? 1;
}
return $config;
}
/**
* Get the appropriate date format.
*
* @param string $date_format_type The date format type.
* @param string $custom_format Custom date format.
* @return string The date format.
*/
private static function get_date_format( $date_format_type, $custom_format ) {
switch ( $date_format_type ) {
case 'F j, Y':
case 'M j, Y':
$date_format = $date_format_type;
break;
case 'custom':
$date_format = $custom_format;
break;
default:
$date_format = get_option( 'date_format' );
break;
}
return $date_format;
}
/**
* Prepare multiple post types array.
*
* @param string $current_post_type Current post type.
* @param array $query_data Query data.
* @return array Multiple post types.
*/
private static function prepare_multiple_post_types( $current_post_type, $query_data ) {
if ( 'multiple_post_type' !== $current_post_type || empty( $query_data['multiplePostType'] ) ) {
return array( 'post' );
}
return array_map(
function ( $multi_type ) {
return is_array( $multi_type ) && isset( $multi_type['value'] ) ? $multi_type['value'] : $multi_type->value;
},
$query_data['multiplePostType']
);
}
/**
* Setup image-related filters.
*
* @param bool $image_srcset Whether srcset is enabled.
*/
private static function setup_image_filters( $image_srcset ) {
if ( ! $image_srcset ) {
add_filter( 'wp_get_attachment_image_attributes', array( self::class, 'remove_image_srcset' ), 10, 3 );
}
}
/**
* Cleanup image-related filters.
*
* @param bool $image_srcset Whether srcset is enabled.
*/
private static function cleanup_image_filters( $image_srcset ) {
if ( ! $image_srcset ) {
remove_filter( 'wp_get_attachment_image_attributes', array( self::class, 'remove_image_srcset' ), 10, 3 );
}
}
/**
* Process posts from the query.
*
* @param WP_Query $the_query The WordPress query object.
* @param array $config Configuration array.
* @return array Post data array.
*/
private static function process_query_posts( $the_query, $config ) {
$post_data = array();
if ( ! $the_query->have_posts() ) {
return $post_data;
}
while ( $the_query->have_posts() ) {
$the_query->the_post();
$single_post_data = self::prepare_single_post_data( $config );
$post_data[] = $single_post_data;
}
wp_reset_postdata();
return $post_data;
}
/**
* Prepare data for a single post.
*
* @param array $config Configuration array.
* @return array Single post data.
*/
private static function prepare_single_post_data( $config ) {
$post_id = get_the_ID();
$image_id = get_post_thumbnail_id();
$author_meta_id = get_the_author_meta( 'ID' );
$current_date = get_the_date( 'c' );
$post_data = self::get_basic_post_data( $post_id, $image_id, $author_meta_id, $current_date, $config );
// Add WooCommerce product data if applicable.
if ( 'product' === $config['post_type'] && function_exists( 'wc_get_product' ) ) {
$post_data = array_merge( $post_data, self::get_product_data( $post_id ) );
}
return $post_data;
}
/**
* Get basic post data.
*
* @param int $post_id Post ID.
* @param int $image_id Image ID.
* @param int $author_meta_id Author ID.
* @param string $current_date Current date.
* @param array $config Configuration array.
* @return array Basic post data.
*/
private static function get_basic_post_data( $post_id, $image_id, $author_meta_id, $current_date, $config ) {
$archive_year = get_the_time( 'Y' );
$archive_month = get_the_time( 'm' );
$archive_day = get_the_time( 'd' );
$taxonomy_type = $config['taxonomy_type'] ?? '';
return array(
'post_id' => $post_id,
'title' => get_the_title(),
'link' => get_the_permalink(),
'content' => strip_shortcodes( get_the_content() ),
'excerpt' => get_the_excerpt(),
'post_thumbnail' => get_the_post_thumbnail(),
'post_thumbnail_url' => get_the_post_thumbnail_url( $post_id, $config['image_size'] ?? 'thumbnail' ),
'image_size' => $config['image_size'],
'attachment_srcset' => wp_get_attachment_image_srcset( $image_id ),
'attachment_metadata' => wp_get_attachment_metadata( $image_id ),
'attachment_url' => wp_get_attachment_url( $image_id ),
'post_thumbnail_id' => $image_id,
'author' => get_the_author(),
'author_id' => $author_meta_id,
'author_avatar_url' => get_avatar_url( $author_meta_id ),
'author_url' => get_author_posts_url( $author_meta_id ),
'category_list' => get_the_category_list( '' ),
'tag_list' => get_the_tag_list( '' ),
'post_list' => get_post_format( '' ),
'category' => get_the_category(),
'date' => $current_date,
'date_archive_url' => get_day_link( $archive_year, $archive_month, $archive_day ),
'comment_count' => get_comment_count( $post_id ),
'view_count' => get_post_meta( $post_id, '_post_views_count', true ),
'like_count' => get_post_meta( $post_id, '_post_like_count', true ),
'image_title' => get_the_title( $image_id ),
'image_alt' => get_post_meta( $image_id, '_wp_attachment_image_alt', true ),
'post_date' => self::format_post_dates( $current_date, $config['date_format'] ),
'badges_list' => get_the_terms( $post_id, 'sp_smart_badges' ),
'all_term_list' => self::get_all_terms( $post_id, $taxonomy_type ),
);
}
/**
* Format post dates in various formats.
*
* @param string $current_date Current date.
* @param string $date_format Date format.
* @return array Formatted dates.
*/
private static function format_post_dates( $current_date, $date_format ) {
return array(
'default' => date_i18n( $date_format, strtotime( $current_date ) ),
'day' => date_i18n( 'j', strtotime( $current_date ) ),
'month' => date_i18n( 'F', strtotime( $current_date ) ),
'year' => date_i18n( 'Y', strtotime( $current_date ) ),
);
}
/**
* Get all terms of a single post.
*
* @param int $post_id Post ID..
* @param string $taxonomy_type Taxonomy Type.
* @return string Terms markup.
*/
private static function get_all_terms( $post_id, $taxonomy_type ) {
if ( empty( $taxonomy_type ) ) {
return '';
}
$all_taxonomies = '';
$terms = get_the_terms( $post_id, $taxonomy_type );
if ( ! empty( $terms ) && ! is_wp_error( $terms ) ) {
$all_taxonomies .= '
';
foreach ( $terms as $term ) {
if ( empty( $term ) || is_wp_error( get_term_link( $term ) ) ) {
continue; }
$all_taxonomies .= '- ' . esc_html( $term->name ) . '
';
}
$all_taxonomies .= '
';
}
return $all_taxonomies;
}
/**
* Get WooCommerce product data.
*
* @param int $post_id Post ID.
* @return array Product data.
*/
private static function get_product_data( $post_id ) {
$product = wc_get_product( $post_id );
if ( ! $product ) {
return array();
}
return array(
'product_price' => $product->get_price_html(),
'add_to_cart' => do_shortcode( '[add_to_cart id="' . $post_id . '" show_price="false" style="none"]' ),
'average_rating' => $product->get_average_rating(),
'review_count' => $product->get_review_count(),
'on_sale' => $product->is_on_sale(),
);
}
/**
* Ajax query function - refactored version.
*
* @return void
*/
public function sp_smart_post_block_post_query() {
$nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
if ( ! wp_verify_nonce( $nonce, 'sp_smart_post_block_nonce' ) ) {
return;
}
$query_data = isset( $_POST['queryData'] ) ? sanitize_text_field( wp_unslash( $_POST['queryData'] ) ) : '';
$query_data = (array) json_decode( $query_data );
$config = self::prepare_ajax_config( $query_data );
$post_query = \SmartPostShow\Blocks\PostQueryHandler::query( $query_data );
$the_query = $post_query['the_query'];
$post_data = self::process_query_posts( $the_query, $config );
$post_count = self::calculate_post_count( $config, $post_query );
wp_send_json(
array(
'posts' => $post_data,
'posts_status' => count( $post_data ) > 0,
'post_count' => $post_count,
)
);
}
/**
* Prepare configuration for AJAX request.
*
* @param array $query_data Query data.
* @return array Configuration array.
*/
private static function prepare_ajax_config( $query_data ) {
$config = array(
'post_type' => $query_data['postType'] ?? 'post',
'date_format_type' => $query_data['metaDateFormat'] ?? 'default',
'image_size' => $query_data['imageSize'] ?? '',
'custom_date_format' => $query_data['metaDateCustomDateFormat'] ?? '',
'enable_seo_description' => $query_data['seoMetaShow'] ?? false,
'active_seo_plugin' => $query_data['activeSeoPlugin'] ?? '',
'exclude_post_without_image' => $query_data['excludePostWithoutImagePosts'] ?? false,
'taxonomy_type' => $query_data['catTabCategoryType'] ?? '',
);
$config['date_format'] = self::get_date_format( $config['date_format_type'], $config['custom_date_format'] );
$config['multiple_post_type'] = self::prepare_ajax_multiple_post_types( $config['post_type'], $query_data );
return $config;
}
/**
* Prepare multiple post types for AJAX request.
*
* @param string $current_post_type Current post type.
* @param array $query_data Query data.
* @return array Multiple post types.
*/
private static function prepare_ajax_multiple_post_types( $current_post_type, $query_data ) {
if ( 'multiple_post_type' !== $current_post_type || empty( $query_data['multiplePostType'] ) ) {
return array( 'post' );
}
return array_map(
function ( $multi_type ) {
return $multi_type->value;
},
$query_data['multiplePostType']
);
}
/**
* Calculate total post count.
*
* @param array $config Configuration array.
* @param array $post_query Post query results.
* @return int Post count.
*/
private static function calculate_post_count( $config, $post_query ) {
if ( isset( $post_query['post_count'] ) && $post_query['post_count'] ) {
return $post_query['post_count'];
}
if ( 'multiple_post_type' === $config['post_type'] && ! empty( $config['multiple_post_type'] ) ) {
$post_count = 0;
foreach ( $config['multiple_post_type'] as $post_type ) {
$counts = wp_count_posts( $post_type );
$post_count += isset( $counts->publish ) ? (int) $counts->publish : 0;
}
return $post_count;
}
$counts = wp_count_posts( $config['post_type'] );
return isset( $counts->publish ) ? (int) $counts->publish : 0;
}
/**
* Method sp_smart_post_block_all_post_query
*
* @return void
*/
public function sp_smart_post_block_all_post_query() {
$nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
if ( ! wp_verify_nonce( $nonce, 'sp_smart_post_block_nonce' ) ) {
return;
}
$query_data = isset( $_POST['postQueryData'] ) ? sanitize_text_field( wp_unslash( $_POST['postQueryData'] ) ) : '';
$query_data = (array) json_decode( $query_data );
// queries.
$post_type = isset( $query_data['postType'] ) ? $query_data['postType'] : 'post';
$search_text = isset( $query_data['liveSearchText'] ) ? $query_data['liveSearchText'] : '';
$multiple_post_type = ( 'multiple_post_type' === $post_type && isset( $query_data['multiplePostType'] ) ) ? $query_data['multiplePostType'] : array();
$multiple_post_type = ( 'multiple_post_type' === $post_type && ! empty( $multiple_post_type ) ) ? array_map(
function ( $multi_type ) {
return $multi_type->value;
},
$multiple_post_type
) : array( 'post' );
$post_query_args = \SmartPostShow\Blocks\PostQueryHandler::query( $query_data, 'args' )['args'];
$post_query_args['fields'] = 'ids';
$post_query_args['posts_per_page'] = 99999;
$post_lists = get_posts( $post_query_args );
$the_search_query = new \WP_Query(
array(
'post_type' => ( 'multiple_post_type' === $post_type && ! empty( $multiple_post_type ) ) ? $multiple_post_type : $post_type,
'posts_per_page' => 20,
's' => $search_text,
'post__in' => $post_lists,
)
);
wp_send_json(
array(
'posts' => count( $post_lists ) > 0 ? $the_search_query->posts : array(),
)
);
}
/**
* Method sp_smart_post_block_meta_data_query
*
* @return void
*/
public function sp_smart_post_block_meta_data_query() {
$nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
if ( ! wp_verify_nonce( $nonce, 'sp_smart_post_block_nonce' ) ) {
return;
}
$query_data = isset( $_POST['metaQueryData'] ) ? sanitize_text_field( wp_unslash( $_POST['metaQueryData'] ) ) : '';
$query_data = (array) json_decode( $query_data );
// query data.
$post_type = isset( $query_data['postType'] ) ? $query_data['postType'] : 'post';
$ajax_live_filter = isset( $query_data['ajaxLiveFilter'] ) ? (array) $query_data['ajaxLiveFilter'] : array();
$current_page = isset( $query_data['currentPage'] ) ? $query_data['currentPage'] : 1;
$edit_site = isset( $query_data['editSite'] ) ? $query_data['editSite'] : '';
$block_name = isset( $query_data['blockName'] ) ? $query_data['blockName'] : 'post-carousel';
$keyword_search = isset( $query_data['keywordSearch'] ) ? $query_data['keywordSearch'] : '';
$taxonomies_live_filter = isset( $query_data['taxonomiesLiveFilter'] ) ? $query_data['taxonomiesLiveFilter'] : array();
$author_live_filter = isset( $query_data['authorLiveFilter'] ) ? $query_data['authorLiveFilter'] : array();
$multiple_post_type = ( 'multiple_post_type' === $post_type && isset( $query_data['multiplePostType'] ) ) ? $query_data
['multiplePostType'] : array();
$multiple_post_type = ( 'multiple_post_type' === $post_type && ! empty( $multiple_post_type ) ) ? array_map(
function ( $multi_type ) {
return $multi_type->value;
},
$multiple_post_type
) : array( 'post' );
$multiple_post_type = ( 'multiple_post_type' === $post_type && ! empty( $multiple_post_type ) ) ? $multiple_post_type : $post_type;
if ( ! empty( $taxonomies_live_filter ) || ! empty( $author_live_filter ) ) {
$post_query_args = self::query( $query_data, 'args' );
$post_limit = $post_query_args['post_limit'];
$post_in = $post_query_args['include_posts'];
$post_query_args = $post_query_args['args'];
$post_query_args['posts_per_page'] = $post_limit;
$post_query_args['fields'] = 'ids';
if ( ! empty( $keyword_search ) ) {
$post_query_args['s'] = $keyword_search;
}
$post_query_args['offset'] = 0;
$term_query_args = $post_query_args;
$author_query_args = $post_query_args;
$post_lists = get_posts( $post_query_args );
$all_post_ids = $post_in ? $post_in : $post_lists;
$ajax_live_filter_keys = array();
foreach ( $ajax_live_filter as $key => $value ) {
switch ( $value->type ) {
case 'taxonomy':
if ( 'all' !== $value->id ) {
$ajax_live_filter_keys[] = $value->taxonomy_type;
}
break;
case 'author':
if ( 'all' !== $value->id ) {
$ajax_live_filter_keys[] = $key;
}
break;
}
}
}
// taxonomy.
$all_taxonomies = get_object_taxonomies( $multiple_post_type, 'objects' );
$taxonomy_list = array();
foreach ( $all_taxonomies as $taxonomy ) {
$terms = get_terms(
array(
'taxonomy' => $taxonomy->name,
'hide_empty' => false,
)
);
// Prepare the list of terms for this taxonomy.
$term_ajax_filter = array();
$term_items = array();
$index = 0;
foreach ( $terms as $term ) {
$get_term_posts_ids = array(
array(
'taxonomy' => $term->taxonomy,
'field' => 'term_id',
'terms' => array( $term->term_id ),
),
);
if ( ! empty( $taxonomies_live_filter ) ) {
$term_query_args['tax_query'] = $get_term_posts_ids;
$term_query_args['posts_per_page'] = 9999;
$term_ids = get_posts( $term_query_args );
$term_ids = array_intersect( $all_post_ids, $term_ids );
if ( ( ! empty( $ajax_live_filter_keys ) && $ajax_live_filter_keys[0] !== $term->taxonomy ) || count( $ajax_live_filter_keys ) > 1 ) {
$term_ids = array_intersect( $post_lists, $term_ids );
}
$child_terms = get_term_children( $term->term_id, $taxonomy->name );
$total_post_count = count( $term_ids );
if ( is_wp_error( $child_terms ) && count( $child_terms ) > 0 ) {
foreach ( $child_terms as $child_term_id ) {
$child_term = get_term( $child_term_id, $taxonomy->name );
$total_post_count += $child_term->count;
}
}
}
$term_args = array(
'id' => $index,
'label' => $term->name,
'slug' => $term->slug,
'value' => $term->term_id,
'type' => 'taxonomy',
'taxonomy_type' => $term->taxonomy,
);
$term_items[] = $term_args;
if ( ! empty( $taxonomies_live_filter ) && $total_post_count ) {
$term_args['post_count'] = $total_post_count;
$term_ajax_filter[] = $term_args;
}
++$index;
}
$taxonomy_list[] = array(
'label' => $taxonomy->label,
'name' => $taxonomy->name,
'terms' => $term_ajax_filter,
'terms_items' => $term_items,
);
}
// authors details.
$authors = get_users( array( 'role__in' => array( 'author', 'administrator', 'editor' ) ) );
$filtered_authors = array();
$author_list = array();
$author_post_ids = array();
foreach ( $authors as $author ) {
if ( ! empty( $author_live_filter ) && ! empty( $author_query_args ) ) {
unset( $author_query_args['author__not_in'] );
$author_query_args['author__in'] = array( $author->ID );
$author_query_args['fields'] = 'ids';
$author_post_ids = get_posts( $author_query_args );
$author_post_ids = array_intersect( $all_post_ids, $author_post_ids );
}
$author_args = array(
'id' => $author->ID,
'role' => $author->roles,
'type' => 'author',
'value' => $author->ID,
'label' => $author->display_name,
);
$author_list[] = $author_args;
if ( ! empty( $author_live_filter ) && count( $author_post_ids ) > 0 ) {
$author_args['post_count'] = count( $author_post_ids );
$filtered_authors[] = $author_args;
}
}
// all post type list.
$all_post_type_list = get_post_types(
array(
'public' => true,
)
);
// query all meta fields.
$meta_field_list = array();
if ( 'editSite' === $edit_site ) {
global $wpdb;
$keys = $wpdb->get_col(
"SELECT meta_key
FROM $wpdb->postmeta
GROUP BY meta_key
ORDER BY meta_key"
);
if ( $keys ) {
natcasesort( $keys );
}
// Remove empty custom field.
$keys = array_filter( $keys );
foreach ( $keys as $key ) {
/**
* Don't hide protected meta fields, to able to select data of The Events Calendar...
*
* @since 2.0.0
* if ( is_protected_meta( $key, 'post' ) ) {
* continue;
* }
*/
$meta_field_list[ esc_attr( $key ) ] = esc_html( $key );
}
}
$post_count = 0;
if ( 'multiple_post_type' === $post_type && ! is_string( $multiple_post_type ) ) {
foreach ( $multiple_post_type as $value ) {
$post_count += isset( wp_count_posts( $value )->publish ) ? (int) wp_count_posts( $value )->publish : 0;
}
} else {
$post_count = isset( wp_count_posts( $post_type )->publish ) ? (int) wp_count_posts( $post_type )->publish : 0;
}
wp_send_json(
array(
'taxonomies' => $taxonomy_list,
'authors' => $filtered_authors,
'author_list' => $author_list,
'post_count' => $post_count,
'image_sizes' => get_intermediate_image_sizes(),
'all_post_type_list' => $all_post_type_list,
'all_meta_field_list' => $meta_field_list,
)
);
}
/**
* Remove srcset attribute from image attributes.
*
* @param array $attr Image attributes.
* @param WP_Post $attachment Attachment post object.
* @param string $size Image size.
* @return array Modified image attributes.
*/
public static function remove_image_srcset( $attr, $attachment, $size ) {
unset( $attr['srcset'] );
return $attr;
}
}
}