PluginProbe
Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits / 3.1.2
Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits v3.1.2
3.2.2 3.2.3 3.2.1 3.2.0 3.1.9 3.1.8 3.1.7 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.9 trunk 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.3 1.1.4 1.1.5 All 174 releases
master-addons / inc / admin / templates / library / template-library.php

template-library.php in Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits 3.1.2, at inc/admin/templates/library/template-library.php

1,670 lines 69.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) {
4 exit; // Exit if accessed directly.
5 }
6
7 /**
8 * Template Library Main Class
9 *
10 * Handles the Template Library functionality for Master Addons
11 */
12 class Template_Library {
13
14 private static $instance = null;
15
16 public static function get_instance() {
17 if (self::$instance == null) {
18 self::$instance = new self;
19 }
20 return self::$instance;
21 }
22
23 public function __construct() {
24 add_action('admin_menu', array($this, 'add_admin_menu'), 30);
25 add_action('admin_enqueue_scripts', array($this, 'enqueue_assets'));
26 // add_action('wp_ajax_jltma_get_templates', array($this, 'get_templates')); // Disabled to prevent conflict with manager.php
27 add_action('wp_ajax_jltma_get_categories', array($this, 'get_categories'));
28 add_action('wp_ajax_jltma_get_kit_categories', array($this, 'get_kit_categories'));
29 add_action('wp_ajax_jltma_import_template', array($this, 'import_template'));
30 add_action('wp_ajax_jltma_preview_template', array($this, 'preview_template'));
31 add_action('wp_ajax_jltma_open_template', array($this, 'jltma_template_kit_open_kit'));
32 add_action('wp_ajax_jltma_get_plugins_status', array($this, 'jltma_get_plugins_status'));
33 add_action('wp_ajax_jltma_refresh_templates_cache', array($this, 'refresh_templates_cache'));
34 add_action('wp_ajax_jltma_delete_purchased_kit', array($this, 'delete_purchased_kit'));
35 }
36
37 /**
38 * Add Template Library submenu
39 */
40 public function add_admin_menu() {
41 add_submenu_page(
42 'master-addons-settings',
43 __('Template Library', 'master-addons'),
44 __('Template Library', 'master-addons'),
45 'manage_options',
46 'jltma-template-library',
47 array($this, 'admin_page'),
48 3 // Position after Template Kits
49 );
50 }
51
52 /**
53 * Enqueue Template Library JS & CSS on its admin page
54 */
55 public function enqueue_assets($hook) {
56 // Match the screen for this submenu page (handles white-label menu renaming)
57 if (strpos($hook, 'jltma-template-library') === false) {
58 return;
59 }
60
61 // Enqueue registered assets from Assets_Manager
62 wp_enqueue_style('jltma-template-library');
63 wp_enqueue_style('jltma-page-importer');
64 wp_enqueue_script('jltma-template-library');
65
66 wp_localize_script('jltma-template-library', 'JLTMATemplateLibrary', array(
67 'ajaxurl' => admin_url('admin-ajax.php'),
68 'nonce' => wp_create_nonce('jltma_template_library_nonce'),
69 'restUrl' => get_rest_url(),
70 'restNonce' => wp_create_nonce('wp_rest'),
71 'pluginUrl' => JLTMA_URL,
72 'assetsUrl' => JLTMA_ASSETS,
73 'isProActive' => \MasterAddons\Inc\Classes\Helper::jltma_premium(),
74 'strings' => array(
75 'searchPlaceholder' => __('Search templates...', 'master-addons'),
76 'importTemplate' => __('Import', 'master-addons'),
77 'previewTemplate' => __('Preview', 'master-addons'),
78 'loadingTemplates' => __('Loading templates...', 'master-addons'),
79 'noTemplatesFound' => __('No templates found', 'master-addons'),
80 'importSuccess' => __('Template imported successfully!', 'master-addons'),
81 'importError' => __('Failed to import template', 'master-addons'),
82 ),
83 ));
84 }
85
86 /**
87 * Render the Template Library admin page
88 */
89 public function admin_page() {
90 ?>
91 <!-- React app will be mounted here -->
92 <div id="jltma-template-library-root" class="jltma-template-library">
93 </div>
94 <?php
95 }
96
97 /**
98 * AJAX: Get templates
99 */
100 public function get_templates() {
101 if (!wp_verify_nonce( isset( $_POST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ) : '', 'jltma_template_library_nonce') || !current_user_can('manage_options')) {
102 wp_send_json_error(['message' => 'Permission denied']);
103 return;
104 }
105
106 $category = isset($_POST['category']) ? sanitize_text_field( wp_unslash( $_POST['category'] ) ) : '';
107 $search = isset($_POST['search']) ? sanitize_text_field( wp_unslash( $_POST['search'] ) ) : '';
108 $page = isset($_POST['page']) ? intval($_POST['page']) : 1;
109 $per_page = 12;
110 $tab = isset($_POST['tab']) ? sanitize_text_field( wp_unslash( $_POST['tab'] ) ) : 'master_section';
111
112 // Use existing template manager instead of mock data
113 $templates = $this->fetch_real_templates($tab, $category, $search, $page, $per_page);
114
115 // Add debug logging
116
117 // If no templates found, provide some fallback data for debugging
118 if (empty($templates['templates'])) {
119 $templates = [
120 'templates' => [
121 [
122 'id' => 'test-template-1',
123 'title' => 'Test Business Template',
124 'category' => 'business',
125 'thumbnail' => JLTMA_URL . '/assets/images/icon.png',
126 'preview_url' => '#',
127 'is_pro' => false,
128 'tags' => ['business', 'test']
129 ],
130 [
131 'id' => 'test-template-2',
132 'title' => 'Test Portfolio Template',
133 'category' => 'portfolio',
134 'thumbnail' => JLTMA_URL . '/assets/images/icon.png',
135 'preview_url' => '#',
136 'is_pro' => false,
137 'tags' => ['portfolio', 'test']
138 ]
139 ],
140 'pagination' => [
141 'current_page' => $page,
142 'total_pages' => 1,
143 'total_items' => 2,
144 'has_more' => false
145 ]
146 ];
147 }
148
149 wp_send_json_success($templates);
150 }
151
152 /**
153 * AJAX: Get categories
154 */
155 public function get_categories() {
156 if (!wp_verify_nonce( isset( $_POST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ) : '', 'jltma_template_library_nonce') || !current_user_can('manage_options')) {
157 wp_send_json_error(['message' => 'Permission denied']);
158 return;
159 }
160
161 $tab = isset($_POST['tab']) ? sanitize_text_field( wp_unslash( $_POST['tab'] ) ) : 'master_section';
162 $categories = $this->fetch_real_categories($tab);
163
164 // Add debug logging
165
166 // If no categories found, provide fallback data
167 if (empty($categories) || count($categories) <= 1) {
168 $categories = [
169 ['id' => 'all', 'name' => 'All Types', 'count' => 2],
170 ['id' => 'business', 'name' => 'Business', 'count' => 1],
171 ['id' => 'portfolio', 'name' => 'Portfolio', 'count' => 1],
172 ];
173 }
174
175 wp_send_json_success($categories);
176 }
177
178 /**
179 * AJAX: Get kit categories
180 */
181 public function get_kit_categories() {
182 $wpnonce_kit_cats = isset( $_POST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ) : '';
183 $valid_nonce = wp_verify_nonce($wpnonce_kit_cats, 'jltma_template_library_nonce') ||
184 wp_verify_nonce($wpnonce_kit_cats, 'jltma_template_kits_nonce_action');
185
186 if (!$valid_nonce || !current_user_can('manage_options')) {
187 wp_send_json_error(['message' => 'Permission denied']);
188 return;
189 }
190
191 // Check if force refresh is requested
192 $force_refresh = isset($_POST['force_refresh']) && $_POST['force_refresh'] === 'true';
193
194 // Get cached or fresh categories from API
195 $categories = $this->get_cached_kit_categories($force_refresh);
196
197 if (empty($categories)) {
198 $categories = [];
199 }
200
201 // Ensure we have an array structure
202 if (!is_array($categories)) {
203 $categories = [];
204 }
205
206 // Always add "All Types" as first category if not present
207 $has_all = false;
208 foreach ($categories as $cat) {
209 if (isset($cat['id']) && $cat['id'] === 'all') {
210 $has_all = true;
211 break;
212 }
213 }
214
215 if (!$has_all) {
216 array_unshift($categories, ['id' => 'all', 'name' => 'All Types', 'count' => 0]);
217 }
218
219 // Count purchased kits and add/update purchased category
220 $purchased_count = 0;
221 if (class_exists('MasterAddons\Inc\Classes\Template_Kit_Cache')) {
222 $cache_manager = \MasterAddons\Inc\Classes\Template_Kit_Cache::get_instance();
223 if (method_exists($cache_manager, 'get_purchased_kits')) {
224 $purchased_kits = $cache_manager->get_purchased_kits();
225 $purchased_count = is_array($purchased_kits) ? count($purchased_kits) : 0;
226 }
227 }
228
229 // Add or update purchased category if there are purchased kits
230 if ($purchased_count > 0) {
231 $has_purchased = false;
232 foreach ($categories as &$cat) {
233 if (isset($cat['id']) && $cat['id'] === 'purchased') {
234 $cat['count'] = $purchased_count;
235 $has_purchased = true;
236 break;
237 }
238 }
239
240 // If purchased category doesn't exist, add it
241 if (!$has_purchased) {
242 $categories[] = [
243 'id' => 'purchased',
244 'name' => 'Purchased',
245 'count' => $purchased_count
246 ];
247 }
248 }
249
250 // Calculate total count for "All Types"
251 $total_count = 0;
252 foreach ($categories as &$cat) {
253 if (isset($cat['id']) && $cat['id'] !== 'all' && isset($cat['count'])) {
254 $total_count += (int)$cat['count'];
255 }
256 }
257
258 // Update "All Types" count
259 foreach ($categories as &$cat) {
260 if (isset($cat['id']) && $cat['id'] === 'all') {
261 $cat['count'] = $total_count;
262 break;
263 }
264 }
265
266 // If still empty, provide default categories
267 if (empty($categories)) {
268 $categories = [
269 ['id' => 'all', 'name' => 'All Types', 'count' => 0],
270 ];
271
272 if ($purchased_count > 0) {
273 $categories[] = ['id' => 'purchased', 'name' => 'Purchased', 'count' => $purchased_count];
274 }
275 }
276
277 wp_send_json_success($categories);
278 }
279
280 /**
281 * Get cached kit categories
282 */
283 /**
284 * Write a file through the WP_Filesystem API.
285 *
286 * @param string $file
287 * @param string $contents
288 * @return bool
289 */
290 private function fs_put_contents($file, $contents) {
291 global $wp_filesystem;
292 if (empty($wp_filesystem)) {
293 require_once ABSPATH . 'wp-admin/includes/file.php';
294 WP_Filesystem();
295 }
296 if (empty($wp_filesystem)) {
297 return false;
298 }
299 return $wp_filesystem->put_contents($file, $contents, FS_CHMOD_FILE);
300 }
301
302 private function get_cached_kit_categories($force_refresh = false) {
303 $upload_dir = wp_upload_dir();
304 $categories_file = $upload_dir['basedir'] . '/master_addons/templates_kits/kit-categories.json';
305
306 if (!$force_refresh && file_exists($categories_file)) {
307 $categories_content = file_get_contents($categories_file);
308 $categories = json_decode($categories_content, true);
309
310 if ($categories !== null && is_array($categories)) {
311 return $categories;
312 }
313 }
314
315 // Try to get cache manager instance
316 if (class_exists('MasterAddons\Inc\Classes\Template_Kit_Cache')) {
317 $cache_manager = \MasterAddons\Inc\Classes\Template_Kit_Cache::get_instance();
318
319 // Check if we can use file cache
320 if (method_exists($cache_manager, 'get_cached_kit_categories')) {
321 $cached = $cache_manager->get_cached_kit_categories($force_refresh);
322 if ($cached !== false) {
323 if (!file_exists($categories_file) && !empty($cached)) {
324 $this->fs_put_contents($categories_file, wp_json_encode($cached));
325 }
326 return $cached;
327 }
328 }
329 }
330
331 // Fallback to transient cache if cache manager not available
332 $transient_key = 'jltma_kit_categories_cache';
333 $cache_expiry = 12 * HOUR_IN_SECONDS; // 12 hours
334
335 // Check transient cache first
336 if (!$force_refresh) {
337 $cached_categories = get_transient($transient_key);
338 if ($cached_categories !== false) {
339 return $cached_categories;
340 }
341 }
342
343 // Only fetch from API if local file doesn't exist or force refresh
344 if ($force_refresh || !file_exists($categories_file)) {
345 // Fetch fresh categories from API
346 $fresh_categories = $this->fetch_kit_categories();
347
348 if ($fresh_categories !== false && !empty($fresh_categories)) {
349 // Cache the categories
350 set_transient($transient_key, $fresh_categories, $cache_expiry);
351
352 // Save to local file
353 $this->fs_put_contents($categories_file, wp_json_encode($fresh_categories));
354
355 return $fresh_categories;
356 }
357 }
358
359 // Return cached data even if expired as fallback
360 $cached_categories = get_transient($transient_key);
361 return $cached_categories !== false ? $cached_categories : [];
362 }
363
364 private function fetch_kit_categories()
365 {
366 // Get config from the templates system
367 $config = null;
368 if (function_exists('MasterAddons\Inc\Admin\Templates\master_addons_templates')) {
369 $templates_instance = \MasterAddons\Inc\Admin\Templates\master_addons_templates();
370 if ($templates_instance && isset($templates_instance->config)) {
371 $config = $templates_instance->config->get('api');
372 }
373 }
374 $api_url = $config['base'] . $config['path'] . $config['endpoints']['categories'] . 'template_kits';
375
376 // Add pro_enabled parameter if pro is enabled
377 if (\MasterAddons\Inc\Classes\Helper::jltma_premium()) {
378 $api_url = add_query_arg('pro_enabled', 'true', $api_url);
379 }
380
381 $response = wp_remote_get($api_url, [
382 'timeout' => 15, // Reduced from 60 to 15 seconds
383 'sslverify' => false,
384 'headers' => [
385 'User-Agent' => 'Master Addons Template Kit Cache/' . JLTMA_VER
386 ]
387 ]);
388
389 if (is_wp_error($response)) {
390 return false;
391 }
392
393 $body = wp_remote_retrieve_body($response);
394 $data = json_decode($body, true);
395
396 if (json_last_error() !== JSON_ERROR_NONE || !isset($data['success']) || !$data['success']) {
397 return false;
398 }
399
400 return isset($data['categories']) ? $data['categories'] : [];
401 }
402
403 /**
404 * AJAX: Import template
405 */
406 public function import_template() {
407 // Accept both template library and template kits nonces
408 $wpnonce_import = isset( $_POST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ) : '';
409 $valid_nonce = wp_verify_nonce($wpnonce_import, 'jltma_template_library_nonce') ||
410 wp_verify_nonce($wpnonce_import, 'jltma_template_kits_nonce_action');
411
412 if (!$valid_nonce || !current_user_can('import')) {
413 wp_send_json_error(['message' => 'Permission denied']);
414 return;
415 }
416
417 $template_id = isset($_POST['template_id']) ? sanitize_text_field( wp_unslash( $_POST['template_id'] ) ) : '';
418 $tab = isset($_POST['tab']) ? sanitize_text_field( wp_unslash( $_POST['tab'] ) ) : 'master_section';
419 $page_name = isset($_POST['page_name']) ? sanitize_text_field( wp_unslash( $_POST['page_name'] ) ) : null;
420
421 if (empty($template_id)) {
422 wp_send_json_error(['message' => 'Template ID is required']);
423 return;
424 }
425
426 // Check if this is a demo import
427 if ($tab === 'demo') {
428 $result = $this->import_demo_template($template_id);
429 } else {
430 // Use existing template manager for import - now with page_name
431 $result = $this->import_real_template($template_id, $tab, $page_name);
432 }
433
434 if ($result && !is_wp_error($result)) {
435 wp_send_json_success([
436 'message' => 'Template imported successfully',
437 'page_id' => $result['page_id'] ?? 0,
438 'edit_url' => $result['edit_url'] ?? '',
439 'view_url' => $result['view_url'] ?? ''
440 ]);
441 } else {
442 $error_message = is_wp_error($result) ? $result->get_error_message() : 'Failed to import template';
443 wp_send_json_error(['message' => $error_message]);
444 }
445 }
446
447 /**
448 * AJAX: Preview template
449 */
450 public function preview_template() {
451 // Accept both template library and template kits nonces
452 $wpnonce_preview = isset( $_POST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ) : '';
453 $valid_nonce = wp_verify_nonce($wpnonce_preview, 'jltma_template_library_nonce') ||
454 wp_verify_nonce($wpnonce_preview, 'jltma_template_kits_nonce_action');
455
456 if (!$valid_nonce || !current_user_can('manage_options')) {
457 wp_send_json_error(['message' => 'Permission denied']);
458 return;
459 }
460
461 $template_id = isset($_POST['template_id']) ? sanitize_text_field( wp_unslash( $_POST['template_id'] ) ) : '';
462 $mode = isset($_POST['mode']) ? sanitize_text_field( wp_unslash( $_POST['mode'] ) ) : 'library';
463 if (empty($template_id)) {
464 wp_send_json_error(['message' => 'Template ID is required']);
465 return;
466 }
467
468 // Get preview URL
469 $preview_url = $this->get_template_preview_url($template_id, $mode);
470
471 wp_send_json_success(['preview_url' => $preview_url]);
472 }
473
474 /**
475 * Fetch templates from real API using existing manager
476 */
477 private function fetch_real_templates($tab = 'master_section', $category = '', $search = '', $page = 1, $per_page = 12) {
478 // Get template manager instance
479 if (!class_exists('MasterAddons\\Inc\\Admin\\Templates\\Includes\\Classes\\Manager')) {
480 return [
481 'templates' => [],
482 'pagination' => [
483 'current_page' => $page,
484 'total_pages' => 0,
485 'total_items' => 0,
486 'has_more' => false
487 ]
488 ];
489 }
490
491 $manager = \MasterAddons\Inc\Admin\Templates\Includes\Classes\Manager::get_instance();
492
493 // Get the source for the tab
494 $source = $manager->get_template_source('master-api');
495 if (!$source) {
496 return [
497 'templates' => [],
498 'pagination' => [
499 'current_page' => $page,
500 'total_pages' => 0,
501 'total_items' => 0,
502 'has_more' => false
503 ]
504 ];
505 }
506
507
508 // Get templates directly from source
509 $templates = $source->get_items($tab);
510 if (!is_array($templates)) {
511 $templates = [];
512 } else {
513 }
514
515 // Filter by category if specified
516 if (!empty($category) && $category !== 'all') {
517 $templates = array_filter($templates, function($template) use ($category) {
518 return isset($template['categories']) && in_array($category, $template['categories']);
519 });
520 }
521
522 // Filter by search term if specified
523 if (!empty($search)) {
524 $templates = array_filter($templates, function($template) use ($search) {
525 return stripos($template['title'] ?? '', $search) !== false ||
526 stripos($template['content'] ?? '', $search) !== false;
527 });
528 }
529
530 // Apply pagination
531 $total_templates = count($templates);
532 $offset = ($page - 1) * $per_page;
533 $paginated_templates = array_slice($templates, $offset, $per_page);
534
535 return [
536 'templates' => array_values($paginated_templates),
537 'pagination' => [
538 'current_page' => $page,
539 'total_pages' => ceil($total_templates / $per_page),
540 'total_items' => $total_templates,
541 'has_more' => ($offset + $per_page) < $total_templates
542 ]
543 ];
544 }
545
546 /**
547 * Fetch categories from real API using existing manager
548 */
549 private function fetch_real_categories($tab = 'master_section') {
550 // Get template manager instance
551 if (!class_exists('MasterAddons\\Inc\\Admin\\Templates\\Includes\\Classes\\Manager')) {
552 return [['id' => 'all', 'name' => 'All Types', 'count' => 0]];
553 }
554
555 $manager = \MasterAddons\Inc\Admin\Templates\Includes\Classes\Manager::get_instance();
556
557 // Get the source for the tab
558 $source = $manager->get_template_source('master-api');
559 if (!$source) {
560 return [['id' => 'all', 'name' => 'All Types', 'count' => 0]];
561 }
562
563 // Get templates to calculate counts
564 $templates = $source->get_items($tab);
565 if (!is_array($templates)) {
566 $templates = [];
567 }
568
569 // Get categories directly from source
570 $source_categories = $source->get_categories($tab);
571 if (!is_array($source_categories)) {
572 $source_categories = [];
573 }
574
575 // Calculate category counts
576 $category_counts = [];
577 foreach ($templates as $template) {
578 if (isset($template['categories']) && is_array($template['categories'])) {
579 foreach ($template['categories'] as $cat) {
580 if (!isset($category_counts[$cat])) {
581 $category_counts[$cat] = 0;
582 }
583 $category_counts[$cat]++;
584 }
585 }
586 }
587
588 $categories = [];
589
590 // Add "All Types" as first category with total count
591 $categories[] = ['id' => 'all', 'name' => 'All Types', 'count' => count($templates)];
592
593 // Transform categories to expected format with counts
594 foreach ($source_categories as $category) {
595 $slug = $category['slug'] ?? '';
596 $categories[] = [
597 'id' => $slug,
598 'name' => $category['title'] ?? 'Unknown',
599 'count' => $category_counts[$slug] ?? 0
600 ];
601 }
602
603 return $categories;
604 }
605
606 /**
607 * AJAX: Refresh templates cache
608 */
609 public function refresh_templates_cache() {
610 $wpnonce_refresh = isset( $_POST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ) : '';
611 $nonce_valid = wp_verify_nonce($wpnonce_refresh, 'jltma_template_library_nonce') ||
612 wp_verify_nonce($wpnonce_refresh, 'master_addons_nonce');
613
614 // Enhanced security checks
615 if (!$nonce_valid) {
616 wp_send_json_error(array('message' => 'Permission denied - nonce failed'));
617 }
618
619 // Check user permissions
620 if (!current_user_can('manage_options')) {
621 wp_send_json_error(array('message' => 'Permission denied - insufficient capabilities'));
622 }
623
624
625 // Get cache manager and refresh
626 if (class_exists('MasterAddons\Inc\Classes\Template_Library_Cache')) {
627 $cache_manager = \MasterAddons\Inc\Classes\Template_Library_Cache::get_instance();
628 $refreshed_data = $cache_manager->refresh_cache();
629
630 wp_send_json_success([
631 'message' => 'Templates cache refreshed successfully',
632 'data' => $refreshed_data
633 ]);
634 } else {
635 wp_send_json_error(['message' => 'Cache manager not available']);
636 }
637 }
638
639 /**
640 * Import demo template from local files
641 */
642 private function import_demo_template($template_id) {
643 // Get template file from local demo directory
644 $templates_dir = get_stylesheet_directory() . '/jltma_demo/templates';
645 $template_files = glob($templates_dir . '/*.json');
646
647 $template_data = null;
648 foreach ($template_files as $template_file) {
649 $json_content = file_get_contents($template_file);
650 $decoded = json_decode($json_content, true);
651
652 if ($decoded && isset($decoded['template_id']) && $decoded['template_id'] == $template_id) {
653 $template_data = $decoded;
654 break;
655 }
656 }
657
658 if (!$template_data) {
659 return new WP_Error('template_not_found', 'Demo template not found');
660 }
661
662 // Ensure Elementor is loaded
663 if (!did_action('elementor/loaded')) {
664 return new WP_Error('elementor_not_loaded', 'Elementor is not loaded');
665 }
666
667 // Check if this is a re-import
668 $is_reimport = isset($_POST['is_reimport']) && $_POST['is_reimport'] === '1'; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified in calling method
669
670 // Create a new page with the template content
671 $page_title = isset($template_data['title']) ? $template_data['title'] : 'Demo Page ' . $template_id;
672 $template_type = isset($template_data['type']) ? $template_data['type'] : 'page';
673
674 // If re-importing, add a suffix to the title and slug
675 if ($is_reimport) {
676 // Find existing pages with this title
677 $existing_pages = get_posts([
678 'post_type' => 'page',
679 'post_status' => 'any',
680 's' => $page_title,
681 'numberposts' => -1
682 ]);
683
684 // Calculate suffix number
685 $suffix = 2;
686 $base_title = $page_title;
687
688 // Check for existing numbered versions
689 foreach ($existing_pages as $page) {
690 if (preg_match('/(.+)\s+(\d+)$/', $page->post_title, $matches)) {
691 if ($matches[1] === $base_title) {
692 $num = intval($matches[2]);
693 if ($num >= $suffix) {
694 $suffix = $num + 1;
695 }
696 }
697 }
698 }
699
700 $page_title = $base_title . ' ' . $suffix;
701 }
702
703 // Prepare the page data
704 $page_data = [
705 'post_title' => $page_title,
706 'post_type' => 'page',
707 'post_status' => 'publish',
708 'post_content' => '', // Elementor will handle the content
709 'meta_input' => [
710 '_elementor_edit_mode' => 'builder',
711 '_elementor_template_type' => $template_type,
712 '_wp_page_template' => 'elementor_header_footer',
713 '_jltma_demo_import_item' => true,
714 '_jltma_import_date' => current_time('mysql'),
715 '_jltma_original_template_id' => $template_id
716 ]
717 ];
718
719 // Create the page
720 $page_id = wp_insert_post($page_data);
721
722 if (is_wp_error($page_id)) {
723 return $page_id;
724 }
725
726 // Import Elementor data if available
727 if (isset($template_data['content']) && !empty($template_data['content'])) {
728 // Process the content for image imports if needed
729 $processed_content = $this->process_demo_content($template_data['content']);
730
731 // Save Elementor data
732 update_post_meta($page_id, '_elementor_data', wp_json_encode($processed_content));
733 update_post_meta($page_id, '_elementor_version', ELEMENTOR_VERSION);
734
735 // Clear Elementor cache
736 if (class_exists('\Elementor\Plugin')) {
737 \Elementor\Plugin::$instance->files_manager->clear_cache();
738 }
739 }
740
741 // Return success with page details
742 return [
743 'page_id' => $page_id,
744 'edit_url' => admin_url('post.php?post=' . $page_id . '&action=elementor'),
745 'view_url' => get_permalink($page_id),
746 'message' => 'Demo template imported successfully'
747 ];
748 }
749
750 /**
751 * Process demo content for imports (handle images, etc.)
752 */
753 private function process_demo_content($content) {
754 // If content is already an array, use it directly
755 if (is_array($content)) {
756 return $content;
757 }
758
759 // Try to decode if it's JSON
760 if (is_string($content)) {
761 $decoded = json_decode($content, true);
762 if (json_last_error() === JSON_ERROR_NONE) {
763 return $decoded;
764 }
765 }
766
767 // Return as-is if we can't process it
768 return $content;
769 }
770
771 /**
772 * Import real template using existing manager
773 */
774 private function import_real_template($template_id, $tab = 'master_section', $page_name = null) {
775 // Get template manager instance
776 if (!class_exists('MasterAddons\\Inc\\Admin\\Templates\\Includes\\Classes\\Manager')) {
777 return new WP_Error('manager_not_found', 'Template manager not available');
778 }
779
780 $manager = \MasterAddons\Inc\Admin\Templates\Includes\Classes\Manager::get_instance();
781
782 // Instead of calling jltma_insert_inner_template which calls wp_die(),
783 // we'll replicate its logic without the wp_send_json_* functions
784
785 // Get the template source
786 $source = $manager->get_template_source('master-api');
787 if (!$source) {
788 return new WP_Error('source_not_found', 'Template source not available');
789 }
790
791 // Get the template data
792 $template_data = $source->get_item($template_id);
793 if (!$template_data || empty($template_data['content'])) {
794 return new WP_Error('template_not_found', 'Template content not found');
795 }
796
797 // Prepare the post title
798 $post_title = $page_name ? sanitize_text_field($page_name) : 'Imported Template: ' . $template_id;
799 if (strlen($post_title) > 255) {
800 $post_title = substr($post_title, 0, 255);
801 }
802
803 // Ensure Elementor data is a JSON string (Elementor expects JSON, not PHP serialized)
804 $elementor_data = $template_data['content'];
805 if (is_array($elementor_data)) {
806 $elementor_data = wp_slash(wp_json_encode($elementor_data));
807 } elseif (is_string($elementor_data)) {
808 $decoded_data = json_decode($elementor_data, true);
809 if (json_last_error() !== JSON_ERROR_NONE) {
810 return new WP_Error('invalid_data', 'Invalid Elementor data format');
811 }
812 $elementor_data = wp_slash($elementor_data);
813 }
814
815 // Create the template in Elementor library
816 $library_post_id = wp_insert_post([
817 'post_type' => 'elementor_library',
818 'post_title' => $post_title,
819 'post_status' => 'publish',
820 'post_author' => get_current_user_id(),
821 'meta_input' => [
822 '_elementor_data' => $elementor_data,
823 '_elementor_edit_mode' => 'builder',
824 '_elementor_template_type' => 'section',
825 '_jltma_template_source' => 'master-api',
826 '_jltma_template_id' => $template_id,
827 ],
828 ]);
829
830 if (!$library_post_id || is_wp_error($library_post_id)) {
831 return new WP_Error('creation_failed', 'Failed to create template in library');
832 }
833
834 // Now create a page that uses this template (if page_name is provided)
835 if ($page_name) {
836 $page_id = wp_insert_post([
837 'post_title' => $page_name,
838 'post_type' => 'page',
839 'post_status' => 'publish',
840 'post_author' => get_current_user_id(),
841 'meta_input' => [
842 '_elementor_edit_mode' => 'builder',
843 '_elementor_template_type' => 'wp-page',
844 '_wp_page_template' => 'elementor_header_footer',
845 '_elementor_data' => $elementor_data,
846 '_jltma_imported_template' => $template_id,
847 '_jltma_library_post_id' => $library_post_id,
848 '_jltma_import_date' => current_time('mysql')
849 ]
850 ]);
851
852 if ($page_id && !is_wp_error($page_id)) {
853 // Generate Elementor CSS for the new page
854 if (class_exists('\Elementor\Core\Files\CSS\Post')) {
855 $css_file = new \Elementor\Core\Files\CSS\Post($page_id);
856 $css_file->update();
857 }
858
859 return [
860 'success' => true,
861 'page_id' => $page_id,
862 'library_post_id' => $library_post_id,
863 'edit_url' => admin_url('post.php?post=' . $page_id . '&action=elementor'),
864 'view_url' => get_permalink($page_id),
865 'message' => 'Page created successfully with template'
866 ];
867 }
868 }
869
870 // Return library post info if no page was created
871 return [
872 'success' => true,
873 'library_post_id' => $library_post_id,
874 'edit_url' => admin_url('post.php?post=' . $library_post_id . '&action=elementor'),
875 'message' => 'Template imported to library successfully'
876 ];
877 }
878
879 /**
880 * Get template preview URL
881 */
882 private function get_template_preview_url($template_id, $mode) {
883 // if($mode === 'kits'){
884 // return 'http://testing.local/template-kits/?preview-kit=' . $template_id;
885 // }
886 // return 'http://testing.local/template-library/?preview-template=' . $template_id;
887 if($mode === 'kits'){
888 return 'https://master-addons.com/template-kits/?kit=' . $template_id;
889 }
890 return 'https://master-addons.com/template-library/?template=' . $template_id;
891 // if($mode === 'kits'){
892 // return 'https://master-addons.com/template-kits/' . $template_id;
893 // }
894 // return 'https://master-addons.com/template-library/' . $template_id;
895 }
896
897 public function jltma_template_kit_open_kit(){
898 // Verify nonce and permissions
899 $wpnonce_open_kit = isset( $_POST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ) : '';
900 $valid_nonce = wp_verify_nonce($wpnonce_open_kit, 'jltma_template_library_nonce') ||
901 wp_verify_nonce($wpnonce_open_kit, 'jltma_template_kits_nonce_action');
902
903 if (!$valid_nonce || !current_user_can('manage_options')) {
904 wp_send_json_error(['message' => 'Permission denied']);
905 return;
906 }
907
908 $kit_id = isset( $_POST['template_id'] ) ? sanitize_text_field( wp_unslash( $_POST['template_id'] ) ) : '';
909 $kit_category = isset( $_POST['template_category'] ) ? sanitize_text_field( wp_unslash( $_POST['template_category'] ) ) : '';
910 $force_refresh = isset($_POST['force_refresh']) && $_POST['force_refresh'] === 'true';
911
912
913 $templates = false;
914 $required_plugins = [];
915
916 // First check if this is a purchased kit
917 if ($kit_category === 'purchased' || $this->is_purchased_kit($kit_id)) {
918 // Handle purchased kit
919 if (class_exists('MasterAddons\Inc\Classes\Template_Kit_Cache')) {
920 $cache_manager = \MasterAddons\Inc\Classes\Template_Kit_Cache::get_instance();
921
922 // Get purchased kit manifest
923 if (method_exists($cache_manager, 'get_kit_manifest')) {
924 $manifest = $cache_manager->get_kit_manifest($kit_id);
925
926 if ($manifest) {
927 // Extract templates from manifest
928 $templates = $manifest['templates'] ?? $manifest['pages'] ?? [];
929
930 // Format templates for consistent response
931 if (!empty($templates)) {
932 // Build the base URL for the kit folder
933 $upload_dir = wp_upload_dir();
934 $kit_base_url = $upload_dir['baseurl'] . '/master_addons/purchased_kits/kits/kit_' . $kit_id . '/';
935
936 $formatted_templates = [];
937 foreach ($templates as $key => $template) {
938 // Get screenshot path from manifest
939 $screenshot_path = $template['screenshot'] ?? $template['thumbnail'] ?? '';
940
941 // Build full URL if we have a screenshot path
942 $thumbnail = '';
943 if (!empty($screenshot_path)) {
944 // Check if it's already a full URL
945 if (filter_var($screenshot_path, FILTER_VALIDATE_URL)) {
946 $thumbnail = $screenshot_path;
947 } else {
948 // Remove leading slash if present
949 $screenshot_path = ltrim($screenshot_path, '/');
950 // Build the full URL
951 $thumbnail = $kit_base_url . $screenshot_path;
952 }
953 }
954
955 // Ensure each template has required fields
956 $formatted_template = [
957 'template_id' => $template['template_id'] ?? $template['id'] ?? $key,
958 'title' => $template['title'] ?? $template['name'] ?? '',
959 'type' => $template['type'] ?? 'page',
960 'thumbnail' => $thumbnail,
961 'is_pro' => false,
962 'content' => $template['content'] ?? null,
963 'menu_order' => $template['menu_order'] ?? 0
964 ];
965 $formatted_templates[] = $formatted_template;
966 }
967 $templates = $formatted_templates;
968 }
969
970 // Get required plugins from manifest
971 $required_plugins = $manifest['required_plugins'] ?? [];
972
973 // Process required plugins to check their installation status
974 $processed_plugins = [];
975 foreach ($required_plugins as $key => $plugin) {
976 // Handle both array and string formats
977 if (is_string($plugin)) {
978 $plugin = ['name' => $plugin, 'slug' => sanitize_title($plugin)];
979 }
980
981 // Map 'file' to 'plugin_file' if needed
982 if (isset($plugin['file']) && !isset($plugin['plugin_file'])) {
983 $plugin['plugin_file'] = $plugin['file'];
984 }
985
986 // Determine plugin slug
987 $plugin_slug = $plugin['slug'] ?? sanitize_title($plugin['name'] ?? '');
988
989 // Check plugin installation and activation status
990 $is_installed = false;
991 $is_active = false;
992
993 // Check if it's a theme based on type from manifest
994 if (isset($plugin['type']) && $plugin['type'] === 'theme') {
995 $theme = wp_get_theme($plugin_slug);
996 $is_installed = $theme->exists();
997 $is_active = (get_option('stylesheet') === $plugin_slug);
998 $plugin_file = 'theme';
999 } else {
1000 // It's a plugin - determine the plugin file path
1001 $plugin_file = '';
1002
1003 if (isset($plugin['plugin_file'])) {
1004 $plugin_file = $plugin['plugin_file'];
1005 } elseif (isset($plugin['file'])) {
1006 $plugin_file = $plugin['file'];
1007 } else {
1008 // Default pattern: slug/slug.php
1009 $plugin_file = $plugin_slug . '/' . $plugin_slug . '.php';
1010 }
1011
1012 // Check if plugin is installed
1013 if (!function_exists('get_plugins')) {
1014 require_once ABSPATH . 'wp-admin/includes/plugin.php';
1015 }
1016
1017 $all_plugins = get_plugins();
1018 $is_installed = isset($all_plugins[$plugin_file]);
1019
1020 // If not found with default path, search through all plugins
1021 if (!$is_installed) {
1022 foreach ($all_plugins as $path => $plugin_data) {
1023 if (strpos($path, $plugin_slug . '/') === 0 ||
1024 strpos($path, $plugin_slug . '.php') !== false) {
1025 $is_installed = true;
1026 $plugin_file = $path;
1027 break;
1028 }
1029 }
1030 }
1031
1032 // Check if plugin is active
1033 if ($is_installed) {
1034 $is_active = is_plugin_active($plugin_file);
1035 }
1036 }
1037
1038 // Build the processed plugin data
1039 $processed_plugin = [
1040 'slug' => $plugin_slug,
1041 'name' => $plugin['name'] ?? ucfirst(str_replace('-', ' ', $plugin_slug)),
1042 'required' => $plugin['required'] ?? true,
1043 'version' => $plugin['version'] ?? '',
1044 'is_installed' => $is_installed,
1045 'is_active' => $is_active,
1046 'plugin_file' => $plugin_file,
1047 'type' => (isset($plugin['type']) && $plugin['type'] === 'theme') ? 'theme' : 'plugin'
1048 ];
1049
1050 $processed_plugins[] = $processed_plugin;
1051 }
1052
1053 $required_plugins = $processed_plugins;
1054 }
1055 }
1056
1057 // If no manifest, try to get from purchased kit data
1058 if (!$templates && method_exists($cache_manager, 'get_purchased_kit')) {
1059 $purchased_kit = $cache_manager->get_purchased_kit($kit_id);
1060 if ($purchased_kit) {
1061 $templates = $purchased_kit['templates'] ?? [];
1062
1063 // Process templates to build proper thumbnail URLs
1064 if (!empty($templates)) {
1065 // Build the base URL for the kit folder
1066 $upload_dir = wp_upload_dir();
1067 $kit_base_url = $upload_dir['baseurl'] . '/master_addons/purchased_kits/kits/kit_' . $kit_id . '/';
1068
1069 $formatted_templates = [];
1070 foreach ($templates as $key => $template) {
1071 // Get screenshot path from data
1072 $screenshot_path = $template['screenshot'] ?? $template['thumbnail'] ?? '';
1073
1074 // Build full URL if we have a screenshot path
1075 $thumbnail = '';
1076 if (!empty($screenshot_path)) {
1077 // Remove leading slash if present
1078 $screenshot_path = ltrim($screenshot_path, '/');
1079 // Build the full URL
1080 $thumbnail = $kit_base_url . $screenshot_path;
1081 }
1082
1083 // Ensure template has all required fields
1084 $formatted_template = [
1085 'template_id' => $template['template_id'] ?? $template['id'] ?? $key,
1086 'title' => $template['title'] ?? $template['name'] ?? '',
1087 'type' => $template['type'] ?? 'page',
1088 'thumbnail' => $thumbnail,
1089 'is_pro' => $template['is_pro'] ?? false,
1090 'content' => $template['content'] ?? null,
1091 'menu_order' => $template['menu_order'] ?? 0
1092 ];
1093
1094 // Preserve other fields if they exist
1095 foreach ($template as $field => $value) {
1096 if (!isset($formatted_template[$field])) {
1097 $formatted_template[$field] = $value;
1098 }
1099 }
1100
1101 $formatted_templates[] = $formatted_template;
1102 }
1103 $templates = $formatted_templates;
1104 }
1105
1106 $raw_plugins = $purchased_kit['required_plugins'] ?? [];
1107
1108 // Process the required plugins
1109 $processed_plugins = [];
1110 foreach ($raw_plugins as $key => $plugin) {
1111 // Handle both array and string formats
1112 if (is_string($plugin)) {
1113 $plugin = ['name' => $plugin, 'slug' => sanitize_title($plugin)];
1114 }
1115
1116 // Map 'file' to 'plugin_file' if needed
1117 if (isset($plugin['file']) && !isset($plugin['plugin_file'])) {
1118 $plugin['plugin_file'] = $plugin['file'];
1119 }
1120
1121 // Determine plugin slug
1122 $plugin_slug = $plugin['slug'] ?? sanitize_title($plugin['name'] ?? '');
1123
1124 // Check plugin installation and activation status
1125 $is_installed = false;
1126 $is_active = false;
1127
1128 // Check if it's a theme based on type from manifest
1129 if (isset($plugin['type']) && $plugin['type'] === 'theme') {
1130 $theme = wp_get_theme($plugin_slug);
1131 $is_installed = $theme->exists();
1132 $is_active = (get_option('stylesheet') === $plugin_slug);
1133 $plugin_file = 'theme';
1134 } else {
1135 // Determine plugin file path
1136 $plugin_file = '';
1137
1138 if (isset($plugin['plugin_file'])) {
1139 $plugin_file = $plugin['plugin_file'];
1140 } elseif (isset($plugin['file'])) {
1141 $plugin_file = $plugin['file'];
1142 } else {
1143 // Default pattern: slug/slug.php
1144 $plugin_file = $plugin_slug . '/' . $plugin_slug . '.php';
1145 }
1146
1147 // Check if plugin is installed
1148 if (!function_exists('get_plugins')) {
1149 require_once ABSPATH . 'wp-admin/includes/plugin.php';
1150 }
1151
1152 $all_plugins = get_plugins();
1153 $is_installed = isset($all_plugins[$plugin_file]);
1154
1155 // If not found, search through all plugins
1156 if (!$is_installed) {
1157 foreach ($all_plugins as $path => $plugin_data) {
1158 if (strpos($path, $plugin_slug . '/') === 0 ||
1159 strpos($path, $plugin_slug . '.php') !== false) {
1160 $is_installed = true;
1161 $plugin_file = $path;
1162 break;
1163 }
1164 }
1165 }
1166
1167 // Check if plugin is active
1168 if ($is_installed) {
1169 $is_active = is_plugin_active($plugin_file);
1170 }
1171 }
1172
1173 $processed_plugin = [
1174 'slug' => $plugin_slug,
1175 'name' => $plugin['name'] ?? ucfirst(str_replace('-', ' ', $plugin_slug)),
1176 'required' => $plugin['required'] ?? true,
1177 'version' => $plugin['version'] ?? '',
1178 'is_installed' => $is_installed,
1179 'is_active' => $is_active,
1180 'plugin_file' => $plugin_file,
1181 'type' => (isset($plugin['type']) && $plugin['type'] === 'theme') ? 'theme' : 'plugin'
1182 ];
1183
1184 $processed_plugins[] = $processed_plugin;
1185 }
1186
1187 $required_plugins = $processed_plugins;
1188 }
1189 }
1190 }
1191 }
1192
1193 // If not a purchased kit or failed to get purchased templates, try API/cache
1194 if ($templates === false || empty($templates)) {
1195 $templates = $this->get_cached_kit_templates($kit_id, $kit_category, $force_refresh);
1196
1197 // Get required plugins from manifest for API kits
1198 if (empty($required_plugins)) {
1199 $required_plugins = $this->get_kit_required_plugins($kit_id);
1200 }
1201 }
1202
1203 if ($templates === false || empty($templates)) {
1204 wp_send_json_error(['message' => 'Failed to load template kit']);
1205 return;
1206 }
1207
1208 // Validate thumbnail URLs - clear any that point to non-existent local files
1209 $upload_dir = wp_upload_dir();
1210 foreach ($templates as &$tpl) {
1211 if (!empty($tpl['thumbnail']) && strpos($tpl['thumbnail'], $upload_dir['baseurl']) === 0) {
1212 $local_path = str_replace($upload_dir['baseurl'], $upload_dir['basedir'], $tpl['thumbnail']);
1213 if (!file_exists($local_path)) {
1214 $tpl['thumbnail'] = '';
1215 }
1216 }
1217 }
1218 unset($tpl);
1219
1220 // Prepare response with templates and required plugins
1221 $response_data = [
1222 'templates' => $templates,
1223 'required_plugins' => $required_plugins
1224 ];
1225
1226 wp_send_json_success(['data' => $response_data]);
1227 }
1228
1229 /**
1230 * Check if a kit is purchased
1231 */
1232 private function is_purchased_kit($kit_id) {
1233 if (class_exists('MasterAddons\Inc\Classes\Template_Kit_Cache')) {
1234 $cache_manager = \MasterAddons\Inc\Classes\Template_Kit_Cache::get_instance();
1235 if (method_exists($cache_manager, 'is_kit_purchased')) {
1236 return $cache_manager->is_kit_purchased($kit_id);
1237 }
1238 }
1239 return false;
1240 }
1241
1242 /**
1243 * Get required plugins from kit manifest
1244 */
1245 private function get_kit_required_plugins($kit_id) {
1246 if (class_exists('MasterAddons\Inc\Classes\Template_Kit_Cache')) {
1247 $cache_manager = \MasterAddons\Inc\Classes\Template_Kit_Cache::get_instance();
1248
1249 // Get manifest from cache
1250 $manifest = $cache_manager->get_kit_manifest($kit_id);
1251
1252 if ($manifest && isset($manifest['required_plugins'])) {
1253 // Format the required plugins data
1254 $required_plugins = [];
1255 foreach ($manifest['required_plugins'] as $plugin_slug => $plugin_data) {
1256 // Check if plugin is installed and active
1257 $is_installed = false;
1258 $is_active = false;
1259 $plugin_file = '';
1260
1261 // Check if it's a theme based on type in manifest
1262 if (is_array($plugin_data) && isset($plugin_data['type']) && $plugin_data['type'] === 'theme') {
1263 $theme = wp_get_theme($plugin_slug);
1264 $is_installed = $theme->exists();
1265 $is_active = (get_option('stylesheet') === $plugin_slug);
1266 $plugin_file = 'theme';
1267 } else {
1268 // Check for plugin
1269 if (is_array($plugin_data) && isset($plugin_data['plugin_file'])) {
1270 $plugin_file = $plugin_data['plugin_file'];
1271 } elseif (is_array($plugin_data) && isset($plugin_data['file'])) {
1272 $plugin_file = $plugin_data['file'];
1273 } else {
1274 // Default pattern: slug/slug.php
1275 $plugin_file = $plugin_slug . '/' . $plugin_slug . '.php';
1276 }
1277
1278 // Check if plugin is installed
1279 if (!function_exists('get_plugins')) {
1280 require_once ABSPATH . 'wp-admin/includes/plugin.php';
1281 }
1282
1283 $installed_plugins = get_plugins();
1284 $is_installed = isset($installed_plugins[$plugin_file]);
1285
1286 // If not found, search through all plugins
1287 if (!$is_installed) {
1288 foreach ($installed_plugins as $path => $plugin_info) {
1289 if (strpos($path, $plugin_slug . '/') === 0 ||
1290 strpos($path, $plugin_slug . '.php') !== false) {
1291 $is_installed = true;
1292 $plugin_file = $path;
1293 break;
1294 }
1295 }
1296 }
1297
1298 // Check if plugin is active
1299 if ($is_installed) {
1300 $is_active = is_plugin_active($plugin_file);
1301 }
1302 }
1303
1304 // Build plugin info array
1305 $plugin_info = [
1306 'slug' => $plugin_slug,
1307 'name' => is_array($plugin_data) ? ($plugin_data['name'] ?? ucfirst(str_replace('-', ' ', $plugin_slug))) : $plugin_data,
1308 'required' => is_array($plugin_data) ? ($plugin_data['required'] ?? true) : true,
1309 'version' => is_array($plugin_data) ? ($plugin_data['version'] ?? '') : '',
1310 'is_installed' => $is_installed,
1311 'is_active' => $is_active,
1312 'plugin_file' => $plugin_file,
1313 'type' => (is_array($plugin_data) && isset($plugin_data['type']) && $plugin_data['type'] === 'theme') ? 'theme' : 'plugin'
1314 ];
1315
1316 // Add install/activate URLs if needed
1317 if (!$is_installed && $plugin_info['type'] === 'plugin') {
1318 $plugin_info['install_url'] = wp_nonce_url(
1319 admin_url('update.php?action=install-plugin&plugin=' . $plugin_slug),
1320 'install-plugin_' . $plugin_slug
1321 );
1322 } elseif ($is_installed && !$is_active && $plugin_info['type'] === 'plugin') {
1323 $plugin_info['activate_url'] = wp_nonce_url(
1324 admin_url('plugins.php?action=activate&plugin=' . $plugin_file),
1325 'activate-plugin_' . $plugin_file
1326 );
1327 }
1328
1329 $required_plugins[] = $plugin_info;
1330 }
1331
1332 return $required_plugins;
1333 }
1334 }
1335
1336 // Return empty array if no manifest found
1337 return [];
1338 }
1339
1340 public function jltma_get_plugins_status(){
1341 $wpnonce_plugins_status = isset( $_POST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ) : '';
1342 $valid_nonce = wp_verify_nonce($wpnonce_plugins_status, 'jltma_template_library_nonce') ||
1343 wp_verify_nonce($wpnonce_plugins_status, 'jltma_template_kits_nonce_action');
1344
1345 if (!$valid_nonce) {
1346 wp_send_json_error(['message' => 'Invalid nonce']);
1347 return;
1348 }
1349
1350 if (!current_user_can('manage_options')) {
1351 wp_send_json_error(['message' => 'Insufficient permissions']);
1352 return;
1353 }
1354
1355 $plugins_with_status = array();
1356
1357 if( isset($_POST['required_plugins']) && !empty( $_POST['required_plugins'])){
1358 $plugins = json_decode( sanitize_textarea_field( wp_unslash( $_POST['required_plugins'] ) ), true);
1359
1360 // Include plugin.php if needed
1361 if (!function_exists('get_plugins')) {
1362 require_once ABSPATH . 'wp-admin/includes/plugin.php';
1363 }
1364
1365 $installed_plugins = get_plugins();
1366
1367 foreach($plugins as $plugin){
1368 $plugin_file = isset($plugin['file']) ? $plugin['file'] : '';
1369 $plugin_path = explode('/', $plugin_file)[1];
1370 $plugin_slug = explode('.', $plugin_path)[0];
1371
1372 // If no plugin_file provided, try common patterns
1373 if (empty($plugin_file) && !empty($plugin_slug)) {
1374 $plugin_file = $plugin_slug . '/' . $plugin_slug . '.php';
1375 }
1376
1377 $is_installed = false;
1378 $is_active = false;
1379
1380 // Check if plugin is installed
1381 if (!empty($plugin_file) && isset($installed_plugins[$plugin_file])) {
1382 $is_installed = true;
1383 }
1384
1385 // Check if plugin is active
1386 if ($is_installed) {
1387 $is_active = is_plugin_active($plugin_file);
1388 }
1389
1390 // Build the plugin data with status
1391 $plugin_with_status = array(
1392 'slug' => $plugin_slug,
1393 'name' => isset($plugin['name']) ? $plugin['name'] : ucfirst(str_replace('-', ' ', $plugin_slug)),
1394 'plugin_file' => $plugin_file,
1395 'is_installed' => $is_installed,
1396 'is_active' => $is_active
1397 );
1398
1399 // Add any other fields from original plugin data
1400 if (isset($plugin['version'])) {
1401 $plugin_with_status['version'] = $plugin['version'];
1402 }
1403 if (isset($plugin['required'])) {
1404 $plugin_with_status['required'] = $plugin['required'];
1405 }
1406
1407 $plugins_with_status[] = $plugin_with_status;
1408 }
1409
1410 wp_send_json_success(['plugins' => $plugins_with_status]);
1411 } else {
1412 wp_send_json_error(['message' => 'No plugins provided']);
1413 }
1414 }
1415
1416 /**
1417 * Get cached kit templates with fallback to API
1418 */
1419 private function get_cached_kit_templates($kit_id, $kit_category, $force_refresh = false) {
1420 $upload_dir = wp_upload_dir();
1421 $kit_manifest_path = $upload_dir['basedir'] . '/master_addons/templates_kits/kits/' . $kit_id . '/manifest.json';
1422
1423 // If local manifest exists and we're not forcing refresh, use it directly
1424 if (!$force_refresh && file_exists($kit_manifest_path)) {
1425 $manifest_content = file_get_contents($kit_manifest_path);
1426 $manifest = json_decode($manifest_content, true);
1427
1428 if ($manifest && isset($manifest['templates'])) {
1429 // Process templates from local manifest
1430 $templates = [];
1431 $kit_base_url = $upload_dir['baseurl'] . '/master_addons/templates_kits/kits/' . $kit_id . '/';
1432
1433 foreach ($manifest['templates'] as $template) {
1434 // Build proper URLs for screenshots
1435 $screenshot = $template['screenshot'] ?? '';
1436 if (!empty($screenshot) && strpos($screenshot, 'http') !== 0) {
1437 // It's a relative path, verify file exists before building URL
1438 $kit_base_dir = $upload_dir['basedir'] . '/master_addons/templates_kits/kits/' . $kit_id . '/';
1439 $local_file = $kit_base_dir . ltrim($screenshot, '/');
1440 if (file_exists($local_file)) {
1441 $screenshot = $kit_base_url . ltrim($screenshot, '/');
1442 } else {
1443 $screenshot = '';
1444 }
1445 }
1446
1447 $templates[] = [
1448 'template_id' => $template['template_id'] ?? $template['id'] ?? '',
1449 'title' => $template['name'] ?? $template['title'] ?? '',
1450 'type' => $template['type'] ?? 'page',
1451 'thumbnail' => $screenshot,
1452 'is_pro' => $template['is_pro'] ?? false,
1453 'preview_url' => $template['preview_url'] ?? '',
1454 'source' => $template['source'] ?? '',
1455 'menu_order' => $template['menu_order'] ?? 0
1456 ];
1457 }
1458
1459 // Return templates from local cache
1460 return $templates;
1461 }
1462 }
1463
1464 // Try to get from cache manager if available
1465 if (class_exists('MasterAddons\Inc\Classes\Template_Kit_Cache')) {
1466 $cache_manager = \MasterAddons\Inc\Classes\Template_Kit_Cache::get_instance();
1467
1468 // Check if cache manager has a method for kit templates
1469 if (method_exists($cache_manager, 'get_cached_kit_content')) {
1470 $cached_data = $cache_manager->get_cached_kit_content($kit_id, $kit_category, $force_refresh);
1471 if ($cached_data !== false) {
1472 return $cached_data;
1473 }
1474 }
1475 }
1476
1477 // Fallback to transient cache
1478 $cache_key = 'jltma_kit_templates_' . $kit_category . '_' . $kit_id;
1479 $cache_expiry = 12 * HOUR_IN_SECONDS;
1480
1481 // Check transient cache if not forcing refresh
1482 if (!$force_refresh) {
1483 $cached_templates = get_transient($cache_key);
1484 if ($cached_templates !== false) {
1485 return $cached_templates;
1486 }
1487 }
1488
1489 // Only fetch from API if local files don't exist
1490 if (!file_exists($kit_manifest_path)) {
1491 // Fetch fresh data from API
1492 $fresh_templates = $this->fetch_kit_templates_from_api($kit_id, $kit_category);
1493
1494 if ($fresh_templates !== false && !empty($fresh_templates)) {
1495 set_transient($cache_key, $fresh_templates, $cache_expiry);
1496
1497 // Also save to file cache if manager is available
1498 if (isset($cache_manager) && method_exists($cache_manager, 'save_kit_content')) {
1499 $cache_manager->save_kit_content($kit_id, $kit_category, $fresh_templates);
1500 }
1501
1502 return $fresh_templates;
1503 }
1504 }
1505
1506 // Return cached data even if expired as fallback
1507 $cached_templates = get_transient($cache_key);
1508 return $cached_templates !== false ? $cached_templates : false;
1509 }
1510
1511 /**
1512 * Fetch kit templates from API
1513 */
1514 private function fetch_kit_templates_from_api($kit_id, $kit_category) {
1515 // Get API configuration
1516 $config = null;
1517 if (function_exists('MasterAddons\Inc\Admin\Templates\master_addons_templates')) {
1518 $templates_instance = \MasterAddons\Inc\Admin\Templates\master_addons_templates();
1519 if ($templates_instance && isset($templates_instance->config)) {
1520 $config = $templates_instance->config->get('api');
1521 }
1522 }
1523
1524 if (!$config) {
1525 return false;
1526 }
1527
1528 // Build API URL for template kits
1529 $api_url = $config['base'] . $config['path'] . '/template-kits/' . $kit_category . '/' . $kit_id;
1530
1531 // Add pro_enabled parameter if pro is enabled
1532 if (\MasterAddons\Inc\Classes\Helper::jltma_premium()) {
1533 $api_url = add_query_arg('pro_enabled', 'true', $api_url);
1534 }
1535
1536 $response = wp_remote_get($api_url, [
1537 'timeout' => 15, // Reduced from 60 to 15 seconds
1538 'sslverify' => false,
1539 'headers' => [
1540 'User-Agent' => 'Master Addons Template Kit Cache/' . JLTMA_VER
1541 ]
1542 ]);
1543
1544 // if (is_wp_error($response)) {
1545 // // error_log('MA Template Kit API Error: ' . $response->get_error_message());
1546 // return false;
1547 // }
1548
1549 $body = wp_remote_retrieve_body($response);
1550 $data = json_decode($body, true);
1551
1552 if (json_last_error() !== JSON_ERROR_NONE) {
1553 // error_log('MA Template Kit JSON Error: ' . json_last_error_msg());
1554 return false;
1555 }
1556
1557 if (!isset($data['success']) || !$data['success'] || !isset($data['templates'])) {
1558 return false;
1559 }
1560
1561 return $data['templates'];
1562 }
1563
1564 /**
1565 * AJAX: Delete purchased kit
1566 */
1567 public function delete_purchased_kit() {
1568 // Verify nonce and permissions
1569 $wpnonce_delete_kit = isset( $_POST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ) : '';
1570 $valid_nonce = wp_verify_nonce($wpnonce_delete_kit, 'jltma_template_library_nonce') ||
1571 wp_verify_nonce($wpnonce_delete_kit, 'jltma_template_kits_nonce_action');
1572
1573 if (!$valid_nonce || !current_user_can('manage_options')) {
1574 wp_send_json_error(['message' => 'Permission denied']);
1575 return;
1576 }
1577
1578 $kit_id = isset($_POST['kit_id']) ? sanitize_text_field( wp_unslash( $_POST['kit_id'] ) ) : '';
1579
1580 if (empty($kit_id)) {
1581 wp_send_json_error(['message' => 'Kit ID is required']);
1582 return;
1583 }
1584
1585 // Get cache manager instance
1586 if (!class_exists('MasterAddons\Inc\Classes\Template_Kit_Cache')) {
1587 wp_send_json_error(['message' => 'Cache manager not available']);
1588 return;
1589 }
1590
1591 $cache_manager = \MasterAddons\Inc\Classes\Template_Kit_Cache::get_instance();
1592
1593 // Check if this is actually a purchased kit
1594 if (!method_exists($cache_manager, 'is_kit_purchased') || !$cache_manager->is_kit_purchased($kit_id)) {
1595 wp_send_json_error(['message' => 'This kit is not a purchased kit or does not exist']);
1596 return;
1597 }
1598
1599 // Delete the kit and its files
1600 $deleted = $this->delete_kit_files($kit_id);
1601
1602 if ($deleted) {
1603 // Also remove from metadata
1604 if (method_exists($cache_manager, 'delete_purchased_kit')) {
1605 $cache_manager->delete_purchased_kit($kit_id);
1606 }
1607
1608 wp_send_json_success([
1609 'message' => 'Kit deleted successfully',
1610 'kit_id' => $kit_id
1611 ]);
1612 } else {
1613 wp_send_json_error(['message' => 'Failed to delete kit files']);
1614 }
1615 }
1616
1617 /**
1618 * Delete kit files from the filesystem
1619 */
1620 private function delete_kit_files($kit_id) {
1621 $upload_dir = wp_upload_dir();
1622 $kit_dir = $upload_dir['basedir'] . '/master_addons/purchased_kits/kits/kit_' . $kit_id;
1623
1624 // Check if directory exists
1625 if (!file_exists($kit_dir)) {
1626 return false;
1627 }
1628
1629 // Use WordPress filesystem
1630 global $wp_filesystem;
1631 if (!function_exists('WP_Filesystem')) {
1632 require_once ABSPATH . 'wp-admin/includes/file.php';
1633 }
1634 WP_Filesystem();
1635
1636 // Delete directory recursively
1637 return $this->delete_directory_recursive($kit_dir);
1638 }
1639
1640 /**
1641 * Recursively delete a directory and its contents
1642 */
1643 private function delete_directory_recursive($dir) {
1644 if (!file_exists($dir)) {
1645 return false;
1646 }
1647
1648 // Get all files and directories
1649 $files = array_diff(scandir($dir), array('.', '..'));
1650
1651 foreach ($files as $file) {
1652 $path = $dir . '/' . $file;
1653 if (is_dir($path)) {
1654 // Recursively delete subdirectories
1655 $this->delete_directory_recursive($path);
1656 } else {
1657 // Delete file
1658 wp_delete_file($path);
1659 }
1660 }
1661
1662 // Delete the empty directory
1663 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_rmdir -- Removes an empty local cache directory under wp-content/uploads.
1664 return @rmdir($dir);
1665 }
1666
1667 }
1668
1669 // Initialize the Template Library
1670 Template_Library::get_instance();