*/ class WPUPG_Grid { /** * WP_Post object associated with this grid post type. * * @since 3.0.0 * @access private * @var object $post WP_Post object of this grid post type. */ private $post; /** * Cached IDs to display in this grid. * * @since 3.0.0 * @access private * @var array $ids Grid item IDs. */ private $ids = false; /** * Cached total IDs to display in this grid. * * @since 3.3.0 * @access private * @var array $total_ids Total number of grid ids. */ private $total_ids = false; /** * Cached terms to display in this grid. * * @since 3.0.0 * @access private * @var array $ids Terms for this grid. */ private $terms = false; /** * Metadata associated with this grid post type. * * @since 3.0.0 * @access private * @var array $meta Grid metadata. */ private $meta = false; /** * Get new grid object from associated post. * * @since 3.0.0 * @param mixed $post Meta or WP_Post object for this grid post type. */ public function __construct( $post_or_meta ) { $post = is_object( $post_or_meta ) && $post_or_meta instanceof WP_Post ? $post_or_meta : false; $this->post = $post; // Not a WP_Post object, so just manually set the meta values. if ( ! $post ) { $this->meta = $post_or_meta; } } /** * Get grid data. * * @since 3.0.0 */ public function get_data() { $grid = array(); // Technical Fields. $grid['id'] = $this->id(); $grid['version'] = $this->version(); // Grid General. $grid['name'] = $this->name(); $grid['slug'] = $this->slug(); // Grid Data Source. $grid['type'] = $this->type(); $grid['post_types'] = $this->post_types(); $grid['post_status'] = $this->post_status(); $grid['post_status_require_permission'] = $this->post_status_require_permission(); $grid['taxonomies'] = $this->taxonomies(); $grid['password_protected'] = $this->password_protected(); $grid['language'] = $this->language(); $grid['order_by'] = $this->order_by(); $grid['order'] = $this->order(); $grid['order_custom_key'] = $this->order_custom_key(); $grid['order_custom_key_numeric'] = $this->order_custom_key_numeric(); $grid['terms_order_by'] = $this->terms_order_by(); $grid['terms_order'] = $this->terms_order(); // Grid Limit Items. $grid['limit_posts_offset'] = $this->limit_posts_offset(); $grid['limit_posts_number'] = $this->limit_posts_number(); $grid['images_only'] = $this->images_only(); $grid['terms_images_only'] = $this->terms_images_only(); $grid['terms_hide_empty'] = $this->terms_hide_empty(); $grid['limit_terms'] = $this->limit_terms(); $grid['limit_terms_terms'] = $this->limit_terms_terms(); $grid['limit_terms_type'] = $this->limit_terms_type(); $grid['limit_posts'] = $this->limit_posts(); $grid['limit_rules'] = $this->limit_rules(); // Grid Filters. $grid['filters'] = $this->filters(); $grid['filters_enabled'] = $this->filters_enabled(); $grid['filters_style'] = $this->filters_style(); $grid['filters_relation'] = $this->filters_relation(); $grid['filters_no_selection'] = $this->filters_no_selection(); $grid['responsive_toggle_style'] = $this->responsive_toggle_style(); $grid['responsive_toggle_style_closed'] = $this->responsive_toggle_style_closed(); $grid['responsive_toggle_style_open'] = $this->responsive_toggle_style_open(); // Grid Layout. $grid['layout_mode'] = $this->layout_mode(); $grid['centered'] = $this->centered(); $grid['rtl_mode'] = $this->rtl_mode(); $grid['layout_desktop_sizing'] = $this->layout_desktop_sizing(); $grid['layout_desktop_sizing_fixed'] = $this->layout_desktop_sizing_fixed(); $grid['layout_desktop_sizing_columns'] = $this->layout_desktop_sizing_columns(); $grid['layout_desktop_sizing_margin'] = $this->layout_desktop_sizing_margin(); $grid['layout_tablet_different'] = $this->layout_tablet_different(); $grid['layout_tablet_sizing'] = $this->layout_tablet_sizing(); $grid['layout_tablet_sizing_fixed'] = $this->layout_tablet_sizing_fixed(); $grid['layout_tablet_sizing_columns'] = $this->layout_tablet_sizing_columns(); $grid['layout_tablet_sizing_margin'] = $this->layout_tablet_sizing_margin(); $grid['layout_mobile_different'] = $this->layout_mobile_different(); $grid['layout_mobile_sizing'] = $this->layout_mobile_sizing(); $grid['layout_mobile_sizing_fixed'] = $this->layout_mobile_sizing_fixed(); $grid['layout_mobile_sizing_columns'] = $this->layout_mobile_sizing_columns(); $grid['layout_mobile_sizing_margin'] = $this->layout_mobile_sizing_margin(); // Grid Item. $grid['template'] = $this->template(); $grid['use_image'] = $this->use_image(); $grid['link'] = $this->link(); $grid['link_type'] = $this->link_type(); $grid['link_target'] = $this->link_target(); // Grid Pagination. $grid['pagination_type'] = $this->pagination_type(); $grid['pagination'] = $this->pagination(); // Grid Other. $grid['metadata'] = $this->metadata(); $grid['metadata_name'] = $this->metadata_name(); $grid['metadata_description'] = $this->metadata_description(); $grid['deeplinking'] = $this->deeplinking(); $grid['deeplinking_jump'] = $this->deeplinking_jump(); $grid['empty_message'] = $this->empty_message(); return $grid; } /** * Get grid data for the manage page. * * @since 3.0.0 */ public function get_data_manage() { $grid = $this->get_data(); $grid['date'] = $this->date(); return $grid; } /** * Get isotope args for this grid. * * @since 3.0.0 */ public function get_javascript_args() { $args = array(); // Arguments for grid. $args['item_ids'] = $this->ids( array( 'type' => 'initial' ) ); $args['total_ids'] = $this->total_ids(); $args['order'] = array( array( 'by' => $this->grid_order_by(), 'type' => $this->grid_order(), ), ); $args['link'] = $this->link() ? $this->link_target() : false; $args['deeplinking'] = $this->deeplinking(); $args['deeplinking_jump'] = $this->deeplinking_jump(); // Arguments for Isotope JS. $args['isotope'] = array( 'itemSelector' => '.wpupg-item', 'layoutMode' => $this->layout_mode(), 'transitionDuration' => intval( WPUPG_Settings::get( 'grid_animation_speed' ) ), 'stagger' => intval( WPUPG_Settings::get( 'grid_animation_stagger' ) ), 'hiddenStyle' => self::get_css_array( WPUPG_Settings::get( 'grid_animation_hide' ) ), 'visibleStyle' => self::get_css_array( WPUPG_Settings::get( 'grid_animation_show' ) ), ); // RTL Mode. if ( $this->rtl_mode() ) { $args['isotope']['originLeft'] = false; } // Force height layout mode. if ( 'fitRowsHeight' === $this->layout_mode() ) { $args['isotope']['layoutMode'] = 'fitRows'; $args['force_height'] = true; } // CSS if ( $this->can_use_centered() && $this->centered() ) { $args['isotope']['masonry'] = array( 'isFitWidth' => true, ); } // Arguments for filters. $args['filters_relation'] = $this->filters_relation(); $args['filters_no_selection'] = $this->filters_no_selection(); $args['filters'] = array(); if ( $this->filters_enabled() ) { $filters = $this->filters(); foreach ( $filters as $index => $filter ) { // Make sure filter ID is set. if ( ! $filter['id'] ) { $filter['id'] = $index + 1; } $filter_args = array( 'id' => $filter['id'], 'type' => $filter['type'], ); $args['filters'][ $filter['id'] ] = apply_filters( 'wpupg_javascript_args_filter', $filter_args, $this, $filter ); } } // Arguments for pagination. $args['pagination_type'] = $this->pagination_type(); $args['pagination'] = apply_filters( 'wpupg_javascript_args_pagination', false, $this ); return apply_filters( 'wpupg_javascript_args', $args, $this ); } /** * Get CSS array from string. * * @since 3.0.0 * @param mixed $string String to get the CSS from. */ public function get_css_array( $string ) { $css = array(); if ( $string ) { $properties = explode( ';', $string ); foreach( $properties as $property ) { $parts = explode( ':', $property ); if ( 2 === count( $parts ) ) { $key = trim( $parts[0] ); if ( $key ) { $css[ $key ] = trim( $parts[1] ); } } } } return $css; } /** * Get metadata value. * * @since 3.0.0 * @param mixed $field Metadata field to retrieve. * @param mixed $default Default to return if metadata is not set. */ public function meta( $field, $default = '' ) { $value = $default; if ( false === $this->post ) { if ( isset( $this->meta[ $field ] ) ) { $value = $this->meta[ $field ]; } } else { // Use prefix when stored in actual meta. $stored_field = 'wpupg_' . $field; if ( ! $this->meta ) { $this->meta = get_post_custom( $this->id() ); } if ( isset( $this->meta[ $stored_field ] ) && null !== $this->meta[ $stored_field ][0] ) { $value = $this->meta[ $stored_field ][0]; } } // Allow filtering of the value. $value = apply_filters( 'wpupg_grid_meta_field_' . sanitize_key( $field ), $value, $this ); return $value; } /** * Try to unserialize as best as possible. * * @since 3.0.0 * @param mixed $maybe_serialized Potentially serialized data. */ public function unserialize( $maybe_serialized ) { $unserialized = @maybe_unserialize( $maybe_serialized ); if ( false === $unserialized ) { $maybe_serialized = preg_replace('/\s+/', ' ', $maybe_serialized ); $unserialized = unserialize( preg_replace_callback( '!s:(\d+):"(.*?)";!', array( $this, 'regex_replace_serialize' ), $maybe_serialized ) ); } return $unserialized; } /** * Callback for regex to fix serialize issues. * * @since 3.0.0 * @param mixed $match Regex match. */ public function regex_replace_serialize( $match ) { return ( $match[1] == strlen( $match[2] ) ) ? $match[0] : 's:' . strlen( $match[2] ) . ':"' . $match[2] . '";'; } /** * Grid Technical Fields. */ public function id() { return $this->post ? $this->post->ID : $this->meta( 'id' ); } public function slug_or_id() { $slug = $this->slug(); if ( ! $slug ) { $slug = $this->id(); } return $slug; } public function date() { return $this->post ? $this->post->post_date : $this->meta( 'date' ); } public function version() { return $this->meta( 'version', '0.0.0' ); } /** * Grid General Fields. */ public function name() { return $this->post ? $this->post->post_title : $this->meta( 'name' ); } public function slug() { return $this->post ? $this->post->post_name : $this->meta( 'slug' ); } /** * Grid Data Source Fields. */ public function type() { return $this->meta( 'type', 'posts' ); } public function post_types() { return $this->unserialize( $this->meta( 'post_types', array( 'post' ) ) ); } public function post_status() { $post_status = $this->unserialize( $this->meta( 'post_status', array( 'publish' ) ) ); if ( in_array( 'attachment', $this->post_types() ) ) { $post_status[] = 'inherit'; } return $post_status; } public function post_status_require_permission() { $bool = $this->meta( 'post_status_require_permission', true ); return (bool) $bool; } public function taxonomies() { return $this->unserialize( $this->meta( 'taxonomies', array( 'category' ) ) ); } public function password_protected() { return $this->meta( 'password_protected', 'all' ); } public function language() { return $this->meta( 'language', false ); } public function order_by() { return $this->meta( 'order_by', 'date' ); } public function order() { $order = $this->meta( 'order', 'desc' ); if ( 'rand' === $this->order_by() ) { // Adaptive pages break if order is not set to ASC when order_by is set to random. $order = 'asc'; } return $order; } public function order_custom_key() { return $this->meta( 'order_custom_key', '' ); } public function order_custom_key_numeric() { $bool = $this->meta( 'order_custom_key_numeric', false ); return (bool) $bool; } public function terms_order_by() { return $this->meta( 'terms_order_by', 'name' ); } public function terms_order() { return $this->meta( 'terms_order', 'asc' ); } public function grid_order_by() { if ( 'posts' === $this->type() ) { return $this->order_by(); } else { return $this->terms_order_by(); } } public function grid_order() { if ( 'posts' === $this->type() ) { return $this->order(); } else { return $this->terms_order(); } } /** * Grid Limit Items Fields. */ public function limit_posts_offset() { return $this->meta( 'limit_posts_offset', 0 ); } public function limit_posts_number() { return $this->meta( 'limit_posts_number', 0 ); } public function images_only() { $bool = $this->meta( 'images_only', false ); return (bool) $bool; } public function terms_images_only() { $bool = $this->meta( 'terms_images_only', false ); return (bool) $bool; } public function terms_hide_empty() { $bool = $this->meta( 'terms_hide_empty', false ); return (bool) $bool; } public function limit_terms() { $bool = $this->meta( 'limit_terms', false ); return (bool) $bool; } public function limit_terms_terms() { return $this->unserialize( $this->meta( 'limit_terms_terms', array() ) ); } public function limit_terms_type() { return $this->meta( 'limit_terms_type', 'restrict' ); } public function limit_posts() { $bool = $this->meta( 'limit_posts', false ); return (bool) $bool; } public function limit_rules() { return $this->unserialize( $this->meta( 'limit_rules', array() ) ); } /** * Grid Filters Fields. */ public function filter( $id ) { if ( ! $this->filters_enabled() || ! $id ) { return false; } $filters = $this->filters(); // If identifier was found, return filter. $index = array_search( $id, array_column( $filters, 'id' ) ); if ( false !== $index ) { return WPUPG_Filter::filter_with_defaults( $filters[ $index ] ); } // Check if index + 1 was used as the ID if not found by identifier. $index = intval( $id ) - 1; if ( isset( $filters[ $index ] ) ) { $filter = $filters[ $index ]; $filter['id'] = $id; return WPUPG_Filter::filter_with_defaults( $filter ); } return false; } public function filters() { if ( version_compare( $this->version(), '3.0.0', '<' ) ) { return WPUPG_Migrations::single_filter_to_multiple( $this ); } $filters_with_defaults = array(); $filters = $this->unserialize( $this->meta( 'filters', array() ) ); foreach ( $filters as $index => $filter ) { $filters_with_defaults[ $index ] = WPUPG_Filter::filter_with_defaults( $filter ); } return $filters_with_defaults; } public function filters_enabled() { if ( version_compare( $this->version(), '3.0.0', '<' ) ) { return 0 < count( $this->filters() ); } // No filters for a term grid. if ( 'terms' === $this->type() ) { return false; } $bool = $this->meta( 'filters_enabled', false ); return (bool) $bool; } public function filters_style( $field = false ) { $defaults = WPUPG_Filter::get_general_style_defaults(); $filters_style = $this->unserialize( $this->meta( 'filters_style', array() ) ); $filters_style_with_defaults = array_replace_recursive( $defaults, $filters_style ); if ( $field === false ) { return $filters_style_with_defaults; } else { return isset( $filters_style_with_defaults[ $field ] ) ? $filters_style_with_defaults[ $field ] : false; } } public function filters_relation() { return $this->meta( 'filters_relation', 'AND' ); } public function filters_no_selection() { return $this->meta( 'filters_no_selection', 'all' ); } public function responsive_toggle_style() { return $this->meta( 'responsive_toggle_style', 'custom' ); } public function responsive_toggle_style_closed() { return $this->meta( 'responsive_toggle_style_closed', '+' ); } public function responsive_toggle_style_open() { return $this->meta( 'responsive_toggle_style_open', '-' ); } /** * Grid Layout Fields. */ public function layout_mode() { return $this->meta( 'layout_mode', 'masonry' ); } public function centered() { $bool = $this->meta( 'centered', false ); return (bool) $bool; } public function rtl_mode() { $bool = $this->meta( 'rtl_mode', false ); return (bool) $bool; } public function can_use_centered() { $can_use_centered = false; if ( 'masonry' === $this->layout_mode() ) { if ( ! $this->layout_tablet_different() && ! $this->layout_mobile_different() ) { $can_use_centered = true; } } return $can_use_centered; } public function layout_desktop_sizing() { if ( version_compare( $this->version(), '3.0.0', '<' ) ) { return 'ignore'; } return $this->meta( 'layout_desktop_sizing', 'fixed' ); } public function layout_desktop_sizing_fixed() { return $this->meta( 'layout_desktop_sizing_fixed', 300 ); } public function layout_desktop_sizing_columns() { return $this->meta( 'layout_desktop_sizing_columns', 3 ); } public function layout_desktop_sizing_margin() { return $this->meta( 'layout_desktop_sizing_margin', 10 ); } public function layout_tablet_different() { $bool = $this->meta( 'layout_tablet_different', false ); return (bool) $bool; } public function layout_tablet_sizing() { return $this->meta( 'layout_tablet_sizing', 'fixed' ); } public function layout_tablet_sizing_fixed() { return $this->meta( 'layout_tablet_sizing_fixed', 300 ); } public function layout_tablet_sizing_columns() { return $this->meta( 'layout_tablet_sizing_columns', 2 ); } public function layout_tablet_sizing_margin() { return $this->meta( 'layout_tablet_sizing_margin', 10 ); } public function layout_mobile_different() { $bool = $this->meta( 'layout_mobile_different', false ); return (bool) $bool; } public function layout_mobile_sizing() { return $this->meta( 'layout_mobile_sizing', 'columns' ); } public function layout_mobile_sizing_fixed() { return $this->meta( 'layout_mobile_sizing_fixed', 300 ); } public function layout_mobile_sizing_columns() { return $this->meta( 'layout_mobile_sizing_columns', 1 ); } public function layout_mobile_sizing_margin() { return $this->meta( 'layout_mobile_sizing_margin', 10 ); } /** * Grid Item Fields. */ public function template() { if ( version_compare( $this->version(), '3.0.0', '<' ) ) { return WPUPG_Migrations::template_mapping( $this ); } return $this->meta( 'template', 'simple' ); } public function use_image() { return $this->meta( 'use_image', 'default' ); } public function link() { if ( version_compare( $this->version(), '3.0.0', '<' ) ) { return 'none' !== $this->meta( 'link_type' ); } $bool = $this->meta( 'link', true ); return (bool) $bool; } public function link_type() { if ( version_compare( $this->version(), '3.0.0', '<' ) ) { return $this->meta( 'link_target', 'post' ); } return $this->meta( 'link_type', 'post' ); } public function link_target() { if ( version_compare( $this->version(), '3.0.0', '<' ) ) { $target = $this->meta( 'link_type', '_self' ); return 'none' === $target ? '_self' : $target; } return $this->meta( 'link_target', '_self' ); } /** * Grid Pagination Fields. */ public function pagination_type() { // No pagination for term grid at the moment. if ( 'terms' === $this->type() ) { return 'none'; } $pagination_type = $this->meta( 'pagination_type', 'none' ); if ( version_compare( $this->version(), '3.0.0', '<' ) ) { $pagination_type = str_replace( 'load_more_filter', 'load_more', $pagination_type ); } return $pagination_type; } public function pagination( $type = false ) { if ( version_compare( $this->version(), '3.0.0', '<' ) ) { $pagination = WPUPG_Migrations::get_pagination_with_style( $this ); } else { $pagination = $this->unserialize( $this->meta( 'pagination', array() ) ); } $pagination_defaults = WPUPG_Pagination::get_defaults(); $pagination = array_replace_recursive( $pagination_defaults, $pagination ); if ( false === $type ) { return $pagination; } else { if ( isset( $pagination[ $type ] ) ) { return $pagination[ $type ]; } else { return array(); } } } /** * Grid Other Fields. */ public function metadata() { $bool = $this->meta( 'metadata', false ); return (bool) $bool; } public function metadata_name() { return $this->meta( 'metadata_name', '' ); } public function metadata_description() { return $this->meta( 'metadata_description', '' ); } public function deeplinking() { $bool = $this->meta( 'deeplinking', true ); return (bool) $bool; } public function deeplinking_jump() { $bool = $this->meta( 'deeplinking_jump', false ); return (bool) $bool; } public function empty_message() { return $this->meta( 'empty_message', '' ); } /** * Get the IDs that are displayed in this grid for the current page. * * @since 3.0.0 * @param mixed $grid_args Optional arguments. */ public function ids( $grid_args = array() ) { $all_ids = $this->all_ids( $grid_args ); $ids = apply_filters( 'wpupg_grid_ids', $all_ids, $this, $grid_args ); // Prevent already loaded IDs from loading again. $already_loaded_ids = isset( $grid_args['loaded_ids'] ) ? $grid_args['loaded_ids'] : array(); return array_filter( $ids, function( $id ) use ( $already_loaded_ids ) { return ! in_array( $id, $already_loaded_ids ); } ); } /** * Get the total number of IDs for this grid. * * @since 3.4.0 */ public function total_ids() { $total_ids = $this->total_ids; return false !== $total_ids ? intval( $total_ids ) : false; } /** * Get the query post arguments for getting all IDs. * * @since 3.0.0 * @param mixed $grid_args Optional arguments. */ public function all_ids_query_post_args( $grid_args = array() ) { $post_types = $this->post_types(); $args = array( 'post_type' => $post_types, 'post_status' => $this->post_status(), 'order' => $this->order(), 'orderby' => $this->order_by(), 'posts_per_page' => -1, 'fields' => 'ids', ); // Read permission for private posts. if ( in_array( 'private', $this->post_status() ) && $this->post_status_require_permission() ) { $args['perm'] = 'readable'; } // Password protected. switch ( $this->password_protected() ) { case 'exclude': $args['has_password'] = false; break; case 'only': $args['has_password'] = true; break; default: $args['has_password'] = null; } // Images Only if ( $this->images_only() ) { if ( in_array( 'attachment', $post_types ) ) { $args['post_mime_type'] = 'image/jpeg,image/gif,image/jpg,image/png'; } else { $args['meta_query'] = array( array( 'relation' => 'OR', array( 'key' => '_thumbnail_id', 'value' => '0', 'compare' => '>' ), array( 'key' => 'wpupg_custom_image_id', 'value' => '0', 'compare' => '>' ), ) ); } } // Exclude specific ids. if ( isset( $grid_args['loaded_ids'] ) ) { $args['post__not_in'] = $grid_args['loaded_ids']; } // Apply filters. $tax_query = array( 'relation' => 'AND', ); $meta_query = isset( $args['meta_query'] ) ? $args['meta_query'] : array(); $meta_query['relation'] = 'AND'; $filters = isset( $grid_args['filters'] ) ? $grid_args['filters'] : array(); foreach ( $filters as $filter ) { switch ( $filter['type'] ) { case 'search': $args['s'] = $filter['text']; break; case 'terms': foreach ( $filter['terms'] as $taxonomy => $terms ) { if ( $terms ) { $tax_query[] = array( 'taxonomy' => $taxonomy, 'field' => 'slug', 'terms' => $terms, 'operator' => $filter['terms_inverse'] ? 'NOT IN' : 'IN', ); } } if ( 'AND' !== $filter['terms_relation'] ) { $tax_query['relation'] = $filter['terms_relation']; } break; case 'custom_field': foreach ( $filter['values'] as $value ) { if ( $value ) { switch ( $value['type'] ) { case 'string': $meta_query[] = array( 'key' => $filter['custom_field'], 'value' => $value['value'], 'compare' => $filter['values_inverse'] ? '!=' : '=', ); break; case 'fuzzy': $meta_query[] = array( 'key' => $filter['custom_field'], 'value' => $value['value'], 'compare' => $filter['values_inverse'] ? 'NOT LIKE' : 'LIKE', ); break; case 'number': $meta_query[] = array( 'key' => $filter['custom_field'], 'value' => $value['value'], 'type' => 'NUMERIC', 'compare' => $filter['values_inverse'] ? '!=' : '=', ); break; case 'range': $meta_query[] = array( 'key' => $filter['custom_field'], 'value' => $value['value'], 'type' => 'NUMERIC', 'compare' => $filter['values_inverse'] ? 'NOT BETWEEN' : 'BETWEEN', ); break; } } } if ( 'AND' !== $filter['values_relation'] ) { $meta_query['relation'] = $filter['values_relation']; } break; } } if ( 1 < count( $tax_query ) ) { $args['tax_query'] = $tax_query; } if ( 1 < count( $meta_query ) ) { $args['meta_query'] = $meta_query; } return apply_filters( 'wpupg_query_post_args', $args, $this, $grid_args ); } /** * Get all the IDs that are displayed in this grid. * * @since 3.0.0 * @param mixed $grid_args Optional arguments. */ public function all_ids( $grid_args = array() ) { if ( false === $this->ids ) { $ids = array(); $total_ids = false; if ( 'posts' === $this->type() ) { $args = $this->all_ids_query_post_args( $grid_args ); // Optionally set specific language. WPUPG_Multilingual::set_language( $this->language() ); // Query IDs. $query = new WP_Query( $args ); $posts = $query->have_posts() ? $query->posts : array(); $ids = array_map( 'intval', $posts ); $total_ids = $query->found_posts; // Unset language. WPUPG_Multilingual::unset_language(); // Offset posts if ( $this->limit_posts_offset() ) { $ids = array_slice( $ids, $this->limit_posts_offset() ); } // Limit Total # Posts if ( $this->limit_posts_number() ) { $ids = array_slice( $ids, 0, $this->limit_posts_number() ); } } $this->ids = apply_filters( 'wpupg_grid_all_ids', $ids, $this, $grid_args ); $this->total_ids = $total_ids; } return $this->ids; } /** * Get the terms to display in this grid. * * @since 3.0.0 * @param mixed $grid_args Optional arguments. */ public function terms( $grid_args = array() ) { if ( false === $this->terms ) { $terms = array( 'per_item' => array(), 'per_taxonomy' => array(), ); $taxonomies = $this->filters_taxonomies(); // Only need to get terms if there actually are taxonomies. if ( count( $taxonomies ) ) { $post_ids = $this->all_ids( $grid_args ); // Build sanitized query. global $wpdb; $where_taxonomies = implode( '","', array_map( 'sanitize_key', $taxonomies ) ); $where_post_ids = implode( ',', array_map( 'intval', $post_ids ) ); $query = 'SELECT tr.object_id, tt.taxonomy, tt.parent, t.term_id, t.slug, t.name FROM ' . $wpdb->prefix . 'term_relationships tr JOIN ' . $wpdb->prefix . 'term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id JOIN ' . $wpdb->prefix . 'terms t on tt.term_id = t.term_id WHERE tt.taxonomy IN ("' . $where_taxonomies . '")'; if ( $where_post_ids ) { $query .= ' AND tr.object_id IN (' . $where_post_ids . ')'; } $results = $wpdb->get_results( $query, ARRAY_A ); // Loop over all results. $terms = $this->handle_terms( $terms, $results ); } $this->terms = $terms; } return $this->terms; } /** * Get terms from query reults. * * @since 3.5.0 */ public function handle_terms( $terms, $results, $depth = 0 ) { $parents = array(); foreach ( $results as $result ) { $item_id = intval( $result['object_id'] ); $taxonomy = $result['taxonomy']; $term_id = intval( $result['term_id'] ); $parent = intval( $result['parent'] ); $slug = rawurldecode( $result['slug'] ); $name = apply_filters( 'wpupg_term_name', $result['name'], $term_id, $taxonomy ); // Make sure arrays exist. if ( ! isset( $terms['per_taxonomy'][ $taxonomy ] ) ) { $terms['per_taxonomy'][ $taxonomy ] = array(); } if ( ! isset( $terms['per_item'][ $item_id ] ) ) { $terms['per_item'][ $item_id ] = array(); } if ( ! isset( $terms['per_item'][ $item_id ][ $taxonomy ] ) ) { $terms['per_item'][ $item_id ][ $taxonomy ] = array( 'terms' => array(), 'parent_terms' => array(), ); } if ( !isset( $terms['per_taxonomy'][ $taxonomy ][ $slug ] ) ) { $terms['per_taxonomy'][ $taxonomy ][ $slug ] = array( 'id' => $term_id, 'parent' => $parent, 'name' => $name, 'posts' => array(), 'child_posts' => array(), ); } // Add in correct category. if ( 0 === $depth ) { $terms['per_item'][ $item_id ][ $taxonomy ]['terms'][] = $slug; $terms['per_taxonomy'][ $taxonomy ][ $slug ]['posts'][] = $item_id; } else { $terms['per_item'][ $item_id ][ $taxonomy ]['parent_terms'][] = $slug; $terms['per_taxonomy'][ $taxonomy ][ $slug ]['child_posts'][] = $item_id; } // Check if there are further parents we need to get. if ( $parent ) { $parents[] = array( 'object_id' => $item_id, 'taxonomy' => $taxonomy, 'term_id' => $parent, ); } } // Check if there are any parents we need to associate as well. if ( count( $parents ) ) { $parent_results = array(); $parent_ids = wp_list_pluck( $parents, 'term_id' ); $parent_terms = get_terms( array( 'include' => $parent_ids, ) ); if ( $parent_terms && ! is_wp_error( $parent_terms ) ) { // Set term_id as key. $keyed_parent_terms = array(); foreach ( $parent_terms as $parent_term ) { $keyed_parent_terms[ $parent_term->term_id ] = $parent_term; } foreach( $parents as $index => $parent ) { if ( isset( $keyed_parent_terms[ $parent['term_id'] ] ) ) { $parent_term = $keyed_parent_terms[ $parent['term_id'] ]; $parent_results[] = array( 'object_id' => $parent['object_id'], 'taxonomy' => $parent['taxonomy'], 'term_id' => $parent['term_id'], 'parent' => $parent_term->parent, 'slug' => $parent_term->slug, 'name' => $parent_term->name, ); } } } // Recursive loop. if ( count( $parent_results ) ) { $terms = $this->handle_terms( $terms, $parent_results, $depth + 1 ); } } return $terms; } /** * Get the terms for a specific item. * * @since 3.0.0 * @param int $item_id Item to get the terms for. */ public function get_terms_for_item( $item_id ) { $terms = $this->terms(); return isset( $terms['per_item'][ $item_id ] ) ? $terms['per_item'][ $item_id ] : array(); } /** * Get the terms for a specific taxonomy. * * @since 3.0.0 * @param string $taxonomy Taxonomy to get the terms for. */ public function get_terms_for_taxonomy( $taxonomy ) { $terms = $this->terms(); return isset( $terms['per_taxonomy'][ $taxonomy ] ) ? $terms['per_taxonomy'][ $taxonomy ] : array(); } /** * Get the taxonomies filtered by in this grid. * * @since 3.0.0 */ public function filters_taxonomies() { $taxonomies = array(); if ( $this->filters_enabled() ) { $filters = $this->filters(); foreach ( $filters as $filter ) { if ( ! isset( $filter['options']['source'] ) || 'taxonomies' === $filter['options']['source'] ) { if ( isset( $filter['options']['taxonomies'] ) ) { $taxonomies = array_merge( $taxonomies, $filter['options']['taxonomies'] ); } } } } return array_unique( $taxonomies ); } /** * Get the custom fields filtered by in this grid. * * @since 3.3.0 */ public function filters_custom_fields() { $custom_fields = array(); if ( $this->filters_enabled() ) { $filters = $this->filters(); foreach ( $filters as $filter ) { if ( ! isset( $filter['options']['source'] ) || 'custom_field' === $filter['options']['source'] ) { if ( isset( $filter['options']['custom_field'] ) ) { $custom_fields[] = $filter['options']['custom_field']; } } } } return array_unique( $custom_fields ); } }