PluginProbe
Post Grid Gutenberg Blocks – PostX / 5.1.1
Post Grid Gutenberg Blocks – PostX v5.1.1
5.1.1 5.1.0 5.0.41 5.0.39 5.0.38 5.0.37 5.0.36 5.0.35 5.0.34 5.0.33 5.0.32 5.0.31 5.0.30 5.0.29 5.0.28 5.0.27 5.0.26 5.0.25 5.0.23 5.0.24 5.0.22 5.0.21 5.0.20 5.0.19 5.0.18 All 240 releases
ultimate-post / blocks / Advanced_Filter.php

Advanced_Filter.php in Post Grid Gutenberg Blocks – PostX 5.1.1, at blocks/Advanced_Filter.php

677 lines 19.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace ULTP\blocks;
3
4 use WP_Taxonomy;
5
6 defined( 'ABSPATH' ) || exit;
7
8 /**
9 * Class Advanced_Filter
10 */
11 class Advanced_Filter {
12 /**
13 * Select filter types rendered by PostX itself.
14 *
15 * Further types are added through the `ultp_advanced_filter_select_types` filter.
16 *
17 * @var array
18 */
19 private $select_types = array(
20 'category',
21 'tags',
22 'author',
23 'order',
24 'order_by',
25 );
26
27 /**
28 * Order Value for select
29 *
30 * @var array
31 */
32 public function get_order_options() {
33 return array(
34 array(
35 'id' => 'DESC',
36 'name' => __( 'DESC', 'ultimate-post' ),
37 ),
38 array(
39 'id' => 'ASC',
40 'name' => __( 'ASC', 'ultimate-post' ),
41 ),
42 );
43 }
44
45 /**
46 * Order by Value for select
47 *
48 * @var array
49 */
50 public $order_by = array(
51 array(
52 'id' => 'date',
53 'name' => 'Created Date',
54 ),
55 array(
56 'id' => 'modified',
57 'name' => 'Date Modified',
58 ),
59 array(
60 'id' => 'title',
61 'name' => 'Title',
62 ),
63 array(
64 'id' => 'menu_order',
65 'name' => 'Menu Order',
66 ),
67 array(
68 'id' => 'rand',
69 'name' => 'Random',
70 ),
71 // array(
72 // 'id' => 'post__in',
73 // 'name' => 'Post In',
74 // ),
75 array(
76 'id' => 'comment_count',
77 'name' => 'Number of Comments',
78 ),
79 );
80
81 /**
82 * Filter_select_options
83 *
84 * @param MIXED | $options (ARRAY).
85 * @param mixed | $filter (ARRAY).
86 * @return array
87 */
88 private function filter_select_options( $options, $filter ) {
89 $res = array();
90 $map = array();
91
92 foreach ( $options as $option ) {
93 $map[ $option['id'] ] = $option['name'];
94 }
95
96 foreach ( $filter as $f ) {
97
98 if ( isset( $map[ $f ] ) ) {
99 $res[] = array(
100 'id' => strtolower( $f ),
101 'name' => $map[ $f ],
102 );
103 }
104 }
105
106 return $res;
107 }
108
109 /**
110 * Advanced_Filter constructor.
111 */
112 public function __construct() {
113 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
114 add_action( 'init', array( $this, 'register' ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
115 }
116
117 /**
118 * Get_select_attributes
119 *
120 * @return array
121 */
122 public function get_select_attributes() {
123 return array(
124 'advanceId' => '',
125 'blockId' => '',
126 'advanceCss' => '',
127 'filterStyle' => 'dropdown',
128 'filterValues' => '["_all"]',
129 'dropdownOptionsType' => 'all',
130 'dropdownOptions' => '["_all"]',
131 'allText' => 'All',
132 'sPlaceholderText' => 'Search...',
133 'searchEnabled' => false,
134 'inlineMultiSelect' => true,
135 );
136 }
137
138 /**
139 * Get_clear_attributes
140 *
141 * @return array
142 */
143 public function get_clear_attributes() {
144 return array(
145 'advanceId' => '',
146 'blockId' => '',
147 'clearButtonText' => 'Clear Filters',
148 'clearButtonPosition' => 'normal',
149 'cAlign' => 'margin-bottom: inherit',
150 );
151 }
152
153 public function register() {
154 register_block_type(
155 'ultimate-post/filter-select',
156 array(
157 'editor_script' => 'ultp-blocks-editor-script',
158 'editor_style' => 'ultp-blocks-editor-css',
159 'render_callback' => array( $this, 'select_content' ),
160 )
161 );
162
163 register_block_type(
164 'ultimate-post/filter-search-adv',
165 array(
166 'editor_script' => 'ultp-blocks-editor-script',
167 'editor_style' => 'ultp-blocks-editor-css',
168 'render_callback' => array( $this, 'search_content' ),
169 )
170 );
171
172 register_block_type(
173 'ultimate-post/filter-clear',
174 array(
175 'editor_script' => 'ultp-blocks-editor-script',
176 'editor_style' => 'ultp-blocks-editor-css',
177 'render_callback' => array( $this, 'clear_content' ),
178 )
179 );
180 }
181
182 /**
183 * Get_button_data
184 *
185 * @param mixed $type string.
186 * @param mixed $ids string.
187 * @param mixed $post_types string.
188 * @param mixed $allText string.
189 * @param mixed $option string.
190 * @param mixed $order array.
191 * @return array
192 */
193 public function get_button_data( $type, $data_ids, $post_types = '', $allText = 'All', $option = '', $order = array() ) {
194
195 $res = array();
196
197 if ( in_array( '_all', $data_ids ) ) {
198 $res['_all'] = $allText;
199 // $ids = array_filter($ids, function ($item) {
200 // return $item != '_all';
201 // });
202 }
203
204 $ids = implode( ',', $data_ids );
205
206 switch ( $type ) {
207 case 'category':
208 $args = array(
209 'taxonomy' => 'category',
210 'hide_empty' => false,
211 );
212
213 if ( 'all' !== $option && ! empty( $ids ) && '_all' !== $ids ) {
214 $args['include'] = wp_parse_id_list( $ids );
215 $args['orderby'] = 'include';
216 }
217
218 $categories = get_terms( $args );
219
220 if ( ! is_wp_error( $categories ) && ! empty( $categories ) ) {
221 foreach ( $categories as $category ) {
222 $res['_all'] = $allText;
223 $res[ $category->slug ] = $category->name;
224 }
225 }
226 break;
227 case 'tags':
228 $args = array(
229 'taxonomy' => 'post_tag',
230 'hide_empty' => false,
231 );
232 if ( 'all' !== $option && ! empty( $ids ) && $ids !== '_all' ) {
233 $args['include'] = wp_parse_id_list( $ids );
234 $args['orderby'] = 'include';
235 }
236
237 $tags = get_terms( $args );
238 if ( ! is_wp_error( $tags ) && ! empty( $tags ) ) {
239 foreach ( $tags as $tag ) {
240 $res[ $tag->slug ] = $tag->name;
241 }
242 }
243 break;
244 case 'author':
245 if ( ! empty( $ids ) && $ids !== '_all' ) {
246 $authors = get_users(
247 array(
248 'per_page' => -1,
249 'role__in' => array( 'administrator', 'editor', 'author', 'contributor' ),
250 'include' => $ids,
251 )
252 );
253
254 foreach ( $authors as $author ) {
255 $res[ $author->ID ] = $author->display_name;
256 }
257 }
258
259 break;
260 case 'order':
261 foreach ( $this->order as $order ) {
262 $res[ $order['id'] ] = $order['name'];
263 }
264 break;
265 case 'order_by':
266 $orders = explode( ',', $ids );
267 foreach ( $this->order_by as $order_by ) {
268 foreach ( $orders as $order ) {
269 if ( $order_by['id'] === $order ) {
270 $res[ $order ] = $order_by['name'];
271 }
272 }
273 }
274 break;
275 default:
276 /**
277 * Inline (button) options for a select filter type PostX does not render itself.
278 *
279 * @param array $res Options keyed by value. Holds '_all' when that button is enabled.
280 * @param string $type Filter type.
281 * @param array $data_ids Values chosen in the block settings.
282 * @param array|string $post_types Post types the filter applies to, or ''.
283 * @param string $option 'all' or 'specific'.
284 */
285 $res = apply_filters( 'ultp_advanced_filter_button_data', $res, $type, $data_ids, $post_types, $option );
286 break;
287 }
288
289 return is_array( $res ) ? $res : array();
290 }
291
292 /**
293 * Inserts all option depending on condition
294 *
295 * @param array $arr array.
296 * @param array $item all item.
297 * @param int $idx all item idx.
298 * @param string $mode mode.
299 * @return void
300 */
301 private function maybe_insert_all_option( &$arr, &$item, $idx, $mode ) {
302 if ( 'all' === $mode ) {
303 array_unshift( $arr, $item );
304 } elseif ( ! empty( $item ) && false !== $idx ) {
305 array_splice( $arr, $idx, 0, array( $item ) );
306 }
307 }
308
309 public function get_select_data( $type, $all_text, $post_types = '', $specific = array(), $mode = 'all', $order = array() ) {
310 $all = null;
311 $all_idx = array_search( '_all', $specific, true );
312 $filtered_specific = $specific;
313
314 if ( false !== $all_idx || 'all' === $mode ) {
315 $all = array(
316 'id' => '_all',
317 'name' => $all_text,
318 );
319
320 $filtered_specific = array_filter(
321 $specific,
322 function ( $item ) {
323 return '_all' !== $item;
324 }
325 );
326 }
327
328 switch ( $type ) {
329
330 case 'category':
331 $categories = array();
332
333 if ( 'all' === $mode || ( 'specific' === $mode && count( $filtered_specific ) > 0 ) ) {
334 $categories = get_categories(
335 array(
336 'per_page' => -1,
337
338 'include' => $filtered_specific,
339 'orderby' => 'include',
340 )
341 );
342
343 $categories = array_map(
344 function ( $category ) {
345 return array(
346 'id' => $category->slug,
347 'name' => $category->name,
348 );
349 },
350 $categories
351 );
352 }
353
354 $this->maybe_insert_all_option( $categories, $all, $all_idx, $mode );
355
356 return $categories;
357
358 case 'tags':
359 $tags = array();
360
361 if ( 'all' === $mode || ( 'specific' === $mode && count( $filtered_specific ) > 0 ) ) {
362 $tags = get_tags(
363 array(
364 'per_page' => -1,
365 'include' => $filtered_specific,
366 )
367 );
368 $tags = array_map(
369 function ( \WP_Term $tag ) {
370 return array(
371 'id' => $tag->slug,
372 'name' => $tag->name,
373 );
374 },
375 $tags
376 );
377 }
378
379 $this->maybe_insert_all_option( $tags, $all, $all_idx, $mode );
380
381 return $tags;
382
383 case 'author':
384 $authors = array();
385
386 if ( 'all' === $mode || ( 'specific' === $mode && count( $filtered_specific ) > 0 ) ) {
387
388 $authors = get_users(
389 array(
390 'per_page' => -1,
391 'include' => $filtered_specific,
392 'role__not_in' => array( 'subscriber', 'translator' ),
393 )
394 );
395
396 $authors = array_map(
397 function ( $author ) {
398 return array(
399 'id' => $author->ID,
400 'name' => $author->display_name,
401 );
402 },
403 $authors
404 );
405 }
406
407 $this->maybe_insert_all_option( $authors, $all, $all_idx, $mode );
408
409 return $authors;
410
411 case 'order':
412 $filtered_specific = array_map( 'strtoupper', $filtered_specific );
413
414 return 'specific' === $mode ?
415 $this->filter_select_options( $order, $filtered_specific )
416 : $order;
417
418 case 'order_by':
419 return 'specific' === $mode ?
420 $this->filter_select_options( $this->order_by, $filtered_specific )
421 : $this->order_by;
422
423 default:
424 /**
425 * Dropdown options for a select filter type PostX does not render itself.
426 *
427 * Return a list of array( 'id' => ..., 'name' => ... ) items, without the
428 * '_all' item: PostX inserts that. Return null to render no options at all.
429 *
430 * @param array|null $data Options. Null when no callback handles the type.
431 * @param string $type Filter type.
432 * @param array|string $post_types Post types the filter applies to, or ''.
433 * @param array $specific Values chosen in the block settings, without '_all'.
434 * @param string $mode 'all' or 'specific'.
435 */
436 $data = apply_filters( 'ultp_advanced_filter_select_data', null, $type, $post_types, $filtered_specific, $mode );
437
438 if ( ! is_array( $data ) ) {
439 return array();
440 }
441
442 $this->maybe_insert_all_option( $data, $all, $all_idx, $mode );
443
444 return $data;
445 }
446 }
447
448 public function select_content( $attr ) {
449 $block_name = 'filter-select';
450 $attr = wp_parse_args( $attr, $this->get_select_attributes() );
451
452 /**
453 * Select filter types that render on the front end.
454 *
455 * @param array $types Types PostX renders itself.
456 */
457 $types = apply_filters( 'ultp_advanced_filter_select_types', $this->select_types );
458
459 if ( ! empty( $attr['type'] ) && ! in_array( $attr['type'], (array) $types, true ) ) {
460 return '';
461 }
462
463 $post_types = isset( $attr['postTypes'] ) ? $attr['postTypes'] : '';
464
465 if ( is_string( $post_types ) ) {
466 $post_types = json_decode( $post_types, true );
467 }
468
469 if ( is_array( $post_types ) && in_array( 'customPostType', $post_types, true ) ) {
470 $post_types = json_decode( $attr['queryPostType'][0] ?? '[]', true );
471 if ( is_string( $post_types ) ) {
472 $post_types = json_decode( $post_types, true );
473 }
474 }
475
476 $attr['blockId'] = ultimate_post()->sanitize_attr( $attr, 'blockId', 'sanitize_html_class', 'missing_block_id' );
477 $attr['allText'] = ultimate_post()->sanitize_attr( $attr, 'allText', 'sanitize_text_field', 'missing_block_id' );
478 $current_page_post_id = ( isset( $attr['currentPostId'] ) && $attr['currentPostId'] ) ? sanitize_html_class( $attr['currentPostId'] ) : ultimate_post()->get_page_post_id( $attr['blockId'] );
479
480 if ( 'inline' === $attr['filterStyle'] ) {
481 $inline_values = json_decode( $attr['filterValues'], true );
482
483 if ( ! is_array( $inline_values ) ) {
484 return '';
485 }
486
487 $data = $this->get_button_data( $attr['type'], $inline_values, $post_types, $attr['allText'], $attr['dropdownOptionsType'], $this->get_order_options() );
488
489 $btn_wrapper_attrs = get_block_wrapper_attributes(
490 array(
491 'class' => 'ultp-block-' . $attr['blockId'] . ' ultp-filter-button',
492 'role' => 'button',
493 'data-blockId' => $attr['blockId'],
494 'data-is-active' => 'false',
495 'data-builder' => ultimate_post()->get_builder_attr( 'archiveBuilderFilter' ),
496 'data-current-postid' => $current_page_post_id,
497 'data-filter-label' => isset( $attr['allText'] ) ? $attr['allText'] : 'All',
498 )
499 );
500
501 ob_start();
502 ?>
503 <div class="ultp-block-<?php echo esc_attr( $attr['blockId'] ); ?>-wrapper" data-multiselect="<?php echo isset( $attr['inlineMultiSelect'] ) && $attr['inlineMultiSelect'] ? 'true' : 'false'; ?>" >
504
505 <?php
506
507 foreach ( $data as $key => $value ) :
508 ?>
509 <?php
510 if ( is_array( $value ) ) {
511 $name = $value['name'];
512 $tax = isset( $value['taxonomy'] ) ? $value['taxonomy'] : null;
513 } else {
514 $name = $value;
515 $tax = null;
516 }
517 ?>
518 <div <?php echo $btn_wrapper_attrs; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- get_block_wrapper_attributes() returns pre-escaped attribute HTML; escaping it again would corrupt it. ?> <?php echo null !== $tax ? 'data-tax="' . esc_attr( $tax ) . '"' : ''; ?> data-selected="<?php echo esc_attr( $key ); ?>" data-type="<?php echo esc_attr( $attr['type'] ); ?>">
519 <?php echo esc_html( $name ); ?>
520 </div>
521 <?php endforeach ?>
522 </div>
523 <?php
524
525 $content = ob_get_clean();
526 return $content;
527 } elseif ( 'dropdown' === $attr['filterStyle'] ) {
528
529 $mode = 'all';
530 $specific = array();
531
532 if ( 'specific' === $attr['dropdownOptionsType'] && isset( $attr['dropdownOptions'] ) ) {
533 $specific_data = json_decode( stripslashes( $attr['dropdownOptions'] ) );
534 $mode = 'specific';
535 if ( is_array( $specific_data ) ) {
536 $specific = array_map(
537 function ( $item ) {
538 return sanitize_text_field( $item );
539 },
540 $specific_data
541 );
542 }
543 }
544
545 $data = $this->get_select_data( $attr['type'], $attr['allText'], $post_types, $specific, $mode, $this->get_order_options() );
546
547 $def_value = ! empty( current( $data ) ) ? current( $data ) : null;
548
549 $wrapper_attrs = get_block_wrapper_attributes(
550 array(
551 'class' => 'ultp-block-' . $attr['blockId'] . ' ultp-filter-select',
552 'data-selected' => isset( $def_value['id'] ) ? $def_value['id'] : 0,
553 'data-type' => $attr['type'],
554 'data-blockId' => $attr['blockId'],
555 'aria-expanded' => 'false',
556 'aria-label' => 'Select Filter (' . $attr['type'] . ')',
557 'data-builder' => ultimate_post()->get_builder_attr( 'archiveBuilderFilter' ),
558 'data-current-postid' => $current_page_post_id,
559 'data-filter-label' => isset( $def_value['name'] ) ? $def_value['name'] : 'All',
560 )
561 );
562
563 ob_start();
564 ?>
565 <div <?php echo $wrapper_attrs; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- get_block_wrapper_attributes() returns pre-escaped attribute HTML; escaping it again would corrupt it. ?>>
566 <div class="ultp-filter-select-field ultp-filter-select__parent">
567 <span class="ultp-filter-select-field-selected ultp-filter-select__parent-inner">
568 <?php echo esc_html( isset( $def_value['name'] ) ? $def_value['name'] : '' ); ?>
569 </span>
570 <span class="ultp-filter-select-field-icon">
571 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 34.1 19.95"><path d="M17.05 19.949.601 3.499a2.05 2.05 0 0 1 2.9-2.9l13.551 13.55L30.603.599a2.05 2.05 0 0 1 2.9 2.9Z"/></svg>
572 </span>
573 </div>
574 <ul style="display: none;" class="ultp-filter-select-options ultp-filter-select__dropdown">
575
576 <?php
577 if ( isset( $attr['searchEnabled'] ) && $attr['searchEnabled'] ) :
578 ?>
579 <input
580 type="search"
581 class="ultp-filter-select-search"
582 placeholder="<?php echo esc_attr( isset( $attr['sPlaceholderText'] ) ? $attr['sPlaceholderText'] : '' ); ?>"
583 />
584 <?php endif; ?>
585
586 <?php foreach ( $data as $item ) : ?>
587 <li class="ultp-filter-select__dropdown-inner" <?php echo isset( $item['taxonomy'] ) ? 'data-tax="' . esc_attr( $item['taxonomy'] ) . '"' : ''; ?> data-id="<?php echo esc_attr( $item['id'] ); ?>">
588 <?php echo esc_html( $item['name'] ); ?>
589 </li>
590 <?php endforeach; ?>
591
592 </ul>
593 </div>
594
595 <?php
596 $content = ob_get_clean();
597 return $content;
598 }
599
600 return '';
601 }
602
603 /**
604 * Search_content
605 *
606 * The search filter is rendered by PostX Pro through the
607 * `ultp_advanced_filter_search_content` filter; PostX itself outputs nothing.
608 *
609 * @param mixed $attr array.
610 * @return string
611 */
612 public function search_content( $attr ) {
613 /**
614 * Front-end markup for the Advanced Filter search block.
615 *
616 * @param string $content Markup. Empty by default.
617 * @param array $attr Block attributes, unsanitized.
618 */
619 $content = apply_filters( 'ultp_advanced_filter_search_content', '', (array) $attr );
620
621 return is_string( $content ) ? $content : '';
622 }
623
624 /**
625 * Clear_content
626 *
627 * @param mixed $attr array.
628 * @return array
629 */
630 public function clear_content( $attr ) {
631 $block_name = 'filter-clear';
632 $attr = wp_parse_args( $attr, $this->get_clear_attributes() );
633
634 $attr['blockId'] = ultimate_post()->sanitize_attr( $attr, 'blockId', 'sanitize_html_class', 'missing_block_id' );
635 $attr['clearButtonText'] = ultimate_post()->sanitize_attr( $attr, 'clearButtonText', 'sanitize_text_field' );
636
637 $wrapper_attrs = get_block_wrapper_attributes(
638 array(
639 'class' => 'ultp-block-' . $attr['blockId'] . ' ultp-filter-clear ultp-filter-clear-button ',
640 'data-blockid' => $attr['blockId'],
641 )
642 );
643
644 $selected_filter_wrapper_attr = get_block_wrapper_attributes(
645 array(
646 'class' => 'ultp-block-' . $attr['blockId'] . ' ultp-filter-clear ultp-filter-clear-template',
647 'style' => 'display: none;',
648 )
649 );
650
651 ob_start();
652 ?>
653
654 <div class="ultp-block-<?php echo sanitize_html_class( $attr['blockId'] ); ?>-wrapper">
655 <div <?php echo $selected_filter_wrapper_attr; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- get_block_wrapper_attributes() returns pre-escaped attribute HTML; escaping it again would corrupt it. ?>>
656 <div class="ultp-selected-filter">
657 <span class="ultp-selected-filter-icon" role="button">
658 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 31.1 31.25"><path stroke="rgba(0,0,0,0)" strokeMiterlimit="10" d="M27.1 30.153 15.549 18.601 4 30.153a2.05 2.05 0 0 1-2.9-2.9l11.551-11.55L1.1 4.153a2.05 2.05 0 0 1 2.9-2.9l11.549 11.552L27.1 1.253a2.05 2.05 0 0 1 2.9 2.9l-11.553 11.55L30 27.253a2.05 2.05 0 1 1-2.9 2.9Z"/></svg>
659 </span>
660 <span class="ultp-selected-filter-text">
661 </span>
662 </div>
663 </div>
664
665 <div <?php echo $wrapper_attrs; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- get_block_wrapper_attributes() returns pre-escaped attribute HTML; escaping it again would corrupt it. ?>>
666 <button class="ultp-clear-button">
667 <?php echo esc_html( $attr['clearButtonText'] ); ?>
668 </button>
669 </div>
670 </div>
671
672 <?php
673 $content = ob_get_clean();
674 return $content;
675 }
676 }
677