PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.14
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.14
51.1.87 51.1.86 51.1.84 51.1.85 51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 All 41 releases
king-addons / includes / helpers / Dynamic_Posts_Grid_Ajax.php

Dynamic_Posts_Grid_Ajax.php in King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder 51.1.14, at includes/helpers/Dynamic_Posts_Grid_Ajax.php

427 lines 14.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /** @noinspection SpellCheckingInspection, DuplicatedCode, PhpUnused */
4
5 namespace King_Addons;
6
7 if (!defined('ABSPATH')) {
8 exit; // Exit if accessed directly.
9 }
10
11 /**
12 * Dynamic Posts Grid AJAX Helper
13 *
14 * Handles AJAX requests for filtering, searching, and load more functionality
15 * for the Dynamic Posts Grid widget.
16 */
17 class Dynamic_Posts_Grid_Ajax
18 {
19 /**
20 * Constructor.
21 */
22 public function __construct()
23 {
24 $this->init_hooks();
25 }
26
27 /**
28 * Initialize hooks.
29 */
30 private function init_hooks(): void
31 {
32 // AJAX actions for both logged in and non-logged in users
33 add_action('wp_ajax_king_addons_dynamic_posts_grid_filter', [$this, 'handle_filter_request']);
34 add_action('wp_ajax_nopriv_king_addons_dynamic_posts_grid_filter', [$this, 'handle_filter_request']);
35
36 add_action('wp_ajax_king_addons_dynamic_posts_grid_load_more', [$this, 'handle_load_more_request']);
37 add_action('wp_ajax_nopriv_king_addons_dynamic_posts_grid_load_more', [$this, 'handle_load_more_request']);
38
39 // Enqueue AJAX variables
40 add_action('wp_enqueue_scripts', [$this, 'enqueue_ajax_variables']);
41 }
42
43 /**
44 * Enqueue AJAX variables for frontend.
45 */
46 public function enqueue_ajax_variables(): void
47 {
48 wp_localize_script('jquery', 'KingAddonsDynamicPostsGrid', [
49 'ajaxUrl' => admin_url('admin-ajax.php'),
50 'nonce' => wp_create_nonce('king_addons_dynamic_posts_grid_nonce'),
51 ]);
52 }
53
54 /**
55 * Handle filter AJAX request.
56 */
57 public function handle_filter_request(): void
58 {
59 // Verify nonce
60 if (!wp_verify_nonce($_POST['nonce'] ?? '', 'king_addons_dynamic_posts_grid_nonce')) {
61 wp_send_json_error(['message' => esc_html__('Security check failed.', 'king-addons')]);
62 }
63
64 // Get and sanitize POST data
65 $widget_id = sanitize_text_field($_POST['widget_id'] ?? '');
66 $posts_per_page = absint($_POST['posts_per_page'] ?? 12);
67 $post_types = $this->sanitize_array($_POST['post_types'] ?? ['post']);
68 $orderby = sanitize_text_field($_POST['orderby'] ?? 'date');
69 $order = sanitize_text_field($_POST['order'] ?? 'DESC');
70 $filter_taxonomy = sanitize_text_field($_POST['filter_taxonomy'] ?? 'category');
71 $filter_term = sanitize_text_field($_POST['filter_term'] ?? '*');
72 $search_query = sanitize_text_field($_POST['search_query'] ?? '');
73 $page = absint($_POST['page'] ?? 1);
74 $show_excerpt = ($_POST['show_excerpt'] ?? '1') === '1';
75
76 // Build query arguments
77 $query_args = $this->build_query_args([
78 'posts_per_page' => $posts_per_page,
79 'post_types' => $post_types,
80 'orderby' => $orderby,
81 'order' => $order,
82 'filter_taxonomy' => $filter_taxonomy,
83 'filter_term' => $filter_term,
84 'search_query' => $search_query,
85 'page' => $page,
86 ]);
87
88 // Execute query
89 $posts_query = new \WP_Query($query_args);
90
91 // Generate posts HTML
92 $posts_html = $this->generate_posts_html($posts_query, $show_excerpt);
93
94 // Prepare response data
95 $response_data = [
96 'posts_html' => $posts_html,
97 'current_page' => $page,
98 'max_pages' => $posts_query->max_num_pages,
99 'total_posts' => $posts_query->found_posts,
100 'current_count' => $posts_query->post_count,
101 ];
102
103 wp_send_json_success($response_data);
104 }
105
106 /**
107 * Handle load more AJAX request.
108 */
109 public function handle_load_more_request(): void
110 {
111 // Verify nonce
112 if (!wp_verify_nonce($_POST['nonce'] ?? '', 'king_addons_dynamic_posts_grid_nonce')) {
113 wp_send_json_error(['message' => esc_html__('Security check failed.', 'king-addons')]);
114 }
115
116 // Get and sanitize POST data
117 $widget_id = sanitize_text_field($_POST['widget_id'] ?? '');
118 $posts_per_page = absint($_POST['posts_per_page'] ?? 12);
119 $post_types = $this->sanitize_array($_POST['post_types'] ?? ['post']);
120 $orderby = sanitize_text_field($_POST['orderby'] ?? 'date');
121 $order = sanitize_text_field($_POST['order'] ?? 'DESC');
122 $filter_taxonomy = sanitize_text_field($_POST['filter_taxonomy'] ?? 'category');
123 $filter_term = sanitize_text_field($_POST['filter_term'] ?? '*');
124 $search_query = sanitize_text_field($_POST['search_query'] ?? '');
125 $page = absint($_POST['page'] ?? 1);
126 $show_excerpt = ($_POST['show_excerpt'] ?? '1') === '1';
127
128 // Build query arguments for load more (accumulative)
129 $query_args = $this->build_query_args([
130 'posts_per_page' => $posts_per_page,
131 'post_types' => $post_types,
132 'orderby' => $orderby,
133 'order' => $order,
134 'filter_taxonomy' => $filter_taxonomy,
135 'filter_term' => $filter_term,
136 'search_query' => $search_query,
137 'page' => $page,
138 ]);
139
140 // Execute query
141 $posts_query = new \WP_Query($query_args);
142
143 // Generate posts HTML
144 $posts_html = $this->generate_posts_html($posts_query, $show_excerpt);
145
146 // Calculate total shown posts (previous pages + current page)
147 $total_shown = (($page - 1) * $posts_per_page) + $posts_query->post_count;
148
149 // Prepare response data
150 $response_data = [
151 'posts_html' => $posts_html,
152 'current_page' => $page,
153 'max_pages' => $posts_query->max_num_pages,
154 'total_posts' => $posts_query->found_posts,
155 'current_count' => $total_shown,
156 ];
157
158 wp_send_json_success($response_data);
159 }
160
161 /**
162 * Build WP_Query arguments.
163 *
164 * @param array $params Query parameters.
165 * @return array WP_Query arguments.
166 */
167 private function build_query_args(array $params): array
168 {
169 $query_args = [
170 'post_type' => $params['post_types'],
171 'posts_per_page' => $params['posts_per_page'],
172 'paged' => $params['page'],
173 'orderby' => $params['orderby'],
174 'order' => $params['order'],
175 'post_status' => 'publish',
176 'ignore_sticky_posts' => true,
177 ];
178
179 // Add search query
180 if (!empty($params['search_query'])) {
181 $query_args['s'] = $params['search_query'];
182 }
183
184 // Add taxonomy filter
185 if (!empty($params['filter_term']) && $params['filter_term'] !== '*') {
186 $query_args['tax_query'] = [
187 [
188 'taxonomy' => $params['filter_taxonomy'],
189 'field' => 'slug',
190 'terms' => $params['filter_term'],
191 ]
192 ];
193 }
194
195 // Handle random order
196 if ($params['orderby'] === 'rand') {
197 $query_args['orderby'] = 'rand';
198 unset($query_args['order']); // Order is not applicable for random
199 }
200
201 return $query_args;
202 }
203
204 /**
205 * Generate posts HTML.
206 *
207 * @param \WP_Query $posts_query WP_Query object.
208 * @param bool $show_excerpt Whether to show excerpt.
209 * @return string Generated HTML.
210 */
211 private function generate_posts_html(\WP_Query $posts_query, bool $show_excerpt = true): string
212 {
213 if (!$posts_query->have_posts()) {
214 return '<div class="king-addons-dpg-no-posts">' . esc_html__('No posts found.', 'king-addons') . '</div>';
215 }
216
217 ob_start();
218
219 while ($posts_query->have_posts()):
220 $posts_query->the_post();
221 $post = get_post();
222 $post_type_display = $this->get_post_type_display($post);
223 $category_classes = $this->get_post_category_color_classes($post);
224 $cta_text = $this->get_cta_text($post);
225 $post_icon = $this->get_post_type_icon($post);
226 ?>
227
228 <div class="king-addons-dpg-card king-addons-dpg-item <?php echo esc_attr($category_classes); ?>" data-post-id="<?php echo esc_attr($post->ID); ?>">
229
230 <!-- Post Type Header -->
231 <div class="king-addons-dpg-header">
232 <div class="king-addons-dpg-icon">
233 <?php echo $post_icon; ?>
234 </div>
235 <div class="king-addons-dpg-label">
236 <?php echo esc_html($post_type_display); ?>
237 </div>
238 </div>
239
240 <!-- Post Content -->
241 <div class="king-addons-dpg-content">
242 <h3 class="king-addons-dpg-title">
243 <a href="<?php echo esc_url(get_permalink()); ?>">
244 <?php echo esc_html(get_the_title()); ?>
245 </a>
246 </h3>
247
248 <?php if ($show_excerpt): ?>
249 <div class="king-addons-dpg-excerpt">
250 <?php echo esc_html(wp_trim_words(get_the_excerpt(), 20, '...')); ?>
251 </div>
252 <?php endif; ?>
253 </div>
254
255 <!-- CTA Button -->
256 <div class="king-addons-dpg-cta">
257 <a href="<?php echo esc_url(get_permalink()); ?>" class="king-addons-dpg-button">
258 <?php echo esc_html($cta_text); ?>
259 </a>
260 </div>
261
262 </div>
263
264 <?php
265 endwhile;
266
267 wp_reset_postdata();
268
269 return ob_get_clean();
270 }
271
272 /**
273 * Get post category for display.
274 *
275 * @param object $post Post object.
276 * @return string Post category display name.
277 */
278 private function get_post_type_display($post): string
279 {
280 // Get post categories
281 $taxonomy = 'category';
282 $terms = get_the_terms($post->ID, $taxonomy);
283
284 if ($terms && !is_wp_error($terms)) {
285 // Use first category name
286 $term = $terms[0];
287 return strtoupper($term->name);
288 }
289
290 // Fallback to post format/type based display
291 $format = get_post_format($post->ID);
292
293 if ($format === 'video') {
294 return 'VIDEO';
295 } elseif ($format === 'audio') {
296 return 'PUBLICATION';
297 } elseif ($post->post_type === 'page') {
298 return 'WEBSITE';
299 } else {
300 return 'POST';
301 }
302 }
303
304 /**
305 * Get CTA button text (fallback for AJAX context).
306 *
307 * @param object $post Post object.
308 * @return string CTA button text.
309 */
310 private function get_cta_text($post): string
311 {
312 // Simple fallback for AJAX - smart detection only
313 $taxonomy = 'category';
314 $terms = get_the_terms($post->ID, $taxonomy);
315
316 if ($terms && !is_wp_error($terms)) {
317 $term = $terms[0];
318 $slug = strtolower($term->slug);
319
320 // Basic smart mapping for AJAX
321 $smart_cta_map = [
322 'video' => 'WATCH NOW',
323 'videos' => 'WATCH NOW',
324 'website' => 'LEARN MORE',
325 'websites' => 'LEARN MORE',
326 ];
327
328 if (isset($smart_cta_map[$slug])) {
329 return $smart_cta_map[$slug];
330 }
331 }
332
333 return 'READ MORE';
334 }
335
336 /**
337 * Get post category color classes.
338 *
339 * @param object $post Post object.
340 * @return string Color class names.
341 */
342 private function get_post_category_color_classes($post): string
343 {
344 $classes = [];
345 $taxonomy = 'category'; // Default to category for AJAX requests
346
347 // Get post categories/terms
348 $terms = get_the_terms($post->ID, $taxonomy);
349
350 if ($terms && !is_wp_error($terms)) {
351 foreach ($terms as $term) {
352 $classes[] = 'filter-' . $term->slug;
353 $classes[] = 'king-addons-dpg-category-' . $term->slug;
354 }
355 }
356
357 // Add default class if no terms
358 if (empty($classes)) {
359 $classes[] = 'king-addons-dpg-category-default';
360 }
361
362 return implode(' ', $classes);
363 }
364
365 /**
366 * Get post type icon (fallback icons for AJAX context).
367 *
368 * @param object $post Post object.
369 * @return string Icon HTML.
370 */
371 private function get_post_type_icon($post): string
372 {
373 // Get post categories/terms
374 $taxonomy = 'category';
375 $terms = get_the_terms($post->ID, $taxonomy);
376
377 if ($terms && !is_wp_error($terms)) {
378 // Use first category to determine icon type
379 $term = $terms[0];
380
381 // Map common category names to icons
382 $category_icon_map = [
383 'report' => '<i class="far fa-file-alt"></i>',
384 'reports' => '<i class="far fa-file-alt"></i>',
385 'publication' => '<i class="far fa-newspaper"></i>',
386 'publications' => '<i class="far fa-newspaper"></i>',
387 'video' => '<i class="far fa-play-circle"></i>',
388 'videos' => '<i class="far fa-play-circle"></i>',
389 'website' => '<i class="fas fa-globe"></i>',
390 'websites' => '<i class="fas fa-globe"></i>',
391 'news' => '<i class="far fa-newspaper"></i>',
392 'blog' => '<i class="far fa-newspaper"></i>',
393 'article' => '<i class="far fa-newspaper"></i>',
394 'articles' => '<i class="far fa-newspaper"></i>',
395 ];
396
397 $slug = strtolower($term->slug);
398 if (isset($category_icon_map[$slug])) {
399 return $category_icon_map[$slug];
400 }
401 }
402
403 // Fallback based on post format
404 $format = get_post_format($post->ID);
405
406 if ($format === 'video') {
407 return '<i class="far fa-play-circle"></i>';
408 } elseif ($format === 'audio') {
409 return '<i class="far fa-newspaper"></i>';
410 } elseif ($post->post_type === 'page') {
411 return '<i class="fas fa-globe"></i>';
412 } else {
413 return '<i class="far fa-file-alt"></i>';
414 }
415 }
416
417 /**
418 * Sanitize array values.
419 *
420 * @param array $array Array to sanitize.
421 * @return array Sanitized array.
422 */
423 private function sanitize_array(array $array): array
424 {
425 return array_map('sanitize_text_field', $array);
426 }
427 }