PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.37
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.37
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 51.1.45 51.1.46 51.1.47 51.1.49 All 37 releases
king-addons / includes / extensions / Templates / Templates.php

Templates.php in King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder 51.1.37, at includes/extensions/Templates/Templates.php

1,168 lines 54.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 */
4
5 namespace King_Addons;
6
7 use Exception;
8
9 if (!defined('ABSPATH')) {
10 exit;
11 }
12
13 final class Templates
14 {
15 private static ?Templates $instance = null;
16
17 public static function instance(): ?Templates
18 {
19 if (is_null(self::$instance)) {
20 self::$instance = new self();
21 }
22 return self::$instance;
23 }
24
25 public function render_template_catalog_page(): void
26 {
27 $templates = TemplatesMap::getTemplatesMapArray();
28 $collections = CollectionsMap::getCollectionsMapArray();
29
30 uasort($collections, function ($a, $b) {
31 return strcasecmp($a, $b);
32 });
33
34 $is_premium_active = king_addons_freemius()->can_use_premium_code();
35
36 // TODO: TEST: For UI testing, it doesn't enable the real premium
37 // $is_premium_active = false;
38
39 // Arrays for categories and tags
40 $categories = [];
41 $tags = [];
42 $category_counts = [];
43
44 // Getting unique categories and tags
45 foreach ($templates['templates'] as $template) {
46 if (!in_array($template['category'], $categories)) {
47 $categories[] = $template['category'];
48 }
49
50 foreach ($template['tags'] as $tag) {
51 if (!in_array($tag, $tags)) {
52 $tags[] = $tag;
53 }
54 }
55
56 $category = $template['category'];
57 $category_counts[$category] = isset($category_counts[$category]) ? $category_counts[$category] + 1 : 1;
58 }
59
60 sort($categories);
61
62 // Get filters from query
63 $search_query = isset($_GET['s']) ? sanitize_text_field($_GET['s']) : '';
64 $selected_category = isset($_GET['category']) ? sanitize_text_field($_GET['category']) : '';
65 $selected_collection = isset($_GET['collection']) ? sanitize_text_field($_GET['collection']) : '';
66 $selected_tags = isset($_GET['tags']) ? array_filter(explode(',', sanitize_text_field($_GET['tags']))) : [];
67 $current_page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
68
69 // Use the common function to get filtered templates and pagination
70 $result = $this->get_filtered_templates($templates, $search_query, $selected_category, $selected_tags, $selected_collection, $current_page);
71
72 if (isset($_GET['ajax']) && $_GET['ajax']) {
73 wp_send_json_success(['grid_html' => $result['grid_html'], 'pagination_html' => $result['pagination_html']]);
74 }
75 if ($is_premium_active) {
76 ?>
77 <script type="text/javascript">
78 (function() {
79 window.kingAddons = window.kingAddons || {};
80 window.kingAddons.installId = <?php
81 echo json_encode(king_addons_freemius()->get_site()->id);
82 ?>;
83 })();
84 </script>
85 <?php
86 }
87 ?>
88 <div id="king-addons-templates-top"></div>
89 <div id="king-addons-templates" class="king-addons-templates">
90 <div class="kng-intro">
91 <div class="kng-intro-wrap">
92 <div class="kng-intro-wrap-1">
93 <h1 class="kng-intro-title"><?php echo esc_html__('King Addons for Elementor', 'king-addons'); ?></h1>
94 <?php if ($is_premium_active): ?>
95 <span class="premium-active-txt"><?php echo esc_html__('PREMIUM', 'king-addons'); ?></h1></span>
96 <?php endif; ?>
97 <h2 class="kng-intro-subtitle"><?php echo esc_html__('Discover professionally designed, attention-grabbing, and SEO-optimized templates perfect for any site', 'king-addons'); ?></h2>
98 </div>
99 <div class="kng-intro-wrap-2">
100 <div class="kng-navigation">
101 <div class="kng-nav-item kng-nav-item-current">
102 <a href="<?php echo admin_url('admin.php?page=king-addons'); ?>">
103 <div class="kng-nav-item-txt"><?php echo esc_html__('Free Widgets & Features', 'king-addons'); ?></div>
104 </a>
105 </div>
106 <?php if (KING_ADDONS_EXT_HEADER_FOOTER_BUILDER): ?>
107 <div class="kng-nav-item kng-nav-item-current">
108 <a href="<?php echo admin_url('edit.php?post_type=king-addons-el-hf'); ?>">
109 <div class="kng-nav-item-txt"><?php echo esc_html__('Free Header & Footer Builder', 'king-addons'); ?></div>
110 </a>
111 </div>
112 <?php endif; ?>
113 <?php if (KING_ADDONS_EXT_POPUP_BUILDER): ?>
114 <div class="kng-nav-item kng-nav-item-current">
115 <a href="<?php echo admin_url('admin.php?page=king-addons-popup-builder'); ?>">
116 <div class="kng-nav-item-txt"><?php echo esc_html__('Free Popup Builder', 'king-addons'); ?></div>
117 </a>
118 </div>
119 <?php endif; ?>
120 <?php if (!king_addons_freemius()->can_use_premium_code()): ?>
121 <div class="kng-nav-item kng-nav-item-current kng-nav-activate-license">
122 <a id="activate-license-btn">
123 <img src="<?php echo esc_url(KING_ADDONS_URL) . 'includes/admin/img/up.svg'; ?>"
124 alt="<?php echo esc_html__('Activate License', 'king-addons'); ?>">
125 <div class="kng-nav-item-txt"><?php echo esc_html__('Activate License', 'king-addons'); ?></div>
126 </a>
127 </div>
128 <?php endif; ?>
129 <?php if (!king_addons_freemius()->can_use_premium_code()): ?>
130 <div class="kng-promo-btn-wrap">
131 <a href="https://kingaddons.com/pricing/?rel=king-addons-templates-catalog" target="_blank">
132 <div class="kng-promo-btn-txt">
133 <?php esc_html_e('Unlock Premium Features & 650+ Templates Today!', 'king-addons'); ?>
134 </div>
135 <img width="16px"
136 src="<?php echo esc_url(KING_ADDONS_URL) . 'includes/admin/img/share-v2.svg'; ?>"
137 alt="<?php echo esc_html__('Open link in the new tab', 'king-addons'); ?>">
138 </a>
139 </div>
140 <?php endif; ?>
141 </div>
142 </div>
143 </div>
144 </div>
145 <!-- Catalog Tabs -->
146 <div class="king-addons-catalog-tabs">
147 <button class="king-addons-tab-button active" data-tab="templates">
148 <i class="eicon-document-file"></i>
149 <?php esc_html_e('Templates', 'king-addons'); ?>
150 <span class="tab-count"><?php echo count($templates['templates']); ?></span>
151 </button>
152 <?php if (KING_ADDONS_EXT_SECTIONS_CATALOG): ?>
153 <button class="king-addons-tab-button" data-tab="sections">
154 <i class="eicon-section"></i>
155 <?php esc_html_e('Sections', 'king-addons'); ?>
156 <span class="tab-count" id="sections-count">0</span>
157 </button>
158 <?php endif; ?>
159 </div>
160
161 <!-- Templates Tab Content -->
162 <div id="templates-catalog" class="king-addons-tab-content active">
163 <div class="filters-wrapper">
164 <div class="filters">
165 <select id="template-category">
166 <option value=""><?php esc_html_e('All Categories', 'king-addons'); ?>
167 (<?php echo count($templates['templates']); ?>)
168 </option>
169 <?php foreach ($categories as $category): ?>
170 <option value="<?php echo esc_attr($category); ?>" <?php selected($selected_category, $category); ?>>
171 <?php echo esc_html(ucwords(str_replace('-', ' ', $category))); ?>
172 (<?php echo $category_counts[$category]; ?>)
173 </option>
174 <?php endforeach; ?>
175 </select>
176 <select id="template-collection">
177 <option value=""><?php esc_html_e('All Collections', 'king-addons'); ?>
178 (<?php echo count($collections); ?>)
179 </option>
180 <?php foreach ($collections as $id => $name): ?>
181 <option value="<?php echo esc_attr($id); ?>" <?php selected($selected_collection, $id); ?>>
182 <?php echo esc_html($name); ?>
183 </option>
184 <?php endforeach; ?>
185 </select>
186 <div id="template-tags">
187 <?php
188 shuffle($tags); ?>
189 <div class="tags-header">Tags</div>
190 <?php foreach ($tags as $tag): ?>
191 <label>
192 <input type="checkbox"
193 value="<?php echo esc_attr($tag); ?>" <?php echo in_array($tag, $selected_tags) ? 'checked' : ''; ?>> <?php echo esc_html(ucwords(str_replace('-', ' ', $tag))); ?>
194 </label>
195 <?php endforeach; ?>
196 </div>
197 <button id="reset-filters"><?php esc_html_e('Reset Search & Filters', 'king-addons'); ?></button>
198 <?php if (!$is_premium_active): ?>
199 <div class="promo-wrapper">
200 <div class="promo-txt"><?php
201 esc_html_e('Unlock Premium Templates', 'king-addons');
202 echo '<ul><li>$4.99/month</li>';
203 echo '<li>Unlimited Downloads</li>';
204 echo '<li>Keep all templates forever</li></ul>';
205 ?></div>
206 <a class="purchase-btn"
207 href="https://kingaddons.com/pricing/?utm_source=kng-templates-banner-side&utm_medium=plugin&utm_campaign=kng"
208 target="_blank">
209 <button class="promo-btn purchase-btn"
210 style="display: flex;align-items: center;">
211 <img src="<?php echo esc_url(KING_ADDONS_URL) . 'includes/admin/img/icon-for-admin.svg'; ?>"
212 style="margin-right: 5px;width: 14px;height: 14px;"
213 alt="<?php echo esc_html__('Upgrade Now', 'king-addons'); ?>"><?php esc_html_e('Upgrade Now', 'king-addons'); ?>
214 </button>
215 </a>
216 </div>
217 <?php endif; ?>
218 </div>
219 </div>
220 <div class="templates-grid-wrapper">
221 <div class="search-wrapper">
222 <input type="text" id="template-search" value="<?php echo esc_attr($search_query); ?>"
223 placeholder="<?php esc_attr_e('Search templates...', 'king-addons'); ?>">
224 </div>
225 <div class="templates-grid">
226 <?php echo $result['grid_html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
227 ?>
228 </div>
229 <div class="pagination">
230 <?php echo $result['pagination_html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
231 ?>
232 </div>
233 </div>
234 </div>
235
236 <!-- Sections Tab Content -->
237 <?php if (KING_ADDONS_EXT_SECTIONS_CATALOG): ?>
238 <div id="sections-catalog" class="king-addons-tab-content">
239 <div class="filters-wrapper">
240 <div class="filters">
241 <select id="sections-category">
242 <option value=""><?php esc_html_e('All Categories', 'king-addons'); ?></option>
243 </select>
244 <select id="sections-type">
245 <option value=""><?php esc_html_e('All Types', 'king-addons'); ?></option>
246 </select>
247 <select id="sections-plan">
248 <option value=""><?php esc_html_e('All Plans', 'king-addons'); ?></option>
249 <option value="free"><?php esc_html_e('Free', 'king-addons'); ?></option>
250 <?php if ($is_premium_active): ?>
251 <option value="premium"><?php esc_html_e('Premium', 'king-addons'); ?></option>
252 <?php endif; ?>
253 </select>
254 <button id="sections-reset-filters"><?php esc_html_e('Reset Search & Filters', 'king-addons'); ?></button>
255 </div>
256 </div>
257 <div class="sections-grid-wrapper">
258 <div class="search-wrapper">
259 <input type="text" id="sections-search" placeholder="<?php esc_attr_e('Search sections...', 'king-addons'); ?>">
260 </div>
261 <div class="sections-grid">
262 <div class="sections-loading">
263 <?php esc_html_e('Loading sections...', 'king-addons'); ?>
264 </div>
265 </div>
266 <div class="sections-pagination"></div>
267 </div>
268 </div>
269 <?php endif; ?>
270
271 <div id="template-preview-popup" style="display:none;">
272 <div class="popup-content">
273 <div class="popup-content-nav">
274 <button data-plan-active="<?php echo ($is_premium_active) ? 'premium' : 'free'; ?>"
275 id="install-template">
276 <?php esc_html_e('Import Template', 'king-addons'); ?>
277 </button>
278 <a href="#" id="template-preview-link" target="_blank">
279 <?php esc_html_e('Live Preview', 'king-addons'); ?>
280 </a>
281 <div class="preview-mode-switcher">
282 <button data-mode="desktop" class="active" id="preview-desktop">
283 <span class="dashicons dashicons-desktop"></span>
284 </button>
285 <button data-mode="tablet" id="preview-tablet">
286 <span class="dashicons dashicons-tablet"></span>
287 </button>
288 <button data-mode="mobile" id="preview-mobile">
289 <span class="dashicons dashicons-smartphone"></span>
290 </button>
291 </div>
292 <button id="close-popup">
293 <?php esc_html_e('Close Preview X', 'king-addons'); ?>
294 </button>
295 </div>
296 <iframe id="template-preview-iframe" src="" frameborder="0"></iframe>
297 </div>
298 </div>
299 <div id="template-installing-popup" style="display:none;">
300 <div class="popup-content">
301 <div id="progress-container">
302 <div id="progress-bar">
303 0%
304 </div>
305 </div>
306 <div id="progress"></div>
307 <div id="image-list"></div>
308 <div id="final_response"></div>
309 <button id="close-installing-popup"
310 style="display:none;"><?php esc_html_e('Close', 'king-addons'); ?></button>
311 <a href="#" id="go-to-imported-page"
312 style="display:none;"><?php esc_html_e('Go to imported page', 'king-addons'); ?></a>
313 </div>
314 </div>
315 <div id="license-activating-popup" style="display:none;">
316 <div class="license-activating-popup-content">
317 <div class="license-activating-popup-txt"><?php esc_html_e('1. Download and install the premium version of the plugin - King Addons Pro. You can find the link in the email received after the license purchase.', 'king-addons'); ?></div>
318 <div class="license-activating-popup-txt"><?php esc_html_e('2. Go to the Plugins page.', 'king-addons'); ?></div>
319 <div class="license-activating-popup-txt"><?php esc_html_e('3. Find the King Addons Pro plugin.', 'king-addons'); ?></div>
320 <div class="license-activating-popup-txt"><?php esc_html_e('4. Click on Activate License link.', 'king-addons'); ?></div>
321 <div class="license-activating-popup-txt"><?php esc_html_e('5. Enter the License Key provided in the email. Done!', 'king-addons'); ?></div>
322 <button id="close-license-activating-popup"><?php esc_html_e('Close', 'king-addons'); ?></button>
323 </div>
324 </div>
325 <div id="premium-promo-popup" style="display:none;">
326 <div class="premium-promo-popup-content">
327 <div class="premium-promo-popup-wrapper">
328 <div class="premium-promo-popup-txt"><?php
329
330 echo '<span class="pr-popup-title">Want This Premium Template?</span>';
331 echo '<br><span class="pr-popup-desc">';
332 echo 'Get <span class="pr-popup-desc-b">unlimited downloads</span> for just';
333 echo ' <span class="pr-popup-desc-b">$4.99/month';
334 echo '</span> — keep them <span class="pr-popup-desc-b">even after</span> your subscription ends!';
335 echo '</span><span class="pr-popup-desc" style="font-size: 16px;opacity: 0.6;">Trusted by 20,000+ users</span>';
336 ?></div>
337 <a class="purchase-btn"
338 href="https://kingaddons.com/pricing/?utm_source=kng-templates-banner-pro&utm_medium=plugin&utm_campaign=kng"
339 target="_blank">
340 <button class="premium-promo-popup-purchase-btn purchase-btn">
341 <img src="<?php echo esc_url(KING_ADDONS_URL) . 'includes/admin/img/icon-for-admin.svg'; ?>"
342 style="margin-right: 7px;width: 16px;height: 16px;"
343 alt="<?php echo esc_html__('Unlock All Templates', 'king-addons'); ?>"><?php esc_html_e('Unlock All Templates', 'king-addons'); ?>
344 </button>
345 </a>
346 <button id="close-premium-promo-popup"><?php esc_html_e('Close', 'king-addons'); ?></button>
347 </div>
348 </div>
349 </div>
350 </div>
351 <?php
352 }
353
354 private function get_filtered_templates($templates, $search_query, $selected_category, $selected_tags, $selected_collection, $current_page): array
355 {
356 $search_terms = array_filter(explode(' ', $search_query));
357 $has_search = !empty($search_terms);
358 // Custom: direct key search
359 $search_by_key = false;
360 $found_by_key = [];
361 if (!empty($search_query) && isset($templates['templates'][$search_query])) {
362 $found_by_key[] = $templates['templates'][$search_query];
363 $found_by_key[0]['template_key'] = $search_query;
364 $search_by_key = true;
365 }
366
367 // Filter templates based on search and selected filters
368 if ($search_by_key) {
369 $filtered_templates = $found_by_key;
370 } elseif (!$has_search) {
371 $filtered_templates = array_filter($templates['templates'], function ($template) use ($search_terms, $selected_category, $selected_tags, $selected_collection) {
372
373 foreach ($search_terms as $term) {
374 $found_in_title = stripos($template['title'], $term) !== false;
375 $found_in_tags = false;
376
377 foreach ($template['tags'] as $tag) {
378 if (stripos($tag, $term) !== false) {
379 $found_in_tags = true;
380 break;
381 }
382 }
383
384 if (!$found_in_title && !$found_in_tags) {
385 return false;
386 }
387 }
388
389 if ($selected_category && $template['category'] !== $selected_category) {
390 return false;
391 }
392
393 if ($selected_tags) {
394 $template_tags = $template['tags'];
395 foreach ($selected_tags as $tag) {
396 if (!in_array($tag, $template_tags)) {
397 return false;
398 }
399 }
400 }
401
402 if ($selected_collection && $template['collection'] != $selected_collection) {
403 return false;
404 }
405
406 return true;
407 });
408
409 // Shuffle templates
410 // shuffle($filtered_templates);
411
412 } else {
413
414 $filtered_templates = $templates['templates'];
415
416 if (!empty($search_terms) || !empty($selected_category) || !empty($selected_tags) || !empty($selected_collection)) {
417 $matched_by_title = [];
418 $matched_by_tags = [];
419
420 foreach ($filtered_templates as $key => $template) {
421
422 $found_in_title = false;
423 $found_in_tags = false;
424
425 foreach ($search_terms as $term) {
426 if ($term && stripos($template['title'], $term) !== false) {
427 $found_in_title = true;
428 }
429
430 foreach ($template['tags'] as $tag) {
431 if (stripos($tag, $term) !== false) {
432 $found_in_tags = true;
433 break;
434 }
435 }
436 // if (!$found_in_title && !$found_in_tags) {
437 // continue;
438 // }
439 }
440
441 if ($selected_category && $template['category'] !== $selected_category) {
442 continue;
443 }
444
445 if ($selected_tags) {
446 $template_tags = $template['tags'];
447 foreach ($selected_tags as $tag) {
448 if (!in_array($tag, $template_tags)) {
449 continue 2;
450 }
451 }
452 }
453
454 if ($selected_collection && $template['collection'] != $selected_collection) {
455 continue;
456 }
457
458 // Attach the template key for later use
459 $template['template_key'] = $key;
460
461 if ($found_in_title) {
462 $matched_by_title[] = $template;
463 } elseif ($found_in_tags) {
464 $matched_by_tags[] = $template;
465 }
466 }
467
468 // Combine the arrays, so templates matching by title come first
469 $filtered_templates = array_merge($matched_by_title, $matched_by_tags);
470 }
471 }
472
473 // Pagination setup
474 $items_per_page = 20;
475 $total_templates = count($filtered_templates);
476 $offset = ($current_page - 1) * $items_per_page;
477 $paged_templates = array_slice($filtered_templates, $offset, $items_per_page);
478
479 ob_start();
480 if (empty($paged_templates)) {
481 echo '<p class="templates-not-found">' . esc_html__('No templates found.', 'king-addons') . '</p>';
482 } else {
483 foreach ($paged_templates as $key => $template) {
484 $attr_key = ($has_search) ? $template['template_key'] : $key;
485 ?>
486 <div class="template-item"
487 data-category="<?php echo esc_attr($template['category']); ?>"
488 data-tags="<?php echo esc_attr(implode(',', $template['tags'])); ?>"
489 data-template-key="<?php echo esc_attr($attr_key); ?>"
490 data-template-plan="<?php echo esc_attr($template['plan']); ?>">
491 <img class="kng-addons-template-thumbnail" loading="lazy"
492 src="<?php echo esc_url("https://thumbnails.kingaddons.com/$attr_key.png?v=4"); ?>"
493 alt="<?php echo esc_attr($template['title']); ?>">
494 <h3><?php echo esc_html($template['title']); ?></h3>
495 <div class="template-plan template-plan-<?php echo esc_html($template['plan']); ?>"><?php echo esc_html($template['plan']); ?></div>
496 </div>
497 <?php
498 }
499 }
500 $grid_html = ob_get_clean();
501
502 ob_start();
503
504 $pages = paginate_links(array(
505 'base' => add_query_arg(array(
506 'paged' => '%#%',
507 's' => $search_query,
508 'category' => $selected_category,
509 'collection' => $selected_collection,
510 'tags' => implode(',', $selected_tags),
511 )),
512 'format' => '?paged=%#%',
513 'current' => $current_page,
514 'total' => ceil($total_templates / $items_per_page),
515 'prev_text' => __('&larr; Previous', 'king-addons'),
516 'next_text' => __('Next &rarr;', 'king-addons'),
517 'end_size' => 9,
518 'mid_size' => 3,
519 ));
520 if ($pages) {
521 echo '<div id="king-addons-pagination-inner-wrap" class="pagination-inner-wrap"><div class="pagination-inner">';
522 echo $pages; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
523 echo '</div></div>';
524 }
525
526 $pagination_html = ob_get_clean();
527
528 return ['grid_html' => $grid_html, 'pagination_html' => $pagination_html];
529 }
530
531 public function king_addons_enqueue_scripts(): void
532 {
533 $screen = get_current_screen();
534 if ($screen->id === 'toplevel_page_king-addons-templates') {
535 wp_enqueue_style('king-addons-templates-style', KING_ADDONS_URL . 'includes/admin/css/templates.css', '', KING_ADDONS_VERSION);
536
537 if (!wp_script_is(KING_ADDONS_ASSETS_UNIQUE_KEY . '-' . 'jquery' . '-' . 'jquery')) {
538 wp_enqueue_script(KING_ADDONS_ASSETS_UNIQUE_KEY . '-' . 'jquery' . '-' . 'jquery', '', '', KING_ADDONS_VERSION);
539 }
540
541 wp_enqueue_script('king-addons-templates-script', KING_ADDONS_URL . 'includes/admin/js/templates.js', '', KING_ADDONS_VERSION, true);
542
543 wp_localize_script('king-addons-templates-script', 'kingAddonsData', array(
544 'adminUrl' => admin_url('admin-post.php'),
545 'ajaxUrl' => admin_url('admin-ajax.php'),
546 'nonce' => wp_create_nonce('kingAddonsNonce'),
547 ));
548 }
549 // if ($screen->id === 'king-addons_page_king-addons-account') {
550 // wp_enqueue_style('king-addons-account-style', KING_ADDONS_URL . 'includes/admin/css/account.css', '', KING_ADDONS_VERSION);
551 // }
552 }
553
554 public function __construct()
555 {
556 add_action('admin_enqueue_scripts', array($this, 'king_addons_enqueue_scripts'));
557
558 // Get the performance setting
559 $improve_import = get_option('king_addons_improve_import_performance', '1');
560
561 // Conditionally add time limit adjustments based on setting
562 if ($improve_import === '1') {
563 add_action('wp_ajax_import_elementor_page_with_images', function () {
564 @set_time_limit(300);
565 }, 0);
566
567 add_action('wp_ajax_process_import_images', function () {
568 @set_time_limit(300);
569 }, 0);
570 }
571
572 add_action(
573 'wp_ajax_import_elementor_page_with_images',
574 [$this, 'import_elementor_page_with_images']
575 );
576
577 add_action(
578 'wp_ajax_process_import_images',
579 [$this, 'process_import_images']
580 );
581
582 add_action('wp_ajax_filter_templates', array($this, 'handle_filter_templates'));
583
584 // Sections catalog endpoints
585 add_action('wp_ajax_king_addons_get_sections_catalog', array($this, 'handle_get_sections_catalog'));
586 add_action('wp_ajax_king_addons_import_section_admin', array($this, 'handle_import_section_to_page'));
587
588 add_action('http_api_curl', array($this, 'set_custom_curl_options'), 10, 3);
589 }
590
591 public function set_custom_curl_options($handle)
592 {
593 curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 60);
594 curl_setopt($handle, CURLOPT_DNS_CACHE_TIMEOUT, 300);
595 curl_setopt($handle, CURLOPT_TIMEOUT, 300);
596 }
597
598 function handle_filter_templates(): void
599 {
600 if (!current_user_can('manage_options')) {
601 return;
602 }
603
604 if (!isset($_POST['action']) || $_POST['action'] !== 'filter_templates') {
605 wp_send_json_error('Invalid request');
606 return;
607 }
608
609 $search_query = isset($_POST['s']) ? sanitize_text_field($_POST['s']) : '';
610 $selected_category = isset($_POST['category']) ? sanitize_text_field($_POST['category']) : '';
611 $selected_collection = isset($_POST['collection']) ? sanitize_text_field($_POST['collection']) : '';
612 $selected_tags = isset($_POST['tags']) ? array_filter(explode(',', sanitize_text_field($_POST['tags']))) : [];
613 $current_page = isset($_POST['paged']) ? max(1, intval($_POST['paged'])) : 1;
614
615 $templates = TemplatesMap::getTemplatesMapArray();
616
617 // Use the common function to get filtered templates and pagination
618 $result = $this->get_filtered_templates($templates, $search_query, $selected_category, $selected_tags, $selected_collection, $current_page);
619
620 wp_send_json_success(['grid_html' => $result['grid_html'], 'pagination_html' => $result['pagination_html']]);
621 }
622
623 public function import_elementor_page_with_images(): void
624 {
625 if (!current_user_can('manage_options')) {
626 return;
627 }
628
629 $import_data = json_decode(stripslashes($_POST['data']), true);
630
631 if (json_last_error() !== JSON_ERROR_NONE) {
632 wp_send_json_error('JSON decode error: ' . json_last_error_msg());
633 return;
634 }
635
636 if (isset($import_data['content']) && isset($import_data['images']) && isset($import_data['title'])) {
637 $content = $import_data['content'];
638 $image_data = $import_data['images'];
639
640 $page_title = sanitize_text_field($import_data['title']);
641 $elementor_version = sanitize_text_field($import_data['elementor_version']);
642
643 // Check if this is for existing page (from popup import)
644 $existing_page_id = isset($import_data['existing_page_id']) ? intval($import_data['existing_page_id']) : 0;
645 $create_new_page = isset($import_data['create_new_page']) ? (bool)$import_data['create_new_page'] : true;
646
647 delete_transient('elementor_import_content');
648 delete_transient('elementor_import_images');
649 delete_transient('elementor_import_total_images');
650 delete_transient('elementor_import_images_processed');
651 delete_transient('elementor_import_image_retry_count');
652 delete_transient('elementor_import_page_title');
653 delete_transient('elementor_import_elementor_version');
654 delete_transient('elementor_import_existing_page_id');
655 delete_transient('elementor_import_create_new_page');
656
657 set_transient('elementor_import_content', $content, 60 * 60);
658 set_transient('elementor_import_images', $image_data, 60 * 60);
659 set_transient('elementor_import_total_images', count($image_data), 60 * 60);
660 set_transient('elementor_import_images_processed', 0, 60 * 60);
661 set_transient('elementor_import_image_retry_count', [], 60 * 60);
662 set_transient('elementor_import_page_title', $page_title, 60 * 60);
663 set_transient('elementor_import_elementor_version', $elementor_version, 60 * 60);
664 set_transient('elementor_import_existing_page_id', $existing_page_id, 60 * 60);
665 set_transient('elementor_import_create_new_page', $create_new_page, 60 * 60);
666
667 error_log('King Addons Import: Initialized ' . ($create_new_page ? 'new page' : 'existing page') . ' import. Existing page ID: ' . $existing_page_id);
668
669 wp_send_json_success([
670 'message' => 'Import initialized.',
671 'images' => $image_data,
672 'existing_page_id' => $existing_page_id,
673 'create_new_page' => $create_new_page
674 ]);
675 } else {
676 wp_send_json_error('Invalid import data.');
677 }
678 }
679
680 private function replace_image_data(array &$array, $old_url, $new_url, $old_id, $new_id)
681 {
682 foreach ($array as $key => &$value) {
683 if (is_array($value)) {
684 // Standard structure with both 'url' and 'id'
685 if (
686 isset($value['url'], $value['id']) &&
687 $value['url'] === $old_url &&
688 $value['id'] === $old_id
689 ) {
690 $value['url'] = $new_url;
691 $value['id'] = $new_id;
692 }
693
694 // Background image structure (common in Elementor)
695 if (
696 isset($value['url']) &&
697 $value['url'] === $old_url &&
698 (!isset($value['id']) || $value['id'] === $old_id)
699 ) {
700 $value['url'] = $new_url;
701 if (isset($value['id'])) {
702 $value['id'] = $new_id;
703 }
704 }
705
706 // Recursively check deeper nested arrays
707 $this->replace_image_data($value, $old_url, $new_url, $old_id, $new_id);
708 } elseif (is_string($value)) {
709 // Direct URL string replacement
710 if ($value === $old_url) {
711 $array[$key] = $new_url;
712 }
713
714 // CSS background-image style replacement
715 if (strpos($value, 'background-image:') !== false && strpos($value, $old_url) !== false) {
716 $array[$key] = str_replace($old_url, $new_url, $value);
717 }
718
719 // URL() function replacement in CSS
720 if (strpos($value, 'url(') !== false && strpos($value, $old_url) !== false) {
721 $array[$key] = str_replace($old_url, $new_url, $value);
722 }
723
724 // JSON string containing URLs (for widget settings)
725 if (strpos($value, '{') === 0 && strpos($value, $old_url) !== false) {
726 $decoded = json_decode($value, true);
727 if (is_array($decoded)) {
728 $this->replace_image_data($decoded, $old_url, $new_url, $old_id, $new_id);
729 $array[$key] = json_encode($decoded);
730 }
731 }
732 }
733 }
734 }
735
736 public function process_import_images(): void
737 {
738 // Conditionally increase execution time limit based on setting
739 $improve_import = get_option('king_addons_improve_import_performance', '1');
740 if ($improve_import === '1') {
741 @set_time_limit(300);
742 }
743
744 if (!current_user_can('manage_options')) {
745 wp_send_json_error('The current user can not manage options and create pages. Please change it in the WordPress settings.');
746 return;
747 }
748
749 $start_time = time();
750 $timeout = 30;
751
752 $content = get_transient('elementor_import_content');
753 $image_data = get_transient('elementor_import_images');
754 $total_images = get_transient('elementor_import_total_images');
755 $images_processed = get_transient('elementor_import_images_processed');
756 $image_retry_count = get_transient('elementor_import_image_retry_count');
757 $page_title = get_transient('elementor_import_page_title');
758 $elementor_version = get_transient('elementor_import_elementor_version');
759 $existing_page_id = get_transient('elementor_import_existing_page_id');
760 $create_new_page = get_transient('elementor_import_create_new_page');
761
762 if (!is_array($image_retry_count)) {
763 $image_retry_count = [];
764 }
765
766 if ($images_processed < $total_images) {
767 $current_image = $image_data[$images_processed];
768 $url = $current_image['url'];
769 if (!isset($image_retry_count[$url])) {
770 $image_retry_count[$url] = 0;
771 }
772
773 $new_image_id = $this->download_image_to_media_gallery($url, $image_retry_count[$url]);
774 if ($new_image_id === false) {
775 $image_retry_count[$url]++;
776 set_transient('elementor_import_image_retry_count', $image_retry_count, 60 * 60);
777
778 if ($image_retry_count[$url] > 3) {
779 $images_processed++;
780 set_transient('elementor_import_images_processed', $images_processed, 60 * 60);
781 $progress = round(($images_processed / $total_images) * 90) + 10;
782
783 wp_send_json_success([
784 'progress' => $progress,
785 'message' => "Processed $images_processed of $total_images images.",
786 'image_url' => $url,
787 'images_processed' => $images_processed,
788 'new_image_id' => 'SKIPPED'
789 ]);
790 } else {
791 wp_send_json_error([
792 'retry' => true,
793 'image_url' => $url
794 ]);
795 }
796 } else {
797 $new_url = wp_get_attachment_url($new_image_id);
798 $images_processed++;
799 set_transient('elementor_import_images_processed', $images_processed, 60 * 60);
800 $progress = round(($images_processed / $total_images) * 90) + 10;
801
802 $this->replace_image_data($content, $url, $new_url, $current_image['id'], $new_image_id);
803
804 set_transient('elementor_import_content', $content, 60 * 60);
805
806 wp_send_json_success([
807 'progress' => $progress,
808 'message' => "Processed $images_processed of $total_images images.",
809 'image_url' => $url,
810 'new_image_url' => $new_url
811 ]);
812 }
813 } else {
814 // All images processed
815 if ($create_new_page) {
816 // Original behavior - create new page
817 $new_post_id = wp_insert_post([
818 'post_title' => $page_title,
819 'post_content' => '',
820 'post_status' => 'publish',
821 'post_type' => 'page',
822 ]);
823
824 if ($new_post_id) {
825 update_post_meta($new_post_id, '_elementor_data', wp_slash(json_encode($content)));
826 update_post_meta($new_post_id, '_elementor_edit_mode', 'builder');
827 update_post_meta($new_post_id, '_elementor_template_type', 'page');
828 update_post_meta($new_post_id, '_elementor_version', $elementor_version);
829
830 update_post_meta($new_post_id, '_wp_page_template', 'elementor_canvas');
831
832 update_post_meta($new_post_id, '_wp_gutenberg_disable', '1');
833 update_post_meta($new_post_id, '_wp_gutenberg_enabled', '0');
834
835 delete_transient('elementor_import_content');
836 delete_transient('elementor_import_images');
837 delete_transient('elementor_import_total_images');
838 delete_transient('elementor_import_images_processed');
839 delete_transient('elementor_import_image_retry_count');
840 delete_transient('elementor_import_page_title');
841 delete_transient('elementor_import_elementor_version');
842 delete_transient('elementor_import_existing_page_id');
843 delete_transient('elementor_import_create_new_page');
844
845 wp_send_json_success([
846 'message' => "Page imported successfully!",
847 'page_url' => get_permalink($new_post_id),
848 ]);
849 } else {
850 wp_send_json_error('Failed to import page.');
851 }
852 } else {
853 // New behavior - for existing page, just signal completion
854 // Content and images are processed and stored in transients
855 // Will be merged by king_addons_merge_with_existing_page endpoint
856
857 error_log('King Addons Import: Image processing completed for existing page. Content ready for merge.');
858
859 wp_send_json_success([
860 'message' => "Image processing completed! Content ready for merge.",
861 'images_processed' => $images_processed,
862 'existing_page_id' => $existing_page_id,
863 'processing_complete' => true
864 ]);
865 }
866 }
867
868 if (time() >= $start_time + $timeout) {
869 wp_send_json_error('Process timeout, please resume the import.');
870 }
871 }
872
873 public function download_image_to_media_gallery($image_url, $image_retry_count)
874 {
875 try {
876 $response = wp_remote_get($image_url, ['timeout' => 30]);
877
878 if (is_wp_error($response)) {
879 throw new Exception('HTTP request error: ' . $response->get_error_message());
880 }
881
882 $status_code = wp_remote_retrieve_response_code($response);
883 if ($status_code !== 200) {
884 throw new Exception(
885 'HTTP status code ' . $status_code . ' when trying to download: ' . $image_url
886 );
887 }
888
889 $image_data = wp_remote_retrieve_body($response);
890 if (empty($image_data)) {
891 throw new Exception('Failed to retrieve image data from URL: ' . $image_url);
892 }
893
894 $image_name = pathinfo(basename($image_url), PATHINFO_FILENAME);
895 $image_extension = pathinfo(basename($image_url), PATHINFO_EXTENSION);
896 $unique_image_name = $image_name . '-' . time() . '.' . $image_extension;
897
898 $upload_dir = wp_upload_dir();
899 if (! file_exists($upload_dir['path'])) {
900 wp_mkdir_p($upload_dir['path']);
901 }
902 $image_file = $upload_dir['path'] . '/' . $unique_image_name;
903
904 if (file_put_contents($image_file, $image_data) === false) {
905 throw new Exception('Failed to write image data to file: ' . $image_file);
906 }
907
908 $wp_filetype = wp_check_filetype($unique_image_name);
909 $attachment = [
910 'post_mime_type' => $wp_filetype['type'],
911 'post_title' => sanitize_file_name($unique_image_name),
912 'post_content' => '',
913 'post_status' => 'inherit',
914 ];
915
916 $attach_id = wp_insert_attachment($attachment, $image_file);
917
918 // Conditionally disable intermediate image sizes based on setting
919 $improve_import = get_option('king_addons_improve_import_performance', '1');
920 if ($improve_import === '1') {
921 // Disable ALL extra sizes and big-image scaling
922 add_filter('intermediate_image_sizes', '__return_empty_array', 999);
923 add_filter('big_image_size_threshold', '__return_false', 999);
924
925 /* Save only the original file info — no thumbnails, no EXIF */
926 // Get image dimensions
927 $image_size = getimagesize($image_file);
928 $width = $image_size ? $image_size[0] : 0;
929 $height = $image_size ? $image_size[1] : 0;
930
931 $meta = [
932 'file' => wp_basename($image_file),
933 'filesize' => filesize($image_file),
934 'width' => $width,
935 'height' => $height,
936 'sizes' => [],
937 'image_meta' => [],
938 ];
939 wp_update_attachment_metadata($attach_id, $meta);
940
941 // Clean up filters
942 remove_filter('intermediate_image_sizes', '__return_empty_array', 999);
943 remove_filter('big_image_size_threshold', '__return_false', 999);
944 } else {
945 if ($image_retry_count > 1) {
946 add_filter('intermediate_image_sizes', '__return_empty_array', 999);
947 }
948
949 // Generate default metadata if optimization is off
950 require_once(ABSPATH . 'wp-admin/includes/image.php');
951 $attach_data = wp_generate_attachment_metadata($attach_id, $image_file);
952 wp_update_attachment_metadata($attach_id, $attach_data);
953
954 if ($image_retry_count > 1) {
955 remove_filter('intermediate_image_sizes', '__return_empty_array', 999);
956 }
957 }
958
959 return $attach_id;
960 } catch (Exception $e) {
961 error_log('[KING_ADDONS_ERROR] ' . $e->getMessage());
962 return false;
963 }
964 }
965
966 /**
967 * AJAX handler for getting sections catalog data
968 */
969 public function handle_get_sections_catalog(): void
970 {
971 if (!current_user_can('manage_options')) {
972 wp_send_json_error('Insufficient permissions');
973 return;
974 }
975
976 // Check nonce - handle both admin area and popup
977 if (isset($_POST['nonce'])) {
978 $valid_nonce = wp_verify_nonce($_POST['nonce'], 'kingAddonsNonce') ||
979 wp_verify_nonce($_POST['nonce'], 'king_addons_template_catalog');
980
981 if (!$valid_nonce) {
982 wp_send_json_error('Invalid nonce');
983 return;
984 }
985 }
986
987 if (!class_exists('King_Addons\\SectionsMap')) {
988 require_once KING_ADDONS_PATH . 'includes/SectionsMap.php';
989 }
990
991 $sections_map = SectionsMap::getSectionsMapArray();
992 $sections = $sections_map['sections'] ?? [];
993
994 $is_premium_active = function_exists('king_addons_freemius') && king_addons_freemius()->can_use_premium_code();
995
996 // Get filters from request
997 $search_query = sanitize_text_field($_POST['search'] ?? '');
998 $selected_category = sanitize_text_field($_POST['category'] ?? '');
999 $selected_type = sanitize_text_field($_POST['section_type'] ?? '');
1000 $selected_plan = sanitize_text_field($_POST['plan'] ?? '');
1001 $current_page = max(1, intval($_POST['page'] ?? 1));
1002
1003 // Get categories and section types for filters
1004 $categories = [];
1005 $section_types = [];
1006
1007 foreach ($sections as $section_key => $section) {
1008 if (!in_array($section['category'], $categories)) {
1009 $categories[] = $section['category'];
1010 }
1011 if (!in_array($section['section_type'], $section_types)) {
1012 $section_types[] = $section['section_type'];
1013 }
1014 }
1015
1016 sort($categories);
1017 sort($section_types);
1018
1019 // Filter sections
1020 $filtered_sections = [];
1021
1022 foreach ($sections as $section_key => $section) {
1023 // Add section key for frontend
1024 $section['section_key'] = $section_key;
1025
1026 // Note: Premium sections are shown but restricted for import if no premium license
1027
1028 // Apply search filter
1029 if (!empty($search_query)) {
1030 $found_in_title = stripos($section['title'], $search_query) !== false;
1031 $found_in_tags = false;
1032
1033 foreach ($section['tags'] ?? [] as $tag) {
1034 if (stripos($tag, $search_query) !== false) {
1035 $found_in_tags = true;
1036 break;
1037 }
1038 }
1039
1040 if (!$found_in_title && !$found_in_tags) {
1041 continue;
1042 }
1043 }
1044
1045 // Apply category filter
1046 if (!empty($selected_category) && $section['category'] !== $selected_category) {
1047 continue;
1048 }
1049
1050 // Apply section type filter
1051 if (!empty($selected_type) && $section['section_type'] !== $selected_type) {
1052 continue;
1053 }
1054
1055 // Apply plan filter
1056 if (!empty($selected_plan) && $section['plan'] !== $selected_plan) {
1057 continue;
1058 }
1059
1060 $filtered_sections[] = $section;
1061 }
1062
1063 // Pagination
1064 $items_per_page = 20;
1065 $total_sections = count($filtered_sections);
1066 $total_pages = ceil($total_sections / $items_per_page);
1067 $offset = ($current_page - 1) * $items_per_page;
1068 $paged_sections = array_slice($filtered_sections, $offset, $items_per_page);
1069
1070 wp_send_json_success([
1071 'sections' => $paged_sections,
1072 'categories' => $categories,
1073 'section_types' => $section_types,
1074 'pagination' => [
1075 'current_page' => $current_page,
1076 'total_pages' => $total_pages,
1077 'total_sections' => $total_sections,
1078 'items_per_page' => $items_per_page
1079 ],
1080 'is_premium_active' => $is_premium_active
1081 ]);
1082 }
1083
1084 /**
1085 * AJAX handler for importing section to current page (from main admin catalog)
1086 */
1087 public function handle_import_section_to_page(): void
1088 {
1089 if (!current_user_can('edit_posts')) {
1090 wp_send_json_error('Insufficient permissions');
1091 return;
1092 }
1093
1094 if (!wp_verify_nonce($_POST['nonce'], 'kingAddonsNonce')) {
1095 wp_send_json_error('Invalid nonce');
1096 return;
1097 }
1098
1099 $section_key = sanitize_text_field($_POST['section_key']);
1100 $section_plan = sanitize_text_field($_POST['section_plan']);
1101 $is_premium_active = function_exists('king_addons_freemius') && king_addons_freemius()->can_use_premium_code();
1102
1103 // Determine API URL and install ID (same logic as templates)
1104 if ($is_premium_active && $section_plan === 'premium') {
1105 $api_url = 'https://api.kingaddons.com/get-section.php';
1106
1107 // Use the same method as original templates catalog
1108 if (function_exists('king_addons_freemius')) {
1109 $freemius_site = king_addons_freemius()->get_site();
1110 $install_id = $freemius_site ? $freemius_site->id : 0;
1111 } else {
1112 $install_id = 0;
1113 }
1114
1115 error_log('King Addons Premium Section: Using install_id: ' . $install_id . ' for premium section: ' . $section_key);
1116 } elseif ($section_plan === 'free') {
1117 $api_url = 'https://api.kingaddons.com/get-section-free.php';
1118 $install_id = 0;
1119 error_log('King Addons Free Section: Fetching free section: ' . $section_key);
1120 } else {
1121 error_log('King Addons Section Error: Premium section requires premium license. Section: ' . $section_key . ', Plan: ' . $section_plan . ', Premium Active: ' . ($is_premium_active ? 'Yes' : 'No'));
1122 wp_send_json_error('Premium section requires premium license');
1123 return;
1124 }
1125
1126 // Get section data from API (same as templates)
1127 $response = wp_remote_post($api_url, [
1128 'headers' => ['Content-Type' => 'application/json'],
1129 'body' => json_encode([
1130 'key' => $section_key,
1131 'install' => $install_id,
1132 ]),
1133 'timeout' => 60
1134 ]);
1135
1136 if (is_wp_error($response)) {
1137 wp_send_json_error('Failed to fetch section: ' . $response->get_error_message());
1138 return;
1139 }
1140
1141 $body = wp_remote_retrieve_body($response);
1142 $data = json_decode($body, true);
1143
1144 error_log('King Addons Section API Response: ' . substr($body, 0, 500) . (strlen($body) > 500 ? '...' : ''));
1145
1146 if (!$data) {
1147 error_log('King Addons Section Error: Failed to decode JSON response');
1148 wp_send_json_error('Invalid JSON response from section API');
1149 return;
1150 }
1151
1152 if (!isset($data['success']) || !$data['success']) {
1153 $error_message = isset($data['message']) ? $data['message'] : 'Unknown API error';
1154 error_log('King Addons Section Error: API returned error: ' . $error_message);
1155 wp_send_json_error('Section API error: ' . $error_message);
1156 return;
1157 }
1158
1159 // Return section data for frontend processing (adjust format from your API)
1160 wp_send_json_success([
1161 'section_data' => $data['section'], // Your API returns 'section' not 'landing'
1162 'message' => 'Section data retrieved successfully'
1163 ]);
1164 }
1165 }
1166
1167 Templates::instance();
1168