PluginProbe
Post Grid Gutenberg Blocks – PostX / 5.1.0
Post Grid Gutenberg Blocks – PostX v5.1.0
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 / classes / Blocks.php

Blocks.php in Post Grid Gutenberg Blocks – PostX 5.1.0, at classes/Blocks.php

817 lines 36.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // phpcs:ignore
3 // @codingStandardsIgnoreFile
4 // @ignoreWarnings
5 /**
6 * Require All Blocks and handle block ajax action
7 *
8 * @package ULTP\Blocks
9 * @since 4.1.11
10 */
11
12 namespace ULTP;
13 defined( 'ABSPATH' ) || exit;
14
15 /**
16 * Blocks class.
17 */
18 class Blocks {
19
20 /**
21 * Setup class.
22 *
23 * Registers AJAX actions and includes all block classes.
24 *
25 * @since 4.1.11
26 */
27 public function __construct() {
28 $this->include_all_blocks(); // Include Blocks .
29 add_action( 'wp_ajax_ultp_next_prev', array( $this, 'ultp_next_prev_callback' ) ); // Next Previous AJAX Call .
30 add_action( 'wp_ajax_nopriv_ultp_next_prev', array( $this, 'ultp_next_prev_callback' ) ); // Next Previous AJAX Call Logout User .
31
32 add_action( 'wp_ajax_ultp_filter', array( $this, 'ultp_filter_callback' ) ); // Next Previous AJAX Call .
33 add_action( 'wp_ajax_nopriv_ultp_filter', array( $this, 'ultp_filter_callback' ) ); // Next Previous AJAX Call Logout User .
34
35 add_action( 'wp_ajax_ultp_adv_filter', array( $this, 'ultp_adv_filter_callback' ) );
36 add_action( 'wp_ajax_nopriv_ultp_adv_filter', array( $this, 'ultp_adv_filter_callback' ) );
37
38 add_action( 'wp_ajax_ultp_pagination', array( $this, 'ultp_pagination_callback' ) ); // Page Number AJAX Call .
39 add_action( 'wp_ajax_nopriv_ultp_pagination', array( $this, 'ultp_pagination_callback' ) ); // Page Number AJAX Call Logout User .
40
41 add_action( 'wp_ajax_ultp_share_count', array( $this, 'ultp_share_count_callback' ) ); // share Count save .
42 add_action( 'wp_ajax_nopriv_ultp_share_count', array( $this, 'ultp_share_count_callback' ) ); // share Count save .
43 // Public by design — see ultp_get_nonce_callback() for why, and for what
44 // 'ultp-public-nonce' does and does not protect.
45 add_action( 'wp_ajax_ultp_get_nonce', array( $this, 'ultp_get_nonce_callback' ) );
46 add_action( 'wp_ajax_nopriv_ultp_get_nonce', array( $this, 'ultp_get_nonce_callback' ) );
47 }
48
49 /**
50 * Require Blocks
51 *
52 * @since v.1.0.0
53 * @return NULL
54 */
55 public function include_all_blocks() {
56 spl_autoload_register(
57 function ( $class ) {
58 if ( strpos( $class, 'ULTP\blocks' ) === 0 ) {
59 $source = ULTP_PATH . 'blocks/' . explode( '\\', $class )[2] . '.php';
60 if ( file_exists( $source ) ) {
61 include_once $source;
62 } else {
63 $source = ULTP_PATH . 'addons/builder/blocks/' . explode( '\\', $class )[2] . '.php';
64 if ( file_exists( $source ) ) {
65 include_once $source;
66 }
67 }
68 }
69 }
70 );
71
72 $request = isset( $_POST['action'] ) ? sanitize_text_field( $_POST['action'] ) : '';
73 $get_action = isset( $_GET['action'] ) ? sanitize_text_field( $_GET['action'] ) : '';
74
75 if (
76 is_admin() &&
77 $request != 'et_fb_ajax_render_shortcode' && // Divi Module Check .
78 $get_action != 'elementor' && // Elementor Widget Check .
79 $request != 'elementor_ajax' // Elementor Widget Check .
80 ) {
81 return;
82 }
83
84 $settings = ultimate_post()->get_setting();
85 $blocks = array(
86 'post_list_1' => 'Post_List_1',
87 'post_list_2' => 'Post_List_2',
88 'post_list_3' => 'Post_List_3',
89 'post_list_4' => 'Post_List_4',
90 'post_grid_1' => 'Post_Grid_1',
91 'post_grid_2' => 'Post_Grid_2',
92 'post_grid_3' => 'Post_Grid_3',
93 'post_grid_4' => 'Post_Grid_4',
94 'post_grid_5' => 'Post_Grid_5',
95 'post_grid_6' => 'Post_Grid_6',
96 'post_grid_7' => 'Post_Grid_7',
97 'post_slider_1' => 'Post_Slider_1',
98 'post_slider_2' => 'Post_Slider_2',
99 'post_module_1' => 'Post_Module_1',
100 'post_module_2' => 'Post_Module_2',
101 'heading' => 'Heading',
102 'image' => 'Image',
103 'taxonomy' => 'Taxonomy',
104 'news_ticker' => 'News_Ticker',
105 'advanced_search' => 'Advanced_Search',
106 'advanced_filter' => 'Advanced_Filter',
107 'dark_Light' => 'Dark_Light',
108 'advanced_list' => 'Advanced_List',
109 'button' => 'Button',
110 'youtube_gallery' => 'Youtube_Gallery',
111 );
112
113 foreach ( $blocks as $id => $block ) {
114 if ( isset( $settings[ $id ] ) && $settings[ $id ] != 'yes' ) {
115 } else {
116 $obj = '\ULTP\blocks\\' . $block;
117 new $obj();
118 }
119 }
120
121 if ( isset( $settings['ultp_builder'] ) && $settings['ultp_builder'] == 'true' ) {
122 $builder_blocks = array(
123 'builder_archive_title' => 'Archive_Title',
124 'builder_post_title' => 'Post_Title',
125 'builder_post_content' => 'Post_Content',
126 'builder_post_featured_image' => 'Post_Featured_Image',
127 'builder_post_breadcrumb' => 'Post_Breadcrumb',
128 'builder_post_tag' => 'Post_Tag',
129 'builder_post_category' => 'Post_Category',
130 'builder_post_next_previous' => 'Next_Previous',
131 'builder_post_excerpt' => 'Post_Excerpt',
132 'builder_author_box' => 'Author_Box',
133 'builder_post_comments' => 'Post_Comments',
134 'builder_post_view_count' => 'Post_View_Count',
135 'builder_post_reading_time' => 'Post_Reading_Time',
136 'builder_post_comment_count' => 'Post_Comment_Count',
137 'builder_post_author_meta' => 'Post_Author_Meta',
138 'builder_post_date_meta' => 'Post_Date_Meta',
139 'builder_post_social_share' => 'Post_Social_Share',
140 'builder_advance_post_meta' => 'Advance_Post_Meta',
141 );
142 foreach ( $builder_blocks as $id => $block ) {
143 if ( isset( $settings[ $id ] ) && $settings[ $id ] != 'yes' ) {
144 } else {
145 $obj = '\ULTP\blocks\\' . $block;
146 new $obj();
147 }
148 }
149 }
150 }
151
152
153 /**
154 * Blocks Content Start.
155 *
156 * @since v.1.0.0
157 *
158 * @param ARRAY $blocks The blocks array.
159 * @param STRING $paged The current page number.
160 * @param STRING $blockId The block ID.
161 * @param STRING $blockRaw The raw block name.
162 * @param STRING $blockName The block name.
163 * @param STRING $builder The builder flag.
164 * @param STRING $postId The post ID.
165 * @param STRING|ARRAY $filterValue The filter value.
166 * @param STRING $filterType The filter type.
167 * @param ARRAY $ultp_uniqueIds Unique IDs for pagination.
168 * @param ARRAY $ultp_current_unique_posts Current unique posts.
169 * @param STRING $widgetBlockId The widget block ID.
170 * @param STRING $exclude_post_id The post ID to exclude.
171 * @param ARRAY $adv_filter_data Advanced filter data.
172 * @return STRING The AJAX response.
173 */
174 public function pagination_content_return( $blocks, $paged, $blockId, $blockRaw, $blockName, $builder, $postId, $filterValue, $filterType, $ultp_uniqueIds = array(), $ultp_current_unique_posts = array(), $widgetBlockId = '', $exclude_post_id = '', $adv_filter_data = array() ) {
175 foreach ( $blocks as $key => $value ) {
176 if ( $blockName == $value['blockName'] ) {
177 if ( $value['attrs']['blockId'] == $blockId ) {
178 $objName = str_replace( ' ', '_', ucwords( str_replace( array( 'ultimate-post/', '-' ), array( '', ' ' ), $blockName ) ) );
179 $new_obj = '\ULTP\blocks\\' . $objName;
180 $objectBlock = new $new_obj();
181 $attr = $objectBlock->get_attributes( true );
182
183 // Fix for grid blocks that do not support load more by default (pagination block)
184 if ( isset( $adv_filter_data['notFirstLoad'] ) && $adv_filter_data['notFirstLoad'] ) {
185 $attr['notFirstLoad'] = $adv_filter_data['notFirstLoad'];
186 }
187
188 $value['attrs']['paged'] = $paged;
189 if ( $builder ) {
190 $value['attrs']['builder'] = $builder;
191 }
192 if ( $postId ) {
193 $attr['current_post'] = $postId;
194 if ( get_post_type( $postId ) == 'ultp_builder' && ! $builder ) {
195 $attr['current_post'] = $exclude_post_id;
196 }
197 }
198 if ( isset( $value['attrs']['queryUnique'] ) && $value['attrs']['queryUnique'] ) {
199 $value['attrs']['loadMoreQueryUnique'] = $ultp_uniqueIds;
200 $ultp_uniqueIds[ $value['attrs']['queryUnique'] ] = array_diff( $ultp_uniqueIds[ $value['attrs']['queryUnique'] ], $ultp_current_unique_posts );
201 $value['attrs']['savedQueryUnique'] = $ultp_uniqueIds;
202 $value['attrs']['ultp_current_unique_posts'] = $ultp_current_unique_posts;
203 }
204 if ( isset( $value['attrs']['queryUnique'] ) && $value['attrs']['queryUnique'] && ( $value['attrs']['paginationType'] == 'loadMore' || $value['attrs']['paginationType'] == 'navigation' ) && isset( $ultp_uniqueIds ) && ! isset( $ultp_current_unique_posts ) ) {
205 die();
206 }
207
208 if ( $filterValue ) {
209 $value['attrs']['queryTaxValue'] = $adv_filter_data['is_adv'] ? wp_json_encode( $filterValue ) : wp_json_encode( array( $filterValue ) );
210 $value['attrs']['queryTax'] = $filterType;
211 $value['attrs']['checkFilter'] = true;
212 $value['attrs']['filterShow'] = $adv_filter_data['filterShow'];
213 $value['attrs']['queryAuthor'] = $adv_filter_data['author'];
214 $value['attrs']['queryOrderBy'] = $adv_filter_data['orderby'];
215 $value['attrs']['queryOrder'] = $adv_filter_data['order'];
216 $value['attrs']['querySearch'] = $adv_filter_data['search'];
217 $value['attrs']['queryQuick'] = $adv_filter_data['adv_sort'];
218
219 if ( $adv_filter_data['is_adv'] ) {
220 $value['attrs']['queryRelation'] = 'AND';
221 }
222 }
223 // Exclude Current Post From Pagination
224 if ( $exclude_post_id ) {
225 $queryArr = json_decode( $value['attrs']['queryExclude'] );
226 $queryArr[] = array(
227 'value' => $exclude_post_id,
228 'title' => '',
229 );
230 $value['attrs']['queryExclude'] = wp_json_encode( $queryArr );
231 }
232 $attr = array_merge( $attr, $value['attrs'] );
233 echo $objectBlock->content( $attr, true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Block render output; each block escapes its own markup.
234 die();
235 }
236 }
237 if ( ! empty( $value['innerBlocks'] ) ) {
238 $this->pagination_content_return( $value['innerBlocks'], $paged, $blockId, $blockRaw, $blockName, $builder, $postId, $filterValue, $filterType, $ultp_uniqueIds, $ultp_current_unique_posts, $widgetBlockId, $exclude_post_id, $adv_filter_data );
239 }
240 }
241 }
242
243
244 /**
245 * Next Preview Callback of the Blocks.
246 *
247 * @since v.1.0.0
248 *
249 * @return STRING The AJAX response.
250 */
251 public function ultp_next_prev_callback() {
252 if ( ! $this->verify_public_nonce() ) {
253 return;
254 }
255
256 $paged = isset( $_POST['paged'] ) ? sanitize_text_field( $_POST['paged'] ) : '';
257 $blockId = isset( $_POST['blockId'] ) ? sanitize_text_field( $_POST['blockId'] ) : '';
258 $postId = isset( $_POST['postId'] ) ? sanitize_text_field( $_POST['postId'] ) : '';
259 $blockRaw = isset( $_POST['blockName'] ) ? sanitize_text_field( $_POST['blockName'] ) : '';
260 $builder = isset( $_POST['builder'] ) ? sanitize_text_field( $_POST['builder'] ) : '';
261 $blockName = str_replace( '_', '/', $blockRaw );
262 $widgetBlockId = isset( $_POST['widgetBlockId'] ) ? sanitize_text_field( $_POST['widgetBlockId'] ) : '';
263 $exclude_post_id = isset( $_POST['exclude'] ) ? sanitize_text_field( $_POST['exclude'] ) : '';
264
265 if ( $postId ) { // patch security issue
266 $post = get_post( (int) $postId );
267
268 if ( ! $post ) {
269 wp_send_json_error( array( 'message' => 'Post not found' ) );
270 return;
271 }
272
273 // Block private posts
274 if ( $post->post_status === 'private' && ! current_user_can( 'read_private_posts' ) ) {
275 wp_send_json_error( array( 'message' => 'Private post' ) );
276 return;
277 }
278
279 // Block draft/pending
280 if ( in_array( $post->post_status, array( 'draft', 'pending' ), true )
281 && ! current_user_can( 'edit_post', $post->ID ) ) {
282 wp_send_json_error( array( 'message' => 'Not allowed' ) );
283 return;
284 }
285
286 // Block password protected
287 if ( post_password_required( $post ) ) {
288 wp_send_json_error( array( 'message' => 'Password protected' ) );
289 return;
290 }
291 }
292
293 $is_adv = isset( $_POST['isAdv'] ) ? ultimate_post()->ultp_rest_sanitize_params( $_POST['isAdv'] ) : false;
294 $filterValue = isset( $_POST['filterValue'] ) ?
295 (
296 is_array( $_POST['filterValue'] ) ?
297 ultimate_post()->ultp_rest_sanitize_params( $_POST['filterValue'] ) :
298 sanitize_text_field( $_POST['filterValue'] )
299 ) :
300 '';
301
302 $filterType = isset( $_POST['filterType'] ) ? sanitize_text_field( $_POST['filterType'] ) : '';
303 $filterShow = isset( $_POST['filterShow'] ) ? sanitize_text_field( $_POST['filterShow'] ) : false;
304 $checkFilter = isset( $_POST['checkFilter'] ) ? sanitize_text_field( $_POST['checkFilter'] ) : false;
305 $author = isset( $_POST['author'] ) ? sanitize_text_field( $_POST['author'] ) : false;
306 $orderby = isset( $_POST['orderby'] ) ? sanitize_text_field( $_POST['orderby'] ) : 'date';
307 $order = isset( $_POST['order'] ) ? sanitize_text_field( $_POST['order'] ) : 'DESC';
308 $search = isset( $_POST['search'] ) ? sanitize_text_field( $_POST['search'] ) : '';
309 $adv_sort = isset( $_POST['adv_sort'] ) ? sanitize_text_field( $_POST['adv_sort'] ) : '';
310
311 $adv_filter_data = array(
312 'is_adv' => filter_var( $is_adv, FILTER_VALIDATE_BOOLEAN ),
313 'filterShow' => filter_var( $filterShow, FILTER_VALIDATE_BOOLEAN ),
314 'checkFilter' => filter_var( $checkFilter, FILTER_VALIDATE_BOOLEAN ),
315 'author' => $author ? wp_json_encode( $author ) : false,
316 'orderby' => $orderby,
317 'order' => $order,
318 'search' => $search,
319 'adv_sort' => $adv_sort,
320 'notFirstLoad' => true,
321 );
322
323 $ultp_uniqueIds = isset( $_POST['ultpUniqueIds'] ) ? json_decode( stripslashes( sanitize_text_field( $_POST['ultpUniqueIds'] ) ), true ) : array();
324 $ultp_current_unique_posts = isset( $_POST['ultpCurrentUniquePosts'] ) ? json_decode( stripslashes( sanitize_text_field( $_POST['ultpCurrentUniquePosts'] ) ), true ) : array();
325
326 if ( $widgetBlockId ) {
327 $blocks = parse_blocks( get_option( 'widget_block' )[ $widgetBlockId ]['content'] );
328 $this->pagination_content_return( $blocks, $paged, $blockId, $blockRaw, $blockName, $builder, '', $filterValue, $filterType, $ultp_uniqueIds, $ultp_current_unique_posts, $widgetBlockId, '', $adv_filter_data );
329 } elseif ( $paged && $blockId && $postId && $blockName ) {
330 $post = get_post( $postId );
331 if ( has_blocks( $post->post_content ) ) {
332 $blocks = parse_blocks( $post->post_content );
333 $this->pagination_content_return( $blocks, $paged, $blockId, $blockRaw, $blockName, $builder, $postId, $filterValue, $filterType, $ultp_uniqueIds, $ultp_current_unique_posts, '', $exclude_post_id, $adv_filter_data );
334 }
335 }
336 }
337
338 /**
339 * Filter Callback of the Blocks.
340 *
341 * @since v.1.0.0
342 *
343 * @return STRING The AJAX response.
344 */
345 public function ultp_filter_callback() {
346 if ( ! $this->verify_public_nonce() ) {
347 return;
348 }
349
350 $taxtype = isset( $_POST['taxtype'] ) ? sanitize_text_field( $_POST['taxtype'] ) : '';
351 if ( $taxtype ) {
352 $blockId = isset( $_POST['blockId'] ) ? sanitize_text_field( $_POST['blockId'] ) : '';
353 $postId = isset( $_POST['postId'] ) ? sanitize_text_field( $_POST['postId'] ) : '';
354 $taxonomy = isset( $_POST['taxonomy'] ) ? sanitize_text_field( $_POST['taxonomy'] ) : '';
355 $blockRaw = isset( $_POST['blockName'] ) ? sanitize_text_field( $_POST['blockName'] ) : '';
356 $blockName = str_replace( '_', '/', $blockRaw );
357 $post = get_post( $postId );
358 $widgetBlockId = isset( $_POST['widgetBlockId'] ) ? sanitize_text_field( $_POST['widgetBlockId'] ) : '';
359 $ultp_uniqueIds = isset( $_POST['ultpUniqueIds'] ) ? json_decode( stripslashes( sanitize_text_field( $_POST['ultpUniqueIds'] ) ), true ) : array();
360 $ultp_current_unique_posts = isset( $_POST['ultpCurrentUniquePosts'] ) ? json_decode( stripslashes( sanitize_text_field( $_POST['ultpCurrentUniquePosts'] ) ), true ) : array();
361 $toReturn = array();
362
363 if ( $widgetBlockId ) {
364 $blocks = parse_blocks( get_option( 'widget_block' )[ $widgetBlockId ]['content'] );
365 $data = $this->filter_content_return( $blocks, $blockId, $blockRaw, $blockName, $taxtype, $taxonomy, $postId, $toReturn, $widgetBlockId, array(), $ultp_uniqueIds, $ultp_current_unique_posts );
366 } elseif ( has_blocks( $post->post_content ) ) {
367 $blocks = parse_blocks( $post->post_content );
368 $data = $this->filter_content_return( $blocks, $blockId, $blockRaw, $blockName, $taxtype, $taxonomy, $postId, $toReturn, '', array(), $ultp_uniqueIds, $ultp_current_unique_posts );
369 }
370 return wp_send_json_success(
371 array(
372 'filteredData' => $data,
373 )
374 );
375 }
376 }
377
378 /**
379 * Advanced Filter Callback of the Blocks.
380 *
381 * @since v.3.2.4
382 *
383 * @return STRING The AJAX response.
384 */
385 public function ultp_adv_filter_callback() {
386 if ( ! $this->verify_public_nonce() ) {
387 return;
388 }
389
390 $blockId = isset( $_POST['blockId'] ) ? sanitize_text_field( $_POST['blockId'] ) : '';
391 $postId = isset( $_POST['postId'] ) ? sanitize_text_field( $_POST['postId'] ) : '';
392
393 $taxonomy = isset( $_POST['taxonomy'] ) ? ultimate_post()->ultp_rest_sanitize_params( $_POST['taxonomy'] ) : '[]';
394 $builder_id = isset( $_POST['builder_id'] ) ? sanitize_text_field( $_POST['builder_id'] ) : '';
395 $author = isset( $_POST['author'] ) ? ultimate_post()->ultp_rest_sanitize_params( $_POST['author'] ) : false;
396 $orderby = isset( $_POST['orderby'] ) ? sanitize_text_field( $_POST['orderby'] ) : ''; // default orderbyt title requested from support
397 $order = isset( $_POST['order'] ) ? sanitize_text_field( $_POST['order'] ) : 'DESC';
398 $search = isset( $_POST['search'] ) ? sanitize_text_field( $_POST['search'] ) : '';
399 $adv_sort = isset( $_POST['adv_sort'] ) ? sanitize_text_field( $_POST['adv_sort'] ) : '';
400
401 $adv_filter_data = array(
402 'is_adv' => true,
403 'filterShow' => true,
404 'checkFilter' => true,
405 'author' => $author ? wp_json_encode( $author ) : false,
406 'orderby' => $orderby,
407 'order' => $order,
408 'search' => $search,
409 'adv_sort' => $adv_sort,
410 'builder' => $builder_id,
411 );
412
413 $blockRaw = isset( $_POST['blockName'] ) ? sanitize_text_field( $_POST['blockName'] ) : '';
414 $blockName = str_replace( '_', '/', $blockRaw );
415 $post = get_post( $postId );
416 $widgetBlockId = isset( $_POST['widgetBlockId'] ) ? sanitize_text_field( $_POST['widgetBlockId'] ) : '';
417 $toReturn = array();
418
419 $ultp_uniqueIds = isset( $_POST['ultpUniqueIds'] ) ? json_decode( stripslashes( sanitize_text_field( $_POST['ultpUniqueIds'] ) ), true ) : array();
420 $ultp_current_unique_posts = isset( $_POST['ultpCurrentUniquePosts'] ) ? json_decode( stripslashes( sanitize_text_field( $_POST['ultpCurrentUniquePosts'] ) ), true ) : array();
421
422 if ( $widgetBlockId ) {
423 $blocks = parse_blocks( get_option( 'widget_block' )[ $widgetBlockId ]['content'] );
424 $data = $this->filter_content_return( $blocks, $blockId, $blockRaw, $blockName, 'multiTaxonomy', $taxonomy, $postId, $toReturn, $widgetBlockId, $adv_filter_data, $ultp_uniqueIds, $ultp_current_unique_posts );
425 } elseif ( has_blocks( $post->post_content ) ) {
426 $blocks = parse_blocks( $post->post_content );
427 $data = $this->filter_content_return( $blocks, $blockId, $blockRaw, $blockName, 'multiTaxonomy', $taxonomy, $postId, $toReturn, '', $adv_filter_data, $ultp_uniqueIds, $ultp_current_unique_posts );
428 }
429 return wp_send_json_success(
430 array(
431 'filteredData' => $data,
432 )
433 );
434 }
435
436 /**
437 * Pagination of the Blocks.
438 *
439 * @since v.1.0.0
440 *
441 * @return STRING The AJAX response.
442 */
443 public function ultp_pagination_callback() {
444 if ( ! $this->verify_public_nonce() ) {
445 return;
446 }
447
448 $paged = isset( $_POST['paged'] ) ? sanitize_text_field( $_POST['paged'] ) : '';
449 if ( $paged ) {
450 $blockId = isset( $_POST['blockId'] ) ? sanitize_text_field( $_POST['blockId'] ) : '';
451 $postId = isset( $_POST['postId'] ) ? sanitize_text_field( $_POST['postId'] ) : '';
452 $blockRaw = isset( $_POST['blockName'] ) ? sanitize_text_field( $_POST['blockName'] ) : '';
453 $builder = isset( $_POST['builder'] ) ? sanitize_text_field( $_POST['builder'] ) : '';
454 $blockName = str_replace( '_', '/', $blockRaw );
455 $post = get_post( $postId );
456 $widgetBlockId = isset( $_POST['widgetBlockId'] ) ? sanitize_text_field( $_POST['widgetBlockId'] ) : '';
457 $exclude_post_id = isset( $_POST['exclude'] ) ? sanitize_text_field( $_POST['exclude'] ) : '';
458
459 $is_adv = isset( $_POST['isAdv'] ) ? ultimate_post()->ultp_rest_sanitize_params( $_POST['isAdv'] ) : false;
460
461 $filterValue = array();
462
463 if ( isset( $_POST['filterValue'] ) ) {
464 if ( is_array( $_POST['filterValue'] ) ) {
465 $filterValue = ultimate_post()->ultp_rest_sanitize_params( $_POST['filterValue'] );
466 } else {
467 $decoded = json_decode( $_POST['filterValue'] );
468 if ( is_array( $decoded ) ) {
469 $filterValue = ultimate_post()->ultp_rest_sanitize_params( $decoded );
470 } elseif ( $decoded ) {
471 $filterValue = sanitize_text_field( $_POST['filterValue'] );
472 }
473 }
474 }
475
476 $filterType = isset( $_POST['filterType'] ) ? sanitize_text_field( $_POST['filterType'] ) : '';
477 $filterShow = isset( $_POST['filterShow'] ) ? sanitize_text_field( $_POST['filterShow'] ) : false;
478 $checkFilter = isset( $_POST['checkFilter'] ) ? sanitize_text_field( $_POST['checkFilter'] ) : false;
479 $author = isset( $_POST['author'] ) ? sanitize_text_field( $_POST['author'] ) : false;
480 $orderby = isset( $_POST['orderby'] ) ? sanitize_text_field( $_POST['orderby'] ) : 'title'; // default orderbyt title requested from support
481 $order = isset( $_POST['order'] ) ? sanitize_text_field( $_POST['order'] ) : 'DESC';
482 $search = isset( $_POST['search'] ) ? sanitize_text_field( $_POST['search'] ) : '';
483 $adv_sort = isset( $_POST['adv_sort'] ) ? sanitize_text_field( $_POST['adv_sort'] ) : '';
484
485 $adv_filter_data = array(
486 'is_adv' => filter_var( $is_adv, FILTER_VALIDATE_BOOLEAN ),
487 'filterShow' => filter_var( $filterShow, FILTER_VALIDATE_BOOLEAN ),
488 'checkFilter' => filter_var( $checkFilter, FILTER_VALIDATE_BOOLEAN ),
489 'author' => $author ? wp_json_encode( $author ) : false,
490 'orderby' => $orderby,
491 'order' => $order,
492 'search' => $search,
493 'adv_sort' => $adv_sort,
494 );
495
496 $ultp_uniqueIds = isset( $_POST['ultpUniqueIds'] ) ? json_decode( stripslashes( sanitize_text_field( $_POST['ultpUniqueIds'] ) ), true ) : array();
497 $ultp_current_unique_posts = isset( $_POST['ultpCurrentUniquePosts'] ) ? json_decode( stripslashes( sanitize_text_field( $_POST['ultpCurrentUniquePosts'] ) ), true ) : array();
498
499 if ( $widgetBlockId ) {
500 $blocks = parse_blocks( get_option( 'widget_block' )[ $widgetBlockId ]['content'] );
501 $this->pagination_content_return( $blocks, $paged, $blockId, $blockRaw, $blockName, $builder, '', $filterValue, $filterType, $ultp_uniqueIds, $ultp_current_unique_posts, $widgetBlockId, '', $adv_filter_data );
502 } elseif ( has_blocks( $post->post_content ) ) {
503 $blocks = parse_blocks( $post->post_content );
504 $this->pagination_content_return( $blocks, $paged, $blockId, $blockRaw, $blockName, $builder, $postId, $filterValue, $filterType, $ultp_uniqueIds, $ultp_current_unique_posts, '', $exclude_post_id, $adv_filter_data );
505 }
506 }
507 }
508
509 /**
510 * Share Count callback.
511 *
512 * @since v.1.0.0
513 *
514 * @return void
515 */
516 public function ultp_share_count_callback() {
517 if ( ! $this->verify_public_nonce() ) {
518 return;
519 }
520
521 $post_id = isset( $_POST['postId'] ) ? absint( $_POST['postId'] ) : 0;
522
523 // Validate post ID
524 if ( ! $post_id ) {
525 wp_send_json_error( array( 'message' => 'Invalid post ID' ) );
526 return;
527 }
528
529 // Check if post exists
530 $post = get_post( $post_id );
531 if ( ! $post ) {
532 wp_send_json_error( array( 'message' => 'Post not found' ) );
533 return;
534 }
535
536 // Block private posts
537 if ( $post->post_status === 'private' && ! current_user_can( 'read_private_posts' ) ) {
538 wp_send_json_error( array( 'message' => 'Private post' ) );
539 return;
540 }
541
542 // Block draft/pending posts
543 if ( in_array( $post->post_status, array( 'draft', 'pending' ), true )
544 && ! current_user_can( 'edit_post', $post->ID ) ) {
545 wp_send_json_error( array( 'message' => 'Not allowed' ) );
546 return;
547 }
548
549 // Block password protected posts
550 if ( post_password_required( $post ) ) {
551 wp_send_json_error( array( 'message' => 'Password protected' ) );
552 return;
553 }
554
555 // Only allow published posts to have share count updated
556 if ( $post->post_status !== 'publish' ) {
557 wp_send_json_error( array( 'message' => 'Post not published' ) );
558 return;
559 }
560
561 // Rate limiting - 60 second cooldown per user per post
562 $user_identifier = is_user_logged_in() ? 'user_' . get_current_user_id() : 'ip_' . sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ?? '' ) );
563 $transient_key = 'ultp_share_' . $post_id . '_' . md5( $user_identifier );
564
565 if ( get_transient( $transient_key ) ) {
566 return; // Already shared within last 60 seconds
567 }
568
569 set_transient( $transient_key, true, 60 ); // 60 seconds cooldown
570
571 // Always increment by 1 from database - ignore POST shareCount
572 $current_count = get_post_meta( $post_id, 'share_count', true );
573 $current_count = $current_count ? absint( $current_count ) : 0;
574 $new_count = $current_count + 1;
575
576 update_post_meta( $post_id, 'share_count', $new_count );
577 }
578
579 /**
580 * Filter Callback of the Blocks.
581 *
582 * @since v.1.0.0
583 *
584 * @param ARRAY $blocks The blocks array.
585 * @param STRING $blockId The block ID.
586 * @param STRING $blockRaw The raw block name.
587 * @param STRING $blockName The block name.
588 * @param STRING $taxtype The taxonomy type.
589 * @param STRING|ARRAY $taxonomy The taxonomy.
590 * @param STRING $postId The post ID.
591 * @param ARRAY $toReturn The array to return.
592 * @param STRING $widgetBlockId The widget block ID.
593 * @param ARRAY $adv_filter_data The advanced filter data.
594 * @param ARRAY $ultp_uniqueIds The unique IDs.
595 * @param ARRAY $ultp_current_unique_posts The current unique posts.
596 * @return ARRAY
597 */
598 public function filter_content_return( $blocks, $blockId, $blockRaw, $blockName, $taxtype, $taxonomy, $postId, &$toReturn, $widgetBlockId = '', $adv_filter_data = array(), $ultp_uniqueIds = array(), $ultp_current_unique_posts = array() ) {
599 foreach ( $blocks as $key => $value ) {
600 if ( ( $value['blockName'] ?? '' ) === 'core/shortcode' ) {
601 if ( preg_match( '/id="([^"]+)"/', $value['innerHTML'] ?? '', $matches ) ) {
602 $template_post = get_post( $matches[1] );
603 if ( $template_post && ! empty( $template_post->post_content ) ) {
604 $template_blocks = parse_blocks( $template_post->post_content );
605 // recurse into the expanded blocks instead
606 $this->filter_content_return( $template_blocks, $blockId, $blockRaw, $blockName, $taxtype, $taxonomy, $postId, $toReturn, $widgetBlockId, $adv_filter_data, $ultp_uniqueIds, $ultp_current_unique_posts );
607 continue; // skip the shortcode block itself
608 }
609 }
610 }
611 if ( $blockName == $value['blockName'] ) {
612 if ( $value['attrs']['blockId'] == $blockId ) {
613 $objName = str_replace( ' ', '_', ucwords( str_replace( array( 'ultimate-post/', '-' ), array( '', ' ' ), $blockName ) ) );
614 $new_obj = '\ULTP\blocks\\' . $objName;
615 $objectBlock = new $new_obj();
616 $attr = $objectBlock->get_attributes( true );
617 if ( $taxonomy ) {
618
619 if ( isset( $adv_filter_data['is_adv'] ) && $adv_filter_data['is_adv'] ) {
620 $value['attrs']['queryTaxValue'] = wp_json_encode( $taxonomy );
621 $value['attrs']['queryRelation'] = 'AND';
622 $value['attrs']['queryAuthor'] = $adv_filter_data['author'];
623 $value['attrs']['queryOrder'] = $adv_filter_data['order'];
624 $value['attrs']['querySearch'] = $adv_filter_data['search'];
625 $value['attrs']['queryQuick'] = $adv_filter_data['adv_sort'];
626 if ( ! empty( $adv_filter_data['orderby'] ) ) {
627 $value['attrs']['queryOrderBy'] = $adv_filter_data['orderby'];
628 }
629 } else {
630 $value['attrs']['queryTaxValue'] = wp_json_encode( array( $taxonomy ) );
631 }
632
633 $value['attrs']['queryTax'] = $taxtype;
634 $value['attrs']['ajaxCall'] = true;
635 }
636 if ( isset( $value['attrs']['queryNumber'] ) ) {
637 $value['attrs']['queryNumber'] = $value['attrs']['queryNumber'];
638 }
639
640 if ( isset( $value['attrs']['queryUnique'] ) && $value['attrs']['queryUnique'] ) {
641 $value['attrs']['loadMoreQueryUnique'] = $ultp_uniqueIds;
642 $ultp_uniqueIds[ $value['attrs']['queryUnique'] ] = array_diff( $ultp_uniqueIds[ $value['attrs']['queryUnique'] ], $ultp_current_unique_posts );
643 $value['attrs']['savedQueryUnique'] = $ultp_uniqueIds;
644 $value['attrs']['ultp_current_unique_posts'] = $ultp_current_unique_posts;
645 }
646
647 if ( $adv_filter_data['builder'] ) {
648 $value['attrs']['builder'] = $adv_filter_data['builder'];
649 }
650
651 $attr = array_merge( $attr, $value['attrs'] );
652
653 $filter_attributes = array();
654
655 $filter_attributes['isAdv'] = isset( $adv_filter_data['is_adv'] ) ? $adv_filter_data['is_adv'] : false;
656 $filter_attributes['queryTaxValue'] = $filter_attributes['isAdv'] ? wp_json_encode( $taxonomy ) : $taxonomy;
657 $filter_attributes['queryTax'] = $taxtype;
658
659 if ( $filter_attributes['isAdv'] ) {
660 $filter_attributes['queryAuthor'] = $adv_filter_data['author'];
661 $filter_attributes['queryOrder'] = $adv_filter_data['order'];
662 $filter_attributes['querySearch'] = $adv_filter_data['search'];
663 $filter_attributes['queryQuick'] = $adv_filter_data['adv_sort'];
664 $filter_attributes['queryOrderBy'] =
665 ! empty( $adv_filter_data['orderby'] ) ? $adv_filter_data['orderby'] : ( $value['attrs']['queryOrderBy'] ?? '' );
666 }
667 $pagination = $this->pagination_for_filter( $attr, $postId, $blockRaw, $filter_attributes );
668 $toReturn = array(
669 'blocks' => $objectBlock->content( $attr, true ),
670 'notFound' => isset( $attr['notFoundMessage'] ) ? $attr['notFoundMessage'] : '',
671 'pagination' => $pagination['pagination_content'],
672 'data_attrs' => $pagination['data_attrs'],
673 'data_all_attrs' => $pagination['data_attrs'],
674 'paginationType' => $attr['paginationType'],
675 'paginationShow' => $attr['paginationShow'],
676 );
677 }
678 }
679 if ( ! empty( $value['innerBlocks'] ) ) {
680 $this->filter_content_return( $value['innerBlocks'], $blockId, $blockRaw, $blockName, $taxtype, $taxonomy, $postId, $toReturn, $widgetBlockId, $adv_filter_data, $ultp_uniqueIds, $ultp_current_unique_posts );
681 }
682 }
683 return $toReturn;
684 }
685
686 /**
687 * Pagination for filter callback
688 *
689 * @since v.2.8.9
690 *
691 * @param ARRAY $attr .
692 * @param STRING $postId .
693 * @param STRING $blockRaw .
694 * @param ARRAY $filter_attributes .
695 * @return STRING
696 */
697 public function pagination_for_filter( $attr, $postId, $blockRaw, $filter_attributes ) {
698 $attr['queryNumber'] = ultimate_post()->get_post_number( 4, $attr['queryNumber'], $attr['queryNumPosts'] );
699 $recent_posts = new \WP_Query( ultimate_post()->get_query( $attr ) );
700 $pageNum = ultimate_post()->get_page_number( $attr, $recent_posts->found_posts );
701
702 $datasets = ultimate_post()->get_adv_data_attrs( null, $filter_attributes );
703 $datasets .= ' data-for="ultp-block-' . sanitize_html_class( $attr['blockId'] ) . '" ';
704
705 $wraper_after = '';
706 $style = $pageNum == 1 ? 'style="display:none"' : '';
707
708 if ( $attr['paginationType'] == 'loadMore' ) {
709 $wraper_after .= '<div ' . $style . ' class="ultp-loadmore "' . '>';
710 $wraper_after .= '<span class="ultp-loadmore-action" tabindex="0" role="button" data-pages="' . $pageNum . '" data-pagenum="1" data-blockid="' . $attr['blockId'] . '" data-blockname="' . $blockRaw . '" data-postid="' . $postId . '" ' . ultimate_post()->get_builder_attr( $attr['queryType'] ) . $datasets . '>' . ( isset( $attr['loadMoreText'] ) ? $attr['loadMoreText'] : 'Load More' ) . ' <span class="ultp-spin">' . ultimate_post()->get_svg_icon( 'refresh' ) . '</span></span>';
711 $wraper_after .= '</div>';
712 } elseif ( $attr['paginationType'] == 'navigation' ) {
713 $wraper_after .= '<div ' . $style . ' class="ultp-next-prev-wrap" data-pages="' . $pageNum . '" data-pagenum="1" data-blockid="' . $attr['blockId'] . '" data-blockname="' . $blockRaw . '" data-postid="' . $postId . '" ' . ultimate_post()->get_builder_attr( $attr['queryType'] ) . $datasets . '>';
714 $wraper_after .= ultimate_post()->next_prev();
715 $wraper_after .= '</div>';
716 } elseif ( $attr['paginationType'] == 'pagination' ) {
717 $wraper_after .= '<div class="ultp-pagination-wrap' . ( $attr['paginationAjax'] ? ' ultp-pagination-ajax-action' : '' ) . '" data-paged="1" data-blockid="' . $attr['blockId'] . '" data-postid="' . $postId . '" data-pages="' . $pageNum . '" data-blockname="' . $blockRaw . '" ' . ultimate_post()->get_builder_attr( $attr['queryType'] ) . $datasets . '>';
718
719 $wraper_after .= ultimate_post()->pagination(
720 $pageNum,
721 $attr['paginationNav'],
722 $attr['paginationText'],
723 $attr['paginationAjax'],
724 isset( $_SERVER['HTTP_REFERER'] ) ? esc_url_raw( $_SERVER['HTTP_REFERER'] ) : '',
725 $attr['blockId']
726 );
727
728 $wraper_after .= '</div>';
729 }
730 wp_reset_query();
731
732 // queryQuick
733 return array(
734 'pagination_content' => $wraper_after,
735 'data_attrs' => wp_json_encode( array(
736 'paged' => 1,
737 'blockid' => $attr['blockId'],
738 'postid' => $postId,
739 'pages' => $pageNum,
740 'blockname' => $blockRaw,
741 'filter_value' => isset( $filter_attributes['queryTaxValue'] ) ? $filter_attributes['queryTaxValue'] : '',
742 'filter_type' => isset( $filter_attributes['queryTax'] ) ? $filter_attributes['queryTax'] : '',
743 'filter_author' => isset( $filter_attributes['queryAuthor'] ) ? $filter_attributes['queryAuthor'] : '',
744 'filter_order' => isset( $filter_attributes['queryOrder'] ) ? $filter_attributes['queryOrder'] : 'DESC',
745 'filter_orderby' => isset( $filter_attributes['queryOrderBy'] ) ? $filter_attributes['queryOrderBy'] : '',
746 'filter_search' => isset( $filter_attributes['querySearch'] ) ? $filter_attributes['querySearch'] : '',
747 'filter_queryQuick' => isset( $filter_attributes['queryQuick'] ) ? $filter_attributes['queryQuick'] : '',
748 'filter_for' => 'ultp-block-' . $attr['blockId'],
749 )),
750 );
751 }
752
753 /**
754 * Verify the front-end AJAX nonce, or end the request.
755 *
756 * Fails with a machine-readable 403 rather than the bare `return` these
757 * handlers used to do. A bare return lets admin-ajax emit "0" at HTTP 200,
758 * which the client cannot tell apart from a real response — ultp_next_prev's
759 * success handler treated that "0" as markup and rendered it into the block.
760 *
761 * The 'ultp_invalid_nonce' code is a contract with assets/js/ultp.js: on
762 * seeing it the client discards its cached nonce, fetches a fresh one and
763 * retries once. A nonce legitimately expires while a tab is open (WordPress
764 * nonces last 12-24h, tabs last longer), so this is a routine condition and
765 * not an attack signal. See ultp_get_nonce_callback() for what this nonce
766 * does and does not protect.
767 *
768 * @since v.5.1.0
769 *
770 * @return BOOLEAN True when the nonce is valid; otherwise the request ends.
771 */
772 private function verify_public_nonce() {
773 if ( isset( $_REQUEST['wpnonce'] ) && wp_verify_nonce( sanitize_key( wp_unslash( $_REQUEST['wpnonce'] ) ), 'ultp-public-nonce' ) ) {
774 return true;
775 }
776
777 wp_send_json_error(
778 array(
779 'code' => 'ultp_invalid_nonce',
780 'message' => __( 'Security token expired. Please try again.', 'ultimate-post' ),
781 ),
782 403
783 );
784
785 return false;
786 }
787
788 /**
789 * Vends the front-end AJAX nonce.
790 *
791 * Public by design: full-page caching makes a nonce embedded in HTML stale for
792 * most visitors, so front-end blocks fetch one at runtime instead.
793 *
794 * SECURITY: because this endpoint is public, 'ultp-public-nonce' is NOT a CSRF
795 * control on the front end — anyone can request one. It is a request-shape
796 * check only. Any nopriv handler that changes state MUST carry its own
797 * authorisation; see ultp_share_count_callback() for the pattern (post status,
798 * capability and rate-limit checks, none of which depend on the nonce).
799 *
800 * This endpoint vends the PUBLIC nonce only. Admin routes verify the separate
801 * 'ultp-admin-nonce', which is never vended here and is always paired with a
802 * capability check. Do not merge the two.
803 *
804 * @since v.5.0.6
805 *
806 * @return STRING The AJAX response.
807 */
808 public function ultp_get_nonce_callback() {
809 nocache_headers();
810 wp_send_json_success(
811 array(
812 'nonce' => wp_create_nonce( 'ultp-public-nonce' ),
813 )
814 );
815 }
816 }
817