PluginProbe
WP Ultimate Post Grid / 3.5.0
WP Ultimate Post Grid v3.5.0
4.1.1 trunk 1.5 1.6 1.7 1.7.1 1.7.2 1.8 1.9 2.0 2.1 2.2 2.3 2.4.0 2.5.0 2.6.0 2.7.0 2.8.0 2.8.2 3.0.0 3.3.0 3.4.0 3.5.0 3.6.0 3.7.0 All 32 releases
wp-ultimate-post-grid / includes / public / class-wpupg-grid.php

class-wpupg-grid.php in WP Ultimate Post Grid 3.5.0, at includes/public/class-wpupg-grid.php

1,023 lines 28.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Represents a grid.
4 *
5 * @link https://bootstrapped.ventures
6 * @since 3.0.0
7 *
8 * @package WP_Ultimate_Post_Grid
9 * @subpackage WP_Ultimate_Post_Grid/includes/public
10 */
11
12 /**
13 * Represents a grid.
14 *
15 * @since 3.0.0
16 * @package WP_Ultimate_Post_Grid
17 * @subpackage WP_Ultimate_Post_Grid/includes/public
18 * @author Brecht Vandersmissen <brecht@bootstrapped.ventures>
19 */
20 class WPUPG_Grid {
21
22 /**
23 * WP_Post object associated with this grid post type.
24 *
25 * @since 3.0.0
26 * @access private
27 * @var object $post WP_Post object of this grid post type.
28 */
29 private $post;
30
31 /**
32 * Cached IDs to display in this grid.
33 *
34 * @since 3.0.0
35 * @access private
36 * @var array $ids Grid item IDs.
37 */
38 private $ids = false;
39
40 /**
41 * Cached total IDs to display in this grid.
42 *
43 * @since 3.3.0
44 * @access private
45 * @var array $total_ids Total number of grid ids.
46 */
47 private $total_ids = false;
48
49 /**
50 * Cached terms to display in this grid.
51 *
52 * @since 3.0.0
53 * @access private
54 * @var array $ids Terms for this grid.
55 */
56 private $terms = false;
57
58 /**
59 * Metadata associated with this grid post type.
60 *
61 * @since 3.0.0
62 * @access private
63 * @var array $meta Grid metadata.
64 */
65 private $meta = false;
66
67 /**
68 * Get new grid object from associated post.
69 *
70 * @since 3.0.0
71 * @param mixed $post Meta or WP_Post object for this grid post type.
72 */
73 public function __construct( $post_or_meta ) {
74 $post = is_object( $post_or_meta ) && $post_or_meta instanceof WP_Post ? $post_or_meta : false;
75 $this->post = $post;
76
77 // Not a WP_Post object, so just manually set the meta values.
78 if ( ! $post ) {
79 $this->meta = $post_or_meta;
80 }
81 }
82
83 /**
84 * Get grid data.
85 *
86 * @since 3.0.0
87 */
88 public function get_data() {
89 $grid = array();
90
91 // Technical Fields.
92 $grid['id'] = $this->id();
93 $grid['version'] = $this->version();
94
95 // Grid General.
96 $grid['name'] = $this->name();
97 $grid['slug'] = $this->slug();
98
99 // Grid Data Source.
100 $grid['type'] = $this->type();
101 $grid['post_types'] = $this->post_types();
102 $grid['post_status'] = $this->post_status();
103 $grid['post_status_require_permission'] = $this->post_status_require_permission();
104 $grid['taxonomies'] = $this->taxonomies();
105 $grid['order_by'] = $this->order_by();
106 $grid['order'] = $this->order();
107 $grid['order_custom_key'] = $this->order_custom_key();
108 $grid['order_custom_key_numeric'] = $this->order_custom_key_numeric();
109 $grid['terms_order_by'] = $this->terms_order_by();
110 $grid['terms_order'] = $this->terms_order();
111
112 // Grid Limit Items.
113 $grid['limit_posts_offset'] = $this->limit_posts_offset();
114 $grid['limit_posts_number'] = $this->limit_posts_number();
115 $grid['images_only'] = $this->images_only();
116 $grid['terms_images_only'] = $this->terms_images_only();
117 $grid['terms_hide_empty'] = $this->terms_hide_empty();
118 $grid['limit_terms'] = $this->limit_terms();
119 $grid['limit_terms_terms'] = $this->limit_terms_terms();
120 $grid['limit_terms_type'] = $this->limit_terms_type();
121 $grid['limit_posts'] = $this->limit_posts();
122 $grid['limit_rules'] = $this->limit_rules();
123
124 // Grid Filters.
125 $grid['filters'] = $this->filters();
126 $grid['filters_enabled'] = $this->filters_enabled();
127 $grid['filters_style'] = $this->filters_style();
128 $grid['filters_relation'] = $this->filters_relation();
129
130 // Grid Layout.
131 $grid['layout_mode'] = $this->layout_mode();
132 $grid['centered'] = $this->centered();
133 $grid['layout_desktop_sizing'] = $this->layout_desktop_sizing();
134 $grid['layout_desktop_sizing_fixed'] = $this->layout_desktop_sizing_fixed();
135 $grid['layout_desktop_sizing_columns'] = $this->layout_desktop_sizing_columns();
136 $grid['layout_desktop_sizing_margin'] = $this->layout_desktop_sizing_margin();
137 $grid['layout_tablet_different'] = $this->layout_tablet_different();
138 $grid['layout_tablet_sizing'] = $this->layout_tablet_sizing();
139 $grid['layout_tablet_sizing_fixed'] = $this->layout_tablet_sizing_fixed();
140 $grid['layout_tablet_sizing_columns'] = $this->layout_tablet_sizing_columns();
141 $grid['layout_tablet_sizing_margin'] = $this->layout_tablet_sizing_margin();
142 $grid['layout_mobile_different'] = $this->layout_mobile_different();
143 $grid['layout_mobile_sizing'] = $this->layout_mobile_sizing();
144 $grid['layout_mobile_sizing_fixed'] = $this->layout_mobile_sizing_fixed();
145 $grid['layout_mobile_sizing_columns'] = $this->layout_mobile_sizing_columns();
146 $grid['layout_mobile_sizing_margin'] = $this->layout_mobile_sizing_margin();
147
148 // Grid Item.
149 $grid['template'] = $this->template();
150 $grid['link'] = $this->link();
151 $grid['link_type'] = $this->link_type();
152 $grid['link_target'] = $this->link_target();
153
154 // Grid Pagination.
155 $grid['pagination_type'] = $this->pagination_type();
156 $grid['pagination'] = $this->pagination();
157
158 // Grid Other.
159 $grid['deeplinking'] = $this->deeplinking();
160 $grid['empty_message'] = $this->empty_message();
161
162 return $grid;
163 }
164
165 /**
166 * Get grid data for the manage page.
167 *
168 * @since 3.0.0
169 */
170 public function get_data_manage() {
171 $grid = $this->get_data();
172
173 $grid['date'] = $this->date();
174
175 return $grid;
176 }
177
178 /**
179 * Get isotope args for this grid.
180 *
181 * @since 3.0.0
182 */
183 public function get_javascript_args() {
184 $args = array();
185
186 // Arguments for grid.
187 $args['item_ids'] = $this->ids( array( 'type' => 'initial' ) );
188 $args['order'] = array(
189 array(
190 'by' => $this->order_by(),
191 'type' => $this->order(),
192 ),
193 );
194 $args['link'] = $this->link() ? $this->link_target() : false;
195 $args['deeplinking'] = $this->deeplinking();
196
197 // Arguments for Isotope JS.
198 $args['isotope'] = array(
199 'itemSelector' => '.wpupg-item',
200 'layoutMode' => $this->layout_mode(),
201 'transitionDuration' => intval( WPUPG_Settings::get( 'grid_animation_speed' ) ),
202 'stagger' => intval( WPUPG_Settings::get( 'grid_animation_stagger' ) ),
203 'hiddenStyle' => self::get_css_array( WPUPG_Settings::get( 'grid_animation_hide' ) ),
204 'visibleStyle' => self::get_css_array( WPUPG_Settings::get( 'grid_animation_show' ) ),
205 );
206
207 // CSS
208 if ( $this->can_use_centered() && $this->centered() ) {
209 $args['isotope']['masonry'] = array(
210 'isFitWidth' => true,
211 );
212 }
213
214 // Arguments for filters.
215 $args['filters_relation'] = $this->filters_relation();
216 $args['filters'] = array();
217 if ( $this->filters_enabled() ) {
218 $filters = $this->filters();
219
220 foreach ( $filters as $index => $filter ) {
221 // Make sure filter ID is set.
222 if ( ! $filter['id'] ) {
223 $filter['id'] = $index + 1;
224 }
225
226 $filter_args = array(
227 'id' => $filter['id'],
228 'type' => $filter['type'],
229 );
230
231 $args['filters'][ $filter['id'] ] = apply_filters( 'wpupg_javascript_args_filter', $filter_args, $this, $filter );
232 }
233 }
234
235 // Arguments for pagination.
236 $args['pagination_type'] = $this->pagination_type();
237 $args['pagination'] = apply_filters( 'wpupg_javascript_args_pagination', false, $this );
238
239 return apply_filters( 'wpupg_javascript_args', $args, $this );
240 }
241
242 /**
243 * Get CSS array from string.
244 *
245 * @since 3.0.0
246 * @param mixed $string String to get the CSS from.
247 */
248 public function get_css_array( $string ) {
249 $css = array();
250
251 if ( $string ) {
252 $properties = explode( ';', $string );
253
254 foreach( $properties as $property ) {
255 $parts = explode( ':', $property );
256
257 if ( 2 === count( $parts ) ) {
258 $key = trim( $parts[0] );
259
260 if ( $key ) {
261 $css[ $key ] = trim( $parts[1] );
262 }
263 }
264 }
265 }
266
267 return $css;
268 }
269
270 /**
271 * Get metadata value.
272 *
273 * @since 3.0.0
274 * @param mixed $field Metadata field to retrieve.
275 * @param mixed $default Default to return if metadata is not set.
276 */
277 public function meta( $field, $default = '' ) {
278 if ( false === $this->post ) {
279 if ( isset( $this->meta[ $field ] ) ) {
280 return $this->meta[ $field ];
281 }
282 } else {
283 // Use prefix when stored in actual meta.
284 $field = 'wpupg_' . $field;
285
286 if ( ! $this->meta ) {
287 $this->meta = get_post_custom( $this->id() );
288 }
289
290 if ( isset( $this->meta[ $field ] ) && null !== $this->meta[ $field ][0] ) {
291 return $this->meta[ $field ][0];
292 }
293 }
294
295 return $default;
296 }
297
298 /**
299 * Try to unserialize as best as possible.
300 *
301 * @since 3.0.0
302 * @param mixed $maybe_serialized Potentially serialized data.
303 */
304 public function unserialize( $maybe_serialized ) {
305 $unserialized = @maybe_unserialize( $maybe_serialized );
306
307 if ( false === $unserialized ) {
308 $maybe_serialized = preg_replace('/\s+/', ' ', $maybe_serialized );
309 $unserialized = unserialize( preg_replace_callback( '!s:(\d+):"(.*?)";!', array( $this, 'regex_replace_serialize' ), $maybe_serialized ) );
310 }
311
312 return $unserialized;
313 }
314
315 /**
316 * Callback for regex to fix serialize issues.
317 *
318 * @since 3.0.0
319 * @param mixed $match Regex match.
320 */
321 public function regex_replace_serialize( $match ) {
322 return ( $match[1] == strlen( $match[2] ) ) ? $match[0] : 's:' . strlen( $match[2] ) . ':"' . $match[2] . '";';
323 }
324
325 /**
326 * Grid Technical Fields.
327 */
328 public function id() {
329 return $this->post ? $this->post->ID : $this->meta( 'id' );
330 }
331 public function slug_or_id() {
332 $slug = $this->slug();
333
334 if ( ! $slug ) {
335 $slug = $this->id();
336 }
337
338 return $slug;
339 }
340 public function date() {
341 return $this->post ? $this->post->post_date : $this->meta( 'date' );
342 }
343 public function version() {
344 return $this->meta( 'version', '0.0.0' );
345 }
346
347 /**
348 * Grid General Fields.
349 */
350 public function name() {
351 return $this->post ? $this->post->post_title : $this->meta( 'name' );
352 }
353 public function slug() {
354 return $this->post ? $this->post->post_name : $this->meta( 'slug' );
355 }
356 /**
357 * Grid Data Source Fields.
358 */
359 public function type() {
360 return $this->meta( 'type', 'posts' );
361 }
362 public function post_types() {
363 return $this->unserialize( $this->meta( 'post_types', array( 'post' ) ) );
364 }
365 public function post_status() {
366 $post_status = $this->unserialize( $this->meta( 'post_status', array( 'publish' ) ) );
367
368 if ( in_array( 'attachment', $this->post_types() ) ) {
369 $post_status[] = 'inherit';
370 }
371
372 return $post_status;
373 }
374 public function post_status_require_permission() {
375 return $this->meta( 'post_status_require_permission', true );
376 }
377 public function taxonomies() {
378 return $this->unserialize( $this->meta( 'taxonomies', array( 'category' ) ) );
379 }
380 public function order_by() {
381 return $this->meta( 'order_by', 'date' );
382 }
383 public function order() {
384 return $this->meta( 'order', 'desc' );
385 }
386 public function order_custom_key() {
387 return $this->meta( 'order_custom_key', '' );
388 }
389 public function order_custom_key_numeric() {
390 return $this->meta( 'order_custom_key_numeric', false );
391 }
392 public function terms_order_by() {
393 return $this->meta( 'terms_order_by', 'name' );
394 }
395 public function terms_order() {
396 return $this->meta( 'terms_order', 'asc' );
397 }
398 /**
399 * Grid Limit Items Fields.
400 */
401 public function limit_posts_offset() {
402 return $this->meta( 'limit_posts_offset', 0 );
403 }
404 public function limit_posts_number() {
405 return $this->meta( 'limit_posts_number', 0 );
406 }
407 public function images_only() {
408 return $this->meta( 'images_only', false );
409 }
410 public function terms_images_only() {
411 return $this->meta( 'terms_images_only', false );
412 }
413 public function terms_hide_empty() {
414 return $this->meta( 'terms_hide_empty', false );
415 }
416 public function limit_terms() {
417 return $this->meta( 'limit_terms', false );
418 }
419 public function limit_terms_terms() {
420 return $this->unserialize( $this->meta( 'limit_terms_terms', array() ) );
421 }
422 public function limit_terms_type() {
423 return $this->meta( 'limit_terms_type', 'restrict' );
424 }
425 public function limit_posts() {
426 return $this->meta( 'limit_posts', false );
427 }
428 public function limit_rules() {
429 return $this->unserialize( $this->meta( 'limit_rules', array() ) );
430 }
431
432 /**
433 * Grid Filters Fields.
434 */
435 public function filter( $id ) {
436 if ( ! $this->filters_enabled() || ! $id ) {
437 return false;
438 }
439
440 $filters = $this->filters();
441
442 // If identifier was found, return filter.
443 $index = array_search( $id, array_column( $filters, 'id' ) );
444 if ( false !== $index ) {
445 return WPUPG_Filter::filter_with_defaults( $filters[ $index ] );
446 }
447
448 // Check if index + 1 was used as the ID if not found by identifier.
449 $index = intval( $id ) - 1;
450 if ( isset( $filters[ $index ] ) ) {
451 $filter = $filters[ $index ];
452 $filter['id'] = $id;
453 return WPUPG_Filter::filter_with_defaults( $filter );
454 }
455
456 return false;
457 }
458 public function filters() {
459 if ( version_compare( $this->version(), '3.0.0', '<' ) ) {
460 return WPUPG_Migrations::single_filter_to_multiple( $this );
461 }
462
463 $filters_with_defaults = array();
464 $filters = $this->unserialize( $this->meta( 'filters', array() ) );
465
466 foreach ( $filters as $index => $filter ) {
467 $filters_with_defaults[ $index ] = WPUPG_Filter::filter_with_defaults( $filter );
468 }
469
470 return $filters_with_defaults;
471 }
472 public function filters_enabled() {
473 if ( version_compare( $this->version(), '3.0.0', '<' ) ) {
474 return 0 < count( $this->filters() );
475 }
476
477 // No filters for a term grid.
478 if ( 'terms' === $this->type() ) {
479 return false;
480 }
481
482 return $this->meta( 'filters_enabled', false );
483 }
484 public function filters_style( $field = false ) {
485 $defaults = WPUPG_Filter::get_general_style_defaults();
486 $filters_style = $this->unserialize( $this->meta( 'filters_style', array() ) );
487 $filters_style_with_defaults = array_replace_recursive( $defaults, $filters_style );
488
489 if ( $field === false ) {
490 return $filters_style_with_defaults;
491 } else {
492 return isset( $filters_style_with_defaults[ $field ] ) ? $filters_style_with_defaults[ $field ] : false;
493 }
494 }
495 public function filters_relation() {
496 return $this->meta( 'filters_relation', 'AND' );
497 }
498
499 /**
500 * Grid Layout Fields.
501 */
502 public function layout_mode() {
503 return $this->meta( 'layout_mode', 'masonry' );
504 }
505 public function centered() {
506 return $this->meta( 'centered', false );
507 }
508 public function can_use_centered() {
509 $can_use_centered = false;
510
511 if ( 'masonry' === $this->layout_mode() ) {
512 if ( ! $this->layout_tablet_different() && ! $this->layout_mobile_different() ) {
513 $can_use_centered = true;
514 }
515 }
516
517 return $can_use_centered;
518 }
519 public function layout_desktop_sizing() {
520 if ( version_compare( $this->version(), '3.0.0', '<' ) ) {
521 return 'ignore';
522 }
523 return $this->meta( 'layout_desktop_sizing', 'fixed' );
524 }
525 public function layout_desktop_sizing_fixed() {
526 return $this->meta( 'layout_desktop_sizing_fixed', 300 );
527 }
528 public function layout_desktop_sizing_columns() {
529 return $this->meta( 'layout_desktop_sizing_columns', 3 );
530 }
531 public function layout_desktop_sizing_margin() {
532 return $this->meta( 'layout_desktop_sizing_margin', 10 );
533 }
534 public function layout_tablet_different() {
535 return $this->meta( 'layout_tablet_different', false );
536 }
537 public function layout_tablet_sizing() {
538 return $this->meta( 'layout_tablet_sizing', 'fixed' );
539 }
540 public function layout_tablet_sizing_fixed() {
541 return $this->meta( 'layout_tablet_sizing_fixed', 300 );
542 }
543 public function layout_tablet_sizing_columns() {
544 return $this->meta( 'layout_tablet_sizing_columns', 2 );
545 }
546 public function layout_tablet_sizing_margin() {
547 return $this->meta( 'layout_tablet_sizing_margin', 10 );
548 }
549 public function layout_mobile_different() {
550 return $this->meta( 'layout_mobile_different', false );
551 }
552 public function layout_mobile_sizing() {
553 return $this->meta( 'layout_mobile_sizing', 'columns' );
554 }
555 public function layout_mobile_sizing_fixed() {
556 return $this->meta( 'layout_mobile_sizing_fixed', 300 );
557 }
558 public function layout_mobile_sizing_columns() {
559 return $this->meta( 'layout_mobile_sizing_columns', 1 );
560 }
561 public function layout_mobile_sizing_margin() {
562 return $this->meta( 'layout_mobile_sizing_margin', 10 );
563 }
564 /**
565 * Grid Item Fields.
566 */
567 public function template() {
568 if ( version_compare( $this->version(), '3.0.0', '<' ) ) {
569 return WPUPG_Migrations::template_mapping( $this );
570 }
571 return $this->meta( 'template', 'simple' );
572 }
573 public function link() {
574 if ( version_compare( $this->version(), '3.0.0', '<' ) ) {
575 return 'none' !== $this->meta( 'link_type' );
576 }
577 return $this->meta( 'link', true );
578 }
579 public function link_type() {
580 if ( version_compare( $this->version(), '3.0.0', '<' ) ) {
581 return $this->meta( 'link_target', 'post' );
582 }
583 return $this->meta( 'link_type', 'post' );
584 }
585 public function link_target() {
586 if ( version_compare( $this->version(), '3.0.0', '<' ) ) {
587 $target = $this->meta( 'link_type', '_self' );
588 return 'none' === $target ? '_self' : $target;
589 }
590 return $this->meta( 'link_target', '_self' );
591 }
592 /**
593 * Grid Pagination Fields.
594 */
595 public function pagination_type() {
596 $pagination_type = $this->meta( 'pagination_type', 'none' );
597
598 if ( version_compare( $this->version(), '3.0.0', '<' ) ) {
599 $pagination_type = str_replace( 'load_more_filter', 'load_more', $pagination_type );
600 }
601
602 return $pagination_type;
603 }
604 public function pagination( $type = false ) {
605 if ( version_compare( $this->version(), '3.0.0', '<' ) ) {
606 $pagination = WPUPG_Migrations::get_pagination_with_style( $this );
607 } else {
608 $pagination = $this->unserialize( $this->meta( 'pagination', array() ) );
609 }
610
611 $pagination_defaults = WPUPG_Pagination::get_defaults();
612 $pagination = array_replace_recursive( $pagination_defaults, $pagination );
613
614 if ( false === $type ) {
615 return $pagination;
616 } else {
617 if ( isset( $pagination[ $type ] ) ) {
618 return $pagination[ $type ];
619 } else {
620 return array();
621 }
622 }
623 }
624 /**
625 * Grid Other Fields.
626 */
627 public function deeplinking() {
628 return $this->meta( 'deeplinking', true );
629 }
630 public function empty_message() {
631 return $this->meta( 'empty_message', '' );
632 }
633
634 /**
635 * Get the IDs that are displayed in this grid for the current page.
636 *
637 * @since 3.0.0
638 * @param mixed $grid_args Optional arguments.
639 */
640 public function ids( $grid_args = array() ) {
641 $all_ids = $this->all_ids( $grid_args );
642 $ids = apply_filters( 'wpupg_grid_ids', $all_ids, $this, $grid_args );
643
644 // Prevent already loaded IDs from loading again.
645 $already_loaded_ids = isset( $grid_args['loaded_ids'] ) ? $grid_args['loaded_ids'] : array();
646 return array_filter( $ids, function( $id ) use ( $already_loaded_ids ) { return ! in_array( $id, $already_loaded_ids ); } );
647 }
648
649 /**
650 * Get the total number of IDs for this grid.
651 *
652 * @since 3.4.0
653 */
654 public function total_ids() {
655 $total_ids = $this->total_ids;
656 return false !== $total_ids ? intval( $total_ids ) : false;
657 }
658
659 /**
660 * Get the query post arguments for getting all IDs.
661 *
662 * @since 3.0.0
663 * @param mixed $grid_args Optional arguments.
664 */
665 public function all_ids_query_post_args( $grid_args = array() ) {
666 $post_types = $this->post_types();
667
668 $args = array(
669 'post_type' => $post_types,
670 'post_status' => $this->post_status(),
671 'order' => $this->order(),
672 'orderby' => $this->order_by(),
673 'posts_per_page' => -1,
674 // 'posts_per_page' => 2,
675 'fields' => 'ids',
676 );
677
678 // Read permission for private posts.
679 if ( in_array( 'private', $this->post_status() ) && $this->post_status_require_permission() ) {
680 $args['perm'] = 'readable';
681 }
682
683 // Images Only
684 if ( $this->images_only() ) {
685 if ( in_array( 'attachment', $post_types ) ) {
686 $args['post_mime_type'] = 'image/jpeg,image/gif,image/jpg,image/png';
687 } else {
688 $args['meta_query'] = array(
689 array(
690 'key' => '_thumbnail_id',
691 'value' => '0',
692 'compare' => '>'
693 ),
694 );
695 }
696 }
697
698 // Exclude specific ids.
699 if ( isset( $grid_args['loaded_ids'] ) ) {
700 $args['post__not_in'] = $grid_args['loaded_ids'];
701 }
702
703 // Apply filters.
704 $tax_query = array(
705 'relation' => 'AND',
706 );
707
708 $meta_query = isset( $args['meta_query'] ) ? $args['meta_query'] : array();
709 $meta_query['relation'] = 'AND';
710
711 $filters = isset( $grid_args['filters'] ) ? $grid_args['filters'] : array();
712 foreach ( $filters as $filter ) {
713 switch ( $filter['type'] ) {
714 case 'search':
715 $args['s'] = $filter['text'];
716 break;
717 case 'terms':
718 foreach ( $filter['terms'] as $taxonomy => $terms ) {
719 if ( $terms ) {
720 $tax_query[] = array(
721 'taxonomy' => $taxonomy,
722 'field' => 'slug',
723 'terms' => $terms,
724 'operator' => $filter['terms_inverse'] ? 'NOT IN' : 'IN',
725 );
726 }
727 }
728
729 if ( 'AND' !== $filter['terms_relation'] ) {
730 $tax_query['relation'] = $filter['terms_relation'];
731 }
732 break;
733 case 'custom_field':
734 foreach ( $filter['values'] as $value ) {
735 if ( $value ) {
736 switch ( $value['type'] ) {
737 case 'string':
738 $meta_query[] = array(
739 'key' => $filter['custom_field'],
740 'value' => $value['value'],
741 'compare' => $filter['values_inverse'] ? '!=' : '=',
742 );
743 break;
744 case 'number':
745 $meta_query[] = array(
746 'key' => $filter['custom_field'],
747 'value' => $value['value'],
748 'type' => 'NUMERIC',
749 'compare' => $filter['values_inverse'] ? '!=' : '=',
750 );
751 break;
752 case 'range':
753 $meta_query[] = array(
754 'key' => $filter['custom_field'],
755 'value' => $value['value'],
756 'type' => 'NUMERIC',
757 'compare' => $filter['values_inverse'] ? 'NOT BETWEEN' : 'BETWEEN',
758 );
759 break;
760 }
761 }
762 }
763
764 if ( 'AND' !== $filter['values_relation'] ) {
765 $meta_query['relation'] = $filter['values_relation'];
766 }
767 break;
768 }
769 }
770
771 if ( 1 < count( $tax_query ) ) {
772 $args['tax_query'] = $tax_query;
773 }
774 if ( 1 < count( $meta_query ) ) {
775 $args['meta_query'] = $meta_query;
776 }
777
778 return apply_filters( 'wpupg_query_post_args', $args, $this, $grid_args );
779 }
780
781 /**
782 * Get all the IDs that are displayed in this grid.
783 *
784 * @since 3.0.0
785 * @param mixed $grid_args Optional arguments.
786 */
787 public function all_ids( $grid_args = array() ) {
788 if ( false === $this->ids ) {
789 $ids = array();
790 $total_ids = false;
791
792 if ( 'posts' === $this->type() ) {
793 $args = $this->all_ids_query_post_args( $grid_args );
794
795 // Query IDs.
796 $query = new WP_Query( $args );
797 $posts = $query->have_posts() ? $query->posts : array();
798 $ids = array_map( 'intval', $posts );
799 $total_ids = $query->found_posts;
800
801 // Offset posts
802 if ( $this->limit_posts_offset() ) {
803 $ids = array_slice( $ids, $this->limit_posts_offset() );
804 }
805
806 // Limit Total # Posts
807 if ( $this->limit_posts_number() ) {
808 $ids = array_slice( $ids, 0, $this->limit_posts_number() );
809 }
810 }
811
812 $this->ids = apply_filters( 'wpupg_grid_all_ids', $ids, $this, $grid_args );
813 $this->total_ids = $total_ids;
814 }
815
816 return $this->ids;
817 }
818
819 /**
820 * Get the terms to display in this grid.
821 *
822 * @since 3.0.0
823 * @param mixed $grid_args Optional arguments.
824 */
825 public function terms( $grid_args = array() ) {
826 if ( false === $this->terms ) {
827 $terms = array(
828 'per_item' => array(),
829 'per_taxonomy' => array(),
830 );
831 $taxonomies = $this->filters_taxonomies();
832
833 // Only need to get terms if there actually are taxonomies.
834 if ( count( $taxonomies ) ) {
835 $post_ids = $this->all_ids( $grid_args );
836
837 // Build sanitized query.
838 global $wpdb;
839
840 $where_post_ids = implode( ',', array_map( 'intval', $post_ids ) );
841 $where_taxonomies = implode( '","', array_map( 'sanitize_key', $taxonomies ) );
842
843 $query = 'SELECT tr.object_id, tt.taxonomy, tt.parent, t.term_id, t.slug, t.name FROM wp_term_relationships tr JOIN wp_term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id JOIN wp_terms t on tt.term_id = t.term_id WHERE tt.taxonomy IN ("' . $where_taxonomies . '") AND tr.object_id IN (' . $where_post_ids . ')';
844
845 $results = $wpdb->get_results( $query, ARRAY_A );
846
847
848 // Loop over all results.
849 $terms = $this->handle_terms( $terms, $results );
850 }
851
852 $this->terms = $terms;
853 }
854
855 return $this->terms;
856 }
857
858 /**
859 * Get terms from query reults.
860 *
861 * @since 3.5.0
862 */
863 public function handle_terms( $terms, $results, $depth = 0 ) {
864 $parents = array();
865
866 foreach ( $results as $result ) {
867 $item_id = intval( $result['object_id'] );
868 $taxonomy = $result['taxonomy'];
869 $term_id = intval( $result['term_id'] );
870 $parent = intval( $result['parent'] );
871 $slug = $result['slug'];
872 $name = $result['name'];
873
874 // Make sure arrays exist.
875 if ( ! isset( $terms['per_taxonomy'][ $taxonomy ] ) ) { $terms['per_taxonomy'][ $taxonomy ] = array(); }
876 if ( ! isset( $terms['per_item'][ $item_id ] ) ) { $terms['per_item'][ $item_id ] = array(); }
877 if ( ! isset( $terms['per_item'][ $item_id ][ $taxonomy ] ) ) {
878 $terms['per_item'][ $item_id ][ $taxonomy ] = array(
879 'terms' => array(),
880 'parent_terms' => array(),
881 );
882 }
883 if ( !isset( $terms['per_taxonomy'][ $taxonomy ][ $slug ] ) ) {
884 $terms['per_taxonomy'][ $taxonomy ][ $slug ] = array(
885 'id' => $term_id,
886 'parent' => $parent,
887 'name' => $name,
888 'posts' => array(),
889 'child_posts' => array(),
890 );
891 }
892
893 // Add in correct category.
894 if ( 0 === $depth ) {
895 $terms['per_item'][ $item_id ][ $taxonomy ]['terms'][] = $slug;
896 $terms['per_taxonomy'][ $taxonomy ][ $slug ]['posts'][] = $item_id;
897 } else {
898 $terms['per_item'][ $item_id ][ $taxonomy ]['parent_terms'][] = $slug;
899 $terms['per_taxonomy'][ $taxonomy ][ $slug ]['child_posts'][] = $item_id;
900 }
901
902 // Check if there are further parents we need to get.
903 if ( $parent ) {
904 $parents[] = array(
905 'object_id' => $item_id,
906 'taxonomy' => $taxonomy,
907 'term_id' => $parent,
908 );
909 }
910 }
911
912 // Check if there are any parents we need to associate as well.
913 if ( count( $parents ) ) {
914 $parent_results = array();
915
916 $parent_ids = wp_list_pluck( $parents, 'term_id' );
917 $parent_terms = get_terms( array(
918 'include' => $parent_ids,
919 ) );
920
921 if ( $parent_terms && ! is_wp_error( $parent_terms ) ) {
922 // Set term_id as key.
923 $keyed_parent_terms = array();
924 foreach ( $parent_terms as $parent_term ) {
925 $keyed_parent_terms[ $parent_term->term_id ] = $parent_term;
926 }
927
928 foreach( $parents as $index => $parent ) {
929 if ( isset( $keyed_parent_terms[ $parent['term_id'] ] ) ) {
930 $parent_term = $keyed_parent_terms[ $parent['term_id'] ];
931
932 $parent_results[] = array(
933 'object_id' => $parent['object_id'],
934 'taxonomy' => $parent['taxonomy'],
935 'term_id' => $parent['term_id'],
936 'parent' => $parent_term->parent,
937 'slug' => $parent_term->slug,
938 'name' => $parent_term->name,
939 );
940 }
941 }
942 }
943
944 // Recursive loop.
945 if ( count( $parent_results ) ) {
946 $terms = $this->handle_terms( $terms, $parent_results, $depth + 1 );
947 }
948 }
949
950 return $terms;
951 }
952
953 /**
954 * Get the terms for a specific item.
955 *
956 * @since 3.0.0
957 * @param int $item_id Item to get the terms for.
958 */
959 public function get_terms_for_item( $item_id ) {
960 $terms = $this->terms();
961
962 return isset( $terms['per_item'][ $item_id ] ) ? $terms['per_item'][ $item_id ] : array();
963 }
964
965 /**
966 * Get the terms for a specific taxonomy.
967 *
968 * @since 3.0.0
969 * @param string $taxonomy Taxonomy to get the terms for.
970 */
971 public function get_terms_for_taxonomy( $taxonomy ) {
972 $terms = $this->terms();
973
974 return isset( $terms['per_taxonomy'][ $taxonomy ] ) ? $terms['per_taxonomy'][ $taxonomy ] : array();
975 }
976
977 /**
978 * Get the taxonomies filtered by in this grid.
979 *
980 * @since 3.0.0
981 */
982 public function filters_taxonomies() {
983 $taxonomies = array();
984
985 if ( $this->filters_enabled() ) {
986 $filters = $this->filters();
987
988 foreach ( $filters as $filter ) {
989 if ( ! isset( $filter['options']['source'] ) || 'taxonomies' === $filter['options']['source'] ) {
990 if ( isset( $filter['options']['taxonomies'] ) ) {
991 $taxonomies = array_merge( $taxonomies, $filter['options']['taxonomies'] );
992 }
993 }
994 }
995 }
996
997 return array_unique( $taxonomies );
998 }
999
1000 /**
1001 * Get the custom fields filtered by in this grid.
1002 *
1003 * @since 3.3.0
1004 */
1005 public function filters_custom_fields() {
1006 $custom_fields = array();
1007
1008 if ( $this->filters_enabled() ) {
1009 $filters = $this->filters();
1010
1011 foreach ( $filters as $filter ) {
1012 if ( ! isset( $filter['options']['source'] ) || 'custom_field' === $filter['options']['source'] ) {
1013 if ( isset( $filter['options']['custom_field'] ) ) {
1014 $custom_fields[] = $filter['options']['custom_field'];
1015 }
1016 }
1017 }
1018 }
1019
1020 return array_unique( $custom_fields );
1021 }
1022 }
1023