PluginProbe
Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits / 3.1.9
Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits v3.1.9
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 / classes / importer / class-jltma-demo-importer.php

class-jltma-demo-importer.php in Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits 3.1.9, at inc/classes/importer/class-jltma-demo-importer.php

1,949 lines 65.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace MasterAddons\Inc\Classes\Importer;
3
4 if (!defined('ABSPATH')) {
5 exit;
6 }
7
8 /**
9 * Master Addons Demo Importer
10 * Complete standalone demo import system
11 * No external plugin dependencies
12 *
13 * @package MasterAddons
14 * @subpackage Importer
15 * @since 2.0.0
16 */
17 class Demo_Importer {
18 private static $instance = null;
19 public $demo_path;
20 public $demo_url;
21 public $has_variations = false;
22 public $current_variation = null;
23 public $manifest_data = array();
24
25 public function __construct(){
26 add_action( 'after_setup_theme', [$this, 'init'], 20 );
27 }
28
29 public static function get_instance() {
30 if (self::$instance === null) {
31 self::$instance = new self();
32 }
33 return self::$instance;
34 }
35
36 public function init(){
37 if ( ! apply_filters( 'jltma_demo_importer_enabled', false ) || ! current_user_can( 'manage_options' ) ) {
38 return;
39 }
40
41 $this->set_demo_path();
42 add_action( 'admin_menu', array(&$this, 'register_sub_menu') );
43
44 // // AJAX handlers for demo import
45 add_action( 'wp_ajax_jltma_mark_imported_template', array($this, 'ajax_mark_imported_template') );
46
47 add_action( 'wp_ajax_jltma_import_demo_widgets', array($this, 'ajax_import_widgets') );
48
49 // AJAX handler for importing templates (similar to widgets)
50 add_action( 'wp_ajax_jltma_import_demo_template', array($this, 'ajax_import_template') );
51
52 // AJAX handler for fetching templates
53 add_action( 'wp_ajax_jltma_get_demo_templates', array($this, 'ajax_get_templates') );
54
55 // AJAX handler for fetching variations
56 add_action( 'wp_ajax_jltma_get_demo_variations', array($this, 'ajax_get_variations') );
57
58 // AJAX handler for importing a full variation
59 add_action( 'wp_ajax_jltma_import_full_variation', array($this, 'ajax_import_full_variation') );
60
61 // Enqueue scripts
62 add_action( 'admin_enqueue_scripts', array($this, 'enqueue_scripts') );
63 }
64
65 public function set_demo_path(){
66 // Get manifest data from filter
67 $this->manifest_data = apply_filters( 'jltma_demo_importer_variations', array() );
68
69 // Set demo path from manifest or use default
70 if (!empty($this->manifest_data['demo_directory'])) {
71 $this->demo_path = $this->manifest_data['demo_directory'];
72 $this->demo_url = str_replace(
73 wp_normalize_path(WP_CONTENT_DIR),
74 content_url(),
75 wp_normalize_path($this->demo_path)
76 );
77 $this->has_variations = true;
78 } else {
79 // Fallback to old structure
80 $theme_demo_folder = 'jltma_demo';
81 $this->demo_path = get_stylesheet_directory( ) . '/' . $theme_demo_folder;
82 $this->demo_url = get_stylesheet_directory_uri( ) . '/' . $theme_demo_folder;
83 $this->has_variations = false;
84 }
85 }
86
87
88 public function register_sub_menu(){
89 $menu_args = [
90 'parent_slug' => 'master-addons-settings',
91 'page_title' => esc_html__('Demo Importer', 'master-addons'),
92 'menu_title' => esc_html__('Demo Import', 'master-addons'),
93 'capability' => 'manage_options',
94 'menu_slug' => 'jltma-demo-importer',
95 'position' => 70
96 ];
97 $menu_args = apply_filters( 'jltma_import_demo_submenu_page', $menu_args );
98 add_submenu_page(
99 $menu_args['parent_slug'],
100 $menu_args['page_title'],
101 $menu_args['menu_title'],
102 $menu_args['capability'],
103 $menu_args['menu_slug'],
104 [$this, 'jltma_render_demo_importer_page']
105 );
106
107 }
108
109
110 public function jltma_render_demo_importer_page(){
111
112 ?>
113 <div id="jltma-demo-importer-root"></div>
114
115 <?php
116 }
117
118
119 /**
120 * Enqueue scripts for demo importer
121 */
122 public function enqueue_scripts( $hook ) {
123 if ( !str_ends_with( $hook, '_page_jltma-demo-importer' ) ) {
124 return ;
125 }
126
127 // Enqueue Widget Builder admin script
128 $widget_admin_url = JLTMA_ASSETS . 'js/admin/widget-admin.js';
129 wp_enqueue_script(
130 'jltma-widget-admin',
131 $widget_admin_url,
132 array('jquery'),
133 JLTMA_VER,
134 true
135 );
136
137 // Enqueue demo importer specific script
138 $importer_admin_url = JLTMA_URL . '/assets/js/admin/demo-importer-app.js';
139
140 wp_enqueue_script(
141 'jltma-demo-importer',
142 $importer_admin_url,
143 array('wp-element', 'wp-i18n', 'jquery', 'jltma-widget-admin'),
144 JLTMA_VER,
145 true
146 );
147
148
149 $current_theme = wp_get_theme();
150 $theme_name = $current_theme->get( 'Name' );
151 $theme_version = $current_theme->get( 'Version' );
152
153
154 $templates_dir = $this->demo_path . '/templates';
155 $images_dir = $this->demo_path . '/images';
156 $images_url = $this->demo_url . '/images';
157
158 // Localize script for demo importer
159 wp_localize_script('jltma-demo-importer', 'jltmaDemoImporter', array(
160 'ajax_url' => admin_url('admin-ajax.php'),
161 'nonce' => wp_create_nonce('jltma_demo_import'),
162 'themeName' => $theme_name,
163 'themeLogo' => $this->demo_url . '/' . $this->manifest_data['theme_logo'],
164 'themeVersion' => $theme_version,
165 'demoPath' => $this->demo_path,
166 'demoUrl' => $this->demo_url,
167 'templatesDir' => $templates_dir,
168 'imagesDir' => $images_dir,
169 'imagesUrl' => $images_url,
170 'hasVariations' => $this->has_variations,
171 'template_nonce' => wp_create_nonce('jltma_template_library_nonce') // Use template library nonce
172 ));
173
174 // Localize script for widget admin if not already done
175 wp_localize_script('jltma-widget-admin', 'jltmaWidgetAdmin', array(
176 'ajax_url' => admin_url('admin-ajax.php'),
177 'admin_url' => admin_url(),
178 'rest_url' => get_rest_url(null, 'jltma/v1'),
179 'widget_nonce' => wp_create_nonce('jltma_widget_nonce'),
180 'rest_nonce' => wp_create_nonce('wp_rest'),
181 'strings' => array(
182 'saving' => __('Saving...', 'master-addons'),
183 'saved' => __('Widget saved successfully!', 'master-addons'),
184 'error' => __('An error occurred. Please try again.', 'master-addons'),
185 'confirm_delete' => __('Are you sure you want to delete this widget?', 'master-addons'),
186 'copied' => __('Copied!', 'master-addons'),
187 'widget_title_required' => __('Widget title is required', 'master-addons'),
188 'category_added' => __('Category added successfully!', 'master-addons')
189 )
190 ));
191 }
192
193
194 /**
195 * AJAX handler to import widgets for a template
196 */
197 public function ajax_import_widgets() {
198 // Check nonce
199 if ( ! isset( $_POST['_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_nonce'] ) ), 'jltma_demo_import' ) ) {
200 wp_send_json_error( array( 'message' => 'Security check failed', 'debug' => 'Nonce verification failed' ) );
201 }
202
203 // Check permissions
204 if ( ! current_user_can( 'manage_options' ) ) {
205 wp_send_json_error( array( 'message' => 'Insufficient permissions', 'debug' => 'User lacks manage_options capability' ) );
206 }
207
208 // Get all widget files
209 $widgets_dir = $this->demo_path . '/jltma_widget';
210
211
212 if (!file_exists($widgets_dir)) {
213 wp_send_json_error( array(
214 'message' => 'Widgets directory not found at: ' . $widgets_dir,
215 'debug' => array(
216 'theme_dir' => get_stylesheet_directory(),
217 'expected_path' => $widgets_dir
218 )
219 ));
220 }
221
222 $widget_files = glob($widgets_dir . '/*.json');
223
224 if (empty($widget_files)) {
225 wp_send_json_error( array(
226 'message' => 'No widget JSON files found in directory',
227 'debug' => 'Directory exists but contains no .json files'
228 ));
229 }
230
231 $imported_count = 0;
232 $skipped_count = 0;
233 $errors = array();
234 $imported_widgets = array();
235
236 foreach ($widget_files as $widget_file) {
237 $filename = basename($widget_file);
238
239 $json_content = file_get_contents($widget_file);
240 $widget_data = json_decode($json_content, true);
241
242 if (!$widget_data || !isset($widget_data['widget'])) {
243 $errors[] = $filename . ' - Invalid JSON structure';
244 continue;
245 }
246
247 // Import widget using the existing widget builder functionality
248 $widget_to_import = $widget_data['widget'];
249
250 // Check if widget with same title already exists
251 $existing_widget_query = new \WP_Query(array(
252 'post_type' => 'jltma_widget',
253 'title' => $widget_to_import['title'],
254 'post_status' => 'any',
255 'posts_per_page' => 1,
256 'no_found_rows' => true,
257 'update_post_meta_cache' => false,
258 'update_post_term_cache' => false,
259 ));
260 $existing_widget = $existing_widget_query->have_posts() ? $existing_widget_query->posts[0] : null;
261 if ($existing_widget) {
262 $skipped_count++;
263 $imported_widgets[] = array(
264 'title' => $widget_to_import['title'],
265 'status' => 'skipped',
266 'message' => 'Already exists'
267 );
268 continue;
269 }
270
271 // Prepare widget data - ensure all required fields are present
272 $widget_post_data = array(
273 'post_title' => $widget_to_import['title'],
274 'post_type' => 'jltma_widget',
275 'post_status' => 'publish',
276 'post_content' => '', // Empty content since we use meta fields
277 );
278
279 // Create widget post
280 $widget_id = wp_insert_post($widget_post_data);
281
282 if ($widget_id && !is_wp_error($widget_id)) {
283 // Add widget meta data separately for better control
284 update_post_meta($widget_id, '_jltma_widget_data', wp_json_encode($widget_to_import));
285 update_post_meta($widget_id, '_jltma_widget_category', isset($widget_to_import['category']) ? $widget_to_import['category'] : 'general');
286 update_post_meta($widget_id, '_jltma_widget_html', isset($widget_to_import['html_code']) ? $widget_to_import['html_code'] : '');
287 update_post_meta($widget_id, '_jltma_widget_css', isset($widget_to_import['css_code']) ? $widget_to_import['css_code'] : '');
288 update_post_meta($widget_id, '_jltma_widget_js', isset($widget_to_import['js_code']) ? $widget_to_import['js_code'] : '');
289
290 // Store sections data if available
291 if (isset($widget_to_import['sections'])) {
292 update_post_meta($widget_id, '_jltma_widget_sections', wp_json_encode($widget_to_import['sections']));
293 }
294
295 $imported_count++;
296 $imported_widgets[] = array(
297 'title' => $widget_to_import['title'],
298 'status' => 'imported',
299 'id' => $widget_id
300 );
301 } else {
302 $error_message = is_wp_error($widget_id) ? $widget_id->get_error_message() : 'Unknown error';
303 $errors[] = $filename . ' - Failed to create: ' . $error_message;
304 }
305 }
306
307 // Prepare response
308 $total_processed = count($widget_files);
309 $message = '';
310
311 if ($imported_count > 0) {
312 $message = $imported_count . ' widget(s) imported successfully. ';
313 }
314 if ($skipped_count > 0) {
315 $message .= $skipped_count . ' widget(s) skipped (already exist). ';
316 }
317 if (!empty($errors)) {
318 $message .= count($errors) . ' error(s) occurred.';
319 }
320
321 if ($imported_count > 0 || $skipped_count > 0) {
322 wp_send_json_success( array(
323 'message' => trim($message),
324 'imported' => $imported_count,
325 'skipped' => $skipped_count,
326 'total' => $total_processed,
327 'errors' => $errors,
328 'widgets' => $imported_widgets,
329 'debug' => 'Check browser console and PHP error log for details'
330 ));
331 } else {
332 wp_send_json_error( array(
333 'message' => 'No widgets imported. ' . (!empty($errors) ? 'Errors: ' . implode(', ', $errors) : ''),
334 'errors' => $errors,
335 'debug' => 'Total files found: ' . $total_processed
336 ));
337 }
338 }
339
340 /**
341 * AJAX handler to import a template from demo folder only
342 */
343 public function ajax_import_template() {
344 // Check nonce - accept both demo import and template library nonces
345 $valid_nonce = (isset($_POST['_nonce']) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_nonce'] ) ), 'jltma_demo_import')) ||
346 (isset($_POST['_wpnonce']) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'jltma_template_library_nonce')) ||
347 (isset($_POST['_wpnonce']) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'jltma_template_kits_nonce_action'));
348
349 if (!$valid_nonce) {
350 wp_send_json_error(array('message' => 'Security check failed'));
351 return;
352 }
353
354 // Check permissions
355 if (!current_user_can('import')) {
356 wp_send_json_error(array('message' => 'Insufficient permissions'));
357 return;
358 }
359
360 // Get parameters
361 $template_id = isset($_POST['template_id']) ? sanitize_text_field( wp_unslash( $_POST['template_id'] ) ) : '';
362 $page_name = isset($_POST['page_name']) ? sanitize_text_field( wp_unslash( $_POST['page_name'] ) ) : null;
363 $is_reimport = isset($_POST['is_reimport']) && $_POST['is_reimport'] === '1';
364
365 if (empty($template_id)) {
366 wp_send_json_error(array('message' => 'Template ID is required'));
367 return;
368 }
369
370 // Import from local demo folder only
371 $result = $this->import_demo_template($template_id, $page_name, $is_reimport);
372
373 if ($result && !is_wp_error($result)) {
374 wp_send_json_success(array(
375 'message' => $result['message'] ?? 'Template imported successfully',
376 'page_id' => $result['page_id'] ?? 0,
377 'edit_url' => $result['edit_url'] ?? '',
378 'view_url' => $result['view_url'] ?? ''
379 ));
380 } else {
381 $error_message = is_wp_error($result) ? $result->get_error_message() : 'Failed to import template';
382 wp_send_json_error(array('message' => $error_message));
383 }
384 }
385
386 /**
387 * Import demo template from local files
388 */
389 private function import_demo_template($template_id, $page_name = null, $is_reimport = false) {
390 // First, import required dependencies (only once)
391 $this->import_demo_dependencies();
392
393 // Get template file from local demo directory
394 $pages_dir = $this->demo_path . '/pages';
395 $template_files = glob($pages_dir . '/*.json');
396
397 $template_data = null;
398 foreach ($template_files as $template_file) {
399 $json_content = file_get_contents($template_file);
400 $decoded = json_decode($json_content, true);
401
402 if ($decoded && isset($decoded['template_id']) && $decoded['template_id'] == $template_id) {
403 $template_data = $decoded;
404 break;
405 }
406 }
407
408 if (!$template_data) {
409 return new \WP_Error('template_not_found', 'Demo template not found');
410 }
411
412 // Ensure Elementor is loaded
413 if (!did_action('elementor/loaded')) {
414 return new \WP_Error('elementor_not_loaded', 'Elementor is not loaded');
415 }
416
417 // Prepare the page title and slug
418 $page_title = $page_name ? $page_name : (isset($template_data['title']) ? $template_data['title'] : 'Demo Page ' . $template_id);
419 $page_slug = isset($template_data['slug']) ? $template_data['slug'] : sanitize_title($page_title);
420 $template_type = isset($template_data['type']) ? $template_data['type'] : 'page';
421
422 // Handle re-import by adding suffix
423 if ($is_reimport) {
424 $page_title = $this->get_unique_page_title($page_title);
425 $page_slug = $page_slug . '-' . time();
426 }
427
428 // Prepare the page data
429 $page_data = array(
430 'post_title' => $page_title,
431 'post_name' => $page_slug,
432 'post_type' => 'page',
433 'post_status' => 'publish',
434 'post_content' => '', // Elementor will handle the content
435 'meta_input' => array(
436 '_elementor_edit_mode' => 'builder',
437 '_elementor_template_type' => $template_type,
438 '_wp_page_template' => 'elementor_header_footer',
439 '_jltma_demo_import_item' => true,
440 '_jltma_import_date' => current_time('mysql'),
441 '_jltma_original_template_id' => $template_id
442 )
443 );
444
445 // Create the page
446 $page_id = wp_insert_post($page_data);
447
448 if (is_wp_error($page_id)) {
449 return $page_id;
450 }
451
452 // Store the page mapping for menu imports
453 $this->store_page_mapping($page_slug, $page_id);
454
455 // Import Elementor data if available
456 if (isset($template_data['content']) && !empty($template_data['content'])) {
457 // Process the content for image imports if needed
458 $processed_content = $this->process_demo_content($template_data['content']);
459
460 // Save Elementor data
461 update_post_meta($page_id, '_elementor_data', wp_json_encode($processed_content));
462 update_post_meta($page_id, '_elementor_version', ELEMENTOR_VERSION);
463
464 // Clear Elementor cache
465 if (class_exists('\Elementor\Plugin')) {
466 \Elementor\Plugin::$instance->files_manager->clear_cache();
467 }
468 }
469
470 // Mark template as imported (optional)
471 $current_theme = wp_get_theme();
472 $theme_name = $current_theme->get('Name');
473 $theme_version = $current_theme->get('Version');
474 $template_signature = $theme_name . '_' . $theme_version . '_' . $template_id;
475 update_option($template_signature, true);
476
477 // Return success with page details
478 return array(
479 'page_id' => $page_id,
480 'edit_url' => admin_url('post.php?post=' . $page_id . '&action=elementor'),
481 'view_url' => get_permalink($page_id),
482 'message' => 'Demo template imported successfully'
483 );
484 }
485
486 /**
487 * Get unique page title by adding suffix if needed
488 */
489 private function get_unique_page_title($base_title) {
490 // Find existing pages with this title
491 $existing_pages = get_posts(array(
492 'post_type' => 'page',
493 'post_status' => 'any',
494 's' => $base_title,
495 'numberposts' => -1
496 ));
497
498 // Calculate suffix number
499 $suffix = 2;
500
501 // Check for existing numbered versions
502 foreach ($existing_pages as $page) {
503 if (preg_match('/(.+)\s+(\d+)$/', $page->post_title, $matches)) {
504 if ($matches[1] === $base_title) {
505 $num = intval($matches[2]);
506 if ($num >= $suffix) {
507 $suffix = $num + 1;
508 }
509 }
510 }
511 }
512
513 // Check if base title exists without number
514 $base_exists = false;
515 foreach ($existing_pages as $page) {
516 if ($page->post_title === $base_title) {
517 $base_exists = true;
518 break;
519 }
520 }
521
522 // Return title with suffix if base exists
523 return $base_exists ? $base_title . ' ' . $suffix : $base_title;
524 }
525
526 /**
527 * Process demo content for imports (handle images, etc.)
528 */
529 private function process_demo_content($content) {
530 // If content is a string, decode it
531 if (is_string($content)) {
532 $content = json_decode($content, true);
533 if (json_last_error() !== JSON_ERROR_NONE) {
534 return $content;
535 }
536 }
537
538 // If content is not an array, return as-is
539 if (!is_array($content)) {
540 return $content;
541 }
542
543 // Process content recursively to find and replace image URLs
544 $content = $this->process_content_images($content);
545
546 return $content;
547 }
548
549 /**
550 * Process content recursively to find and replace image URLs with imported attachments
551 */
552 private function process_content_images($content) {
553 if (!is_array($content)) {
554 return $content;
555 }
556
557 foreach ($content as $key => &$value) {
558 // Check if this is an image setting
559 if ($key === 'image' && is_array($value) && isset($value['url'])) {
560 // Process the image URL
561 $value = $this->process_image_field($value);
562 }
563 // Check for background images
564 elseif (($key === 'background_image' || $key === '_background_image' ||
565 $key === 'background_overlay_image' || $key === '_background_overlay_image') &&
566 is_array($value) && isset($value['url'])) {
567 // Process background image URL
568 $value = $this->process_image_field($value);
569 }
570 // Recursively process arrays
571 elseif (is_array($value)) {
572 $value = $this->process_content_images($value);
573 }
574 // Process image URLs in strings (for inline images in content)
575 elseif (is_string($value) && (strpos($value, '.jpg') !== false ||
576 strpos($value, '.jpeg') !== false ||
577 strpos($value, '.png') !== false ||
578 strpos($value, '.gif') !== false ||
579 strpos($value, '.svg') !== false)) {
580 $value = $this->process_string_with_images($value);
581 }
582 }
583
584 return $content;
585 }
586
587 /**
588 * Process an image field and replace with imported attachment
589 */
590 private function process_image_field($image_data) {
591 if (!isset($image_data['url'])) {
592 return $image_data;
593 }
594
595 $original_url = $image_data['url'];
596
597 // Check if this is just a filename (not a full URL)
598 if (!filter_var($original_url, FILTER_VALIDATE_URL)) {
599 // This is a filename from the demo folder
600 $attachment_id = $this->import_demo_image($original_url);
601
602 if ($attachment_id && !is_wp_error($attachment_id)) {
603 // Get the new attachment URL and metadata
604 $attachment_url = wp_get_attachment_url($attachment_id);
605 $attachment_metadata = wp_get_attachment_metadata($attachment_id);
606
607 // Update the image data
608 $image_data['url'] = $attachment_url;
609 $image_data['id'] = $attachment_id;
610
611 // Update source to library since it's now in Media Library
612 if (isset($image_data['source'])) {
613 $image_data['source'] = 'library';
614 }
615
616 // Add width and height if available
617 if ($attachment_metadata) {
618 if (isset($attachment_metadata['width'])) {
619 $image_data['width'] = $attachment_metadata['width'];
620 }
621 if (isset($attachment_metadata['height'])) {
622 $image_data['height'] = $attachment_metadata['height'];
623 }
624 }
625 }
626 }
627
628 return $image_data;
629 }
630
631 /**
632 * Process strings that might contain image references
633 */
634 private function process_string_with_images($content) {
635 // Pattern to match image filenames (including complex names with multiple hyphens, underscores, etc.)
636 $pattern = '/([a-zA-Z0-9_\-\.]+\.(?:jpg|jpeg|png|gif|svg))/i';
637
638 return preg_replace_callback($pattern, function($matches) {
639 $filename = $matches[1];
640
641 // Try to import this image
642 $attachment_id = $this->import_demo_image($filename);
643
644 if ($attachment_id && !is_wp_error($attachment_id)) {
645 // Get the new URL
646 $attachment_url = wp_get_attachment_url($attachment_id);
647 return $attachment_url;
648 }
649
650 // Return original if import failed
651 return $filename;
652 }, $content);
653 }
654
655 /**
656 * Import a demo image and create WordPress attachment
657 */
658 private function import_demo_image($filename) {
659 // Check if we've already imported this image
660 $cache_key = 'jltma_imported_image_' . md5($filename);
661 $cached_id = get_transient($cache_key);
662
663 if ($cached_id) {
664 // Verify the attachment still exists
665 if (get_post($cached_id)) {
666 return $cached_id;
667 }
668 }
669
670 // Build the full path to the image
671 $images_dir = $this->demo_path . '/images';
672 $image_path = $images_dir . '/' . $filename;
673
674 // Check if the file exists
675 if (!file_exists($image_path)) {
676 return false;
677 }
678
679 // Check file type
680 $filetype = wp_check_filetype($filename, null);
681 if (!$filetype['type']) {
682 return false;
683 }
684
685 // Prepare upload directory
686 $upload_dir = wp_upload_dir();
687 $upload_path = $upload_dir['path'] . '/' . $filename;
688
689 // Check if file already exists in uploads
690 if (file_exists($upload_path)) {
691 // Try to find existing attachment
692 global $wpdb;
693 $attachment_id = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- direct query required for attachment lookup by guid; no core API supports LIKE on guid
694 $wpdb->prepare(
695 "SELECT ID FROM {$wpdb->posts} WHERE guid LIKE %s AND post_type = 'attachment'",
696 '%/' . $filename
697 )
698 );
699
700 if ($attachment_id) {
701 // Cache the result
702 set_transient($cache_key, $attachment_id, DAY_IN_SECONDS);
703 return $attachment_id;
704 }
705 }
706
707 // Copy file to uploads directory
708 if (!copy($image_path, $upload_path)) {
709 return false;
710 }
711
712 // Create attachment
713 $attachment = array(
714 'guid' => $upload_dir['url'] . '/' . $filename,
715 'post_mime_type' => $filetype['type'],
716 'post_title' => preg_replace('/\.[^.]+$/', '', $filename),
717 'post_content' => '',
718 'post_status' => 'inherit'
719 );
720
721 // Insert the attachment
722 $attachment_id = wp_insert_attachment($attachment, $upload_path);
723
724 if (!is_wp_error($attachment_id)) {
725 // Generate attachment metadata
726 require_once(ABSPATH . 'wp-admin/includes/image.php');
727 $attachment_metadata = wp_generate_attachment_metadata($attachment_id, $upload_path);
728 wp_update_attachment_metadata($attachment_id, $attachment_metadata);
729
730 // Mark as imported from demo
731 update_post_meta($attachment_id, '_jltma_demo_import_image', true);
732 update_post_meta($attachment_id, '_jltma_original_filename', $filename);
733
734 // Cache the result for 1 day
735 set_transient($cache_key, $attachment_id, DAY_IN_SECONDS);
736
737 return $attachment_id;
738 }
739
740 return false;
741 }
742
743
744 /**
745 * AJAX handler to get templates list
746 */
747 public function ajax_get_templates() {
748 // Check nonce
749 if ( ! isset( $_POST['_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_nonce'] ) ), 'jltma_demo_import' ) ) {
750 wp_send_json_error( array( 'message' => 'Security check failed' ) );
751 }
752
753 // Check permissions
754 if ( ! current_user_can( 'manage_options' ) ) {
755 wp_send_json_error( array( 'message' => 'Insufficient permissions' ) );
756 }
757
758 $pages = $this->demo_path . '/pages';
759 $images_dir = $this->demo_path . '/images';
760
761 if (!file_exists($pages)) {
762 wp_send_json_error( array(
763 'message' => 'Templates directory not found',
764 'path' => $pages
765 ));
766 return;
767 }
768
769 // Get all JSON template files
770 $template_files = glob($pages . '/*.json');
771 $templates = array();
772 $current_theme = wp_get_theme();
773 $theme_name = $current_theme->get( 'Name' );
774 $theme_version = $current_theme->get( 'Version' );
775
776
777 foreach ($template_files as $template_file) {
778 // Read and parse the JSON file
779 $json_content = file_get_contents($template_file);
780 $template_data = json_decode($json_content, true);
781 // Skip if JSON decode failed
782 if (!$template_data) {
783 continue;
784 }
785
786 // Skip global.json file if it exists
787 if (isset($template_data['title']) && $template_data['title'] === 'global') {
788 continue;
789 }
790
791 // Check if template is already imported
792 $template_id = isset($template_data['template_id']) ? $template_data['template_id'] : '';
793 $template_signature = $theme_name . '_' . $theme_version . '_' . $template_id;
794 $imported = get_option( $template_signature, false );
795 $template_data['imported'] = $imported;
796
797 // Add to templates array
798 $templates[] = $template_data;
799 }
800
801 wp_send_json_success( $templates );
802 }
803
804
805 /**
806 * AJAX handler to get demo variations
807 */
808 public function ajax_get_variations() {
809 // Check nonce
810 if ( ! isset( $_POST['_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_nonce'] ) ), 'jltma_demo_import' ) ) {
811 wp_send_json_error( array( 'message' => 'Security check failed' ) );
812 }
813
814 // Check permissions
815 if ( ! current_user_can( 'manage_options' ) ) {
816 wp_send_json_error( array( 'message' => 'Insufficient permissions' ) );
817 }
818
819 $manifest_data = $this->manifest_data;
820 if (empty($manifest_data) || empty($manifest_data['demos'])) {
821 wp_send_json_error( array(
822 'message' => 'No demo variations found',
823 'legacy' => true
824 ));
825 return;
826 }
827
828 // Get selected category from POST (case-insensitive)
829 $selected_category = 'all';
830 if ( isset( $_POST['category'] ) && ! empty( $_POST['category'] ) ) {
831 $selected_category = strtolower( sanitize_text_field( wp_unslash( $_POST['category'] ) ) );
832 }
833
834 // Build categories list for tabs
835 $categories = array_unique(array_column($manifest_data['demos'], 'category'));
836 $tabs = array(
837 array('label' => 'All')
838 );
839
840 foreach ($categories as $cat) {
841 $tabs[] = array('label' => $cat);
842 }
843
844 // Process demos and format them as variations
845 $variations = array();
846 $demo_directory = $manifest_data['demo_directory'];
847 $demo_url = str_replace(
848 wp_normalize_path(WP_CONTENT_DIR),
849 content_url(),
850 wp_normalize_path($demo_directory)
851 );
852
853 foreach ($manifest_data['demos'] as $demo) {
854 // Filter by category (case-insensitive comparison)
855 if ( $selected_category !== 'all' ) {
856 $demo_category = isset( $demo['category'] ) ? strtolower( $demo['category'] ) : '';
857 if ( $demo_category !== $selected_category ) {
858 continue; // Skip this demo if it doesn't match the selected category
859 }
860 }
861
862 $variation = array(
863 'name' => $demo['name'],
864 'folder' => $demo['import_folder'],
865 'imported' => get_option('jltma_variation_imported_' . $demo['import_folder'], false)
866 );
867 // Handle thumbnail
868 if (!empty($demo['thumbnail'])) {
869 $variation['thumbnail'] = $this->demo_url . '/'. $demo['import_folder'] . '/'. $demo['thumbnail'];
870 } else {
871 $variation['thumbnail'] = class_exists('\Elementor\Utils') ? \Elementor\Utils::get_placeholder_image_src() : '';
872 }
873
874 // Add summary counts for the variation
875 $variation['summary'] = $this->get_variation_summary($demo_directory . '/' . $demo['import_folder']);
876
877 // Store menu assignments and homepage settings for later use
878 if (!empty($demo['menu_assignments'])) {
879 $variation['menu_assignments'] = $demo['menu_assignments'];
880 }
881 if (!empty($demo['homepage_settings'])) {
882 $variation['homepage_settings'] = $demo['homepage_settings'];
883 }
884
885 $variations[] = $variation;
886 }
887
888 wp_send_json_success(array(
889 'theme' => get_stylesheet(),
890 'theme_name' => wp_get_theme()->get('Name'),
891 'variations' => $variations,
892 'categories' => $tabs
893 ));
894 }
895
896 /**
897 * Get summary counts for a variation
898 */
899 private function get_variation_summary($variation_path) {
900 $summary = array(
901 'page' => 0,
902 'jltma_widget' => 0,
903 'elementor_library' => 0,
904 'menus' => 0
905 );
906
907 // Count pages
908 $pages_dir = $variation_path . '/pages';
909 if (is_dir($pages_dir)) {
910 $pages = glob($pages_dir . '/*.json');
911 $summary['page'] = count($pages);
912 }
913
914 // Count widgets
915 $widgets_dir = $variation_path . '/jltma_widget';
916 if (is_dir($widgets_dir)) {
917 $widgets = glob($widgets_dir . '/*.json');
918 $summary['jltma_widget'] = count($widgets);
919 }
920
921 // Count elementor library templates
922 $library_dir = $variation_path . '/elementor_library';
923 if (is_dir($library_dir)) {
924 $templates = glob($library_dir . '/*.json');
925 $summary['elementor_library'] = count($templates);
926 }
927
928 // Count menus (menus are in the demo root/menus folder)
929 $menus_file = $this->demo_path . '/menus/menus.json';
930 if (file_exists($menus_file)) {
931 $menus_data = json_decode(file_get_contents($menus_file), true);
932 if (is_array($menus_data)) {
933 $summary['menus'] = count($menus_data);
934 }
935 }
936
937 return $summary;
938 }
939
940 /**
941 * AJAX handler to import a full variation
942 */
943 public function ajax_import_full_variation() {
944 // Check nonce
945 if ( ! isset( $_POST['_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_nonce'] ) ), 'jltma_demo_import' ) ) {
946 wp_send_json_error( array( 'message' => 'Security check failed' ) );
947 }
948
949 // Check permissions
950 if ( ! current_user_can( 'manage_options' ) ) {
951 wp_send_json_error( array( 'message' => 'Insufficient permissions' ) );
952 }
953
954 $variation_folder = isset($_POST['variation']) ? sanitize_text_field( wp_unslash( $_POST['variation'] ) ) : '';
955
956 if (empty($variation_folder)) {
957 wp_send_json_error( array( 'message' => 'Variation folder is required' ) );
958 return;
959 }
960
961 // Get the manifest data to find variation settings
962 $manifest_data = apply_filters( 'jltma_demo_importer_variations', array() );
963 $current_demo = null;
964
965 if (!empty($manifest_data['demos'])) {
966 foreach ($manifest_data['demos'] as $demo) {
967 if ($demo['import_folder'] === $variation_folder) {
968 $current_demo = $demo;
969 break;
970 }
971 }
972 }
973
974 // Set the current variation path
975 $this->current_variation = $variation_folder;
976 $variation_path = $this->demo_path . '/' . $variation_folder;
977
978 if (!file_exists($variation_path)) {
979 wp_send_json_error( array(
980 'message' => 'Variation folder not found: ' . $variation_folder
981 ));
982 return;
983 }
984
985 // Start the import process
986 $import_result = array(
987 'widgets' => 0,
988 'templates' => 0,
989 'library_templates' => 0,
990 'menus' => 0,
991 'settings' => false,
992 'errors' => array()
993 );
994
995 // Temporarily update demo path to the variation path
996 $original_path = $this->demo_path;
997 $original_url = $this->demo_url;
998 $this->demo_path = $variation_path;
999 $this->demo_url = $this->demo_url . '/' . $variation_folder;
1000
1001 // Clear page mappings for new import
1002 $this->clear_page_mappings();
1003
1004 // Import dependencies (Elementor templates, settings - but NOT menus yet)
1005 $this->import_demo_dependencies();
1006
1007 // Import widgets
1008 $widgets_result = $this->import_variation_widgets();
1009 $import_result['widgets'] = $widgets_result['imported'] ?? 0;
1010
1011 // Import all templates/pages FIRST
1012 $templates_result = $this->import_all_variation_templates();
1013 $import_result['templates'] = $templates_result['imported'] ?? 0;
1014
1015 // NOW import menus after pages have been imported
1016 // Restore original path temporarily for menu import (menus are in demo root)
1017 $temp_path = $this->demo_path;
1018 $this->demo_path = $original_path;
1019 $created_menus = $this->import_demo_menus();
1020 $this->demo_path = $temp_path;
1021
1022 // Assign menus to locations if specified in manifest
1023 if ($current_demo && !empty($current_demo['menu_assignments']) && $created_menus) {
1024 $this->assign_menus_to_locations($current_demo['menu_assignments'], $created_menus);
1025 }
1026
1027 // Set homepage and posts page if specified (using slugs from manifest)
1028 if ($current_demo) {
1029 $homepage_settings = array();
1030 if (!empty($current_demo['page_on_front'])) {
1031 $homepage_settings['page_on_front'] = $current_demo['page_on_front'];
1032 }
1033 if (!empty($current_demo['page_for_posts'])) {
1034 $homepage_settings['page_for_posts'] = $current_demo['page_for_posts'];
1035 }
1036 if (!empty($homepage_settings)) {
1037 $this->set_homepage_settings($homepage_settings);
1038 }
1039 }
1040
1041 // Count imported library templates
1042 $library_dir = $variation_path . '/elementor_library';
1043 if (file_exists($library_dir)) {
1044 $library_files = glob($library_dir . '/*.json');
1045 $import_result['library_templates'] = count($library_files) - 1;
1046 }
1047
1048 // Count imported menus (menus are in demo root/menus folder)
1049 $menus_file = $original_path . '/menus/menus.json';
1050 if (file_exists($menus_file)) {
1051 $menus_content = file_get_contents($menus_file);
1052 $menus_data = json_decode($menus_content, true);
1053 if ($menus_data) {
1054 $import_result['menus'] = count($menus_data);
1055 }
1056 }
1057
1058 // Settings folder no longer exists
1059 $import_result['settings'] = false;
1060
1061 // Restore original paths
1062 $this->demo_path = $original_path;
1063 $this->demo_url = $original_url;
1064
1065 // Mark variation as imported
1066 update_option('jltma_variation_imported_' . $variation_folder, true);
1067
1068 // Clear the dependencies cache to allow re-import of other variations
1069 delete_transient('jltma_demo_dependencies_imported');
1070
1071 wp_send_json_success( array(
1072 'message' => 'Variation imported successfully',
1073 'variation' => $variation_folder,
1074 'result' => $import_result
1075 ));
1076 }
1077
1078 /**
1079 * Import all widgets for a variation
1080 */
1081 private function import_variation_widgets() {
1082 $widgets_dir = $this->demo_path . '/jltma_widget';
1083
1084 if (!file_exists($widgets_dir)) {
1085 return array('imported' => 0, 'skipped' => 0);
1086 }
1087
1088 $widget_files = glob($widgets_dir . '/*.json');
1089 $imported_count = 0;
1090 $skipped_count = 0;
1091
1092 foreach ($widget_files as $widget_file) {
1093 $json_content = file_get_contents($widget_file);
1094 $widget_data = json_decode($json_content, true);
1095
1096 if (!$widget_data || !isset($widget_data['widget'])) {
1097 continue;
1098 }
1099
1100 $widget_to_import = $widget_data['widget'];
1101
1102 // Check if widget already exists
1103 $existing_widget_query = new \WP_Query(array(
1104 'post_type' => 'jltma_widget',
1105 'title' => $widget_to_import['title'],
1106 'post_status' => 'any',
1107 'posts_per_page' => 1,
1108 'no_found_rows' => true,
1109 'update_post_meta_cache' => false,
1110 'update_post_term_cache' => false,
1111 ));
1112 $existing_widget = $existing_widget_query->have_posts() ? $existing_widget_query->posts[0] : null;
1113 if ($existing_widget) {
1114 $skipped_count++;
1115 continue;
1116 }
1117
1118 // Create widget post
1119 $widget_post_data = array(
1120 'post_title' => $widget_to_import['title'],
1121 'post_type' => 'jltma_widget',
1122 'post_status' => 'publish',
1123 'post_content' => '',
1124 );
1125
1126 $widget_id = wp_insert_post($widget_post_data);
1127
1128 if ($widget_id && !is_wp_error($widget_id)) {
1129 // Add widget meta data
1130 update_post_meta($widget_id, '_jltma_widget_data', wp_json_encode($widget_to_import));
1131 update_post_meta($widget_id, '_jltma_widget_category', $widget_to_import['category'] ?? 'general');
1132 update_post_meta($widget_id, '_jltma_widget_html', $widget_to_import['html_code'] ?? '');
1133 update_post_meta($widget_id, '_jltma_widget_css', $widget_to_import['css_code'] ?? '');
1134 update_post_meta($widget_id, '_jltma_widget_js', $widget_to_import['js_code'] ?? '');
1135
1136 if (isset($widget_to_import['sections'])) {
1137 update_post_meta($widget_id, '_jltma_widget_sections', wp_json_encode($widget_to_import['sections']));
1138 }
1139
1140 $imported_count++;
1141 }
1142 }
1143
1144 return array('imported' => $imported_count, 'skipped' => $skipped_count);
1145 }
1146
1147 /**
1148 * Import all templates for a variation
1149 */
1150 private function import_all_variation_templates() {
1151 $pages_dir = $this->demo_path . '/pages';
1152
1153 if (!file_exists($pages_dir)) {
1154 return array('imported' => 0, 'errors' => array());
1155 }
1156
1157 $template_files = glob($pages_dir . '/*.json');
1158 $imported_count = 0;
1159 $errors = array();
1160
1161 foreach ($template_files as $template_file) {
1162 $json_content = file_get_contents($template_file);
1163 $template_data = json_decode($json_content, true);
1164
1165 if (!$template_data) {
1166 continue;
1167 }
1168
1169 // Skip global.json if exists
1170 if (isset($template_data['title']) && $template_data['title'] === 'global') {
1171 continue;
1172 }
1173
1174 $template_id = $template_data['template_id'] ?? basename($template_file, '.json');
1175
1176 // Import the template
1177 $result = $this->import_demo_template($template_id, null, false);
1178
1179 if ($result && !is_wp_error($result)) {
1180 $imported_count++;
1181
1182 // Store page mapping if slug is available
1183 if (isset($template_data['slug']) && isset($result['page_id'])) {
1184 $this->store_page_mapping($template_data['slug'], $result['page_id']);
1185 }
1186 } else {
1187 $error_message = is_wp_error($result) ? $result->get_error_message() : 'Unknown error';
1188 $errors[] = $template_id . ': ' . $error_message;
1189 }
1190 }
1191
1192 return array('imported' => $imported_count, 'errors' => $errors);
1193 }
1194
1195 public function ajax_mark_imported_template(){
1196 if ( ! isset( $_POST['_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_nonce'] ) ), 'jltma_demo_import' ) ) {
1197 wp_send_json_error( array( 'message' => 'Security check failed', 'debug' => 'Nonce verification failed' ) );
1198 }
1199
1200 // Check permissions
1201 if ( ! current_user_can( 'manage_options' ) ) {
1202 wp_send_json_error( array( 'message' => 'Insufficient permissions', 'debug' => 'User lacks manage_options capability' ) );
1203 }
1204
1205 $template_id = isset($_POST['template_id']) ? sanitize_text_field( wp_unslash( $_POST['template_id'] ) ) : '';
1206 $theme_name = isset($_POST['theme_name']) ? sanitize_text_field( wp_unslash( $_POST['theme_name'] ) ) : '';
1207 $theme_version = isset($_POST['theme_version']) ? sanitize_text_field( wp_unslash( $_POST['theme_version'] ) ) : '';
1208
1209 if (empty($template_id)) {
1210 wp_send_json_error( array( 'message' => 'Template ID is required' ) );
1211 return;
1212 }
1213
1214 $template_signature = $theme_name . '_' . $theme_version . '_' . $template_id;
1215 update_option($template_signature, true);
1216
1217 wp_send_json_success( array(
1218 'message' => 'Template marked as imported',
1219 'template_id' => $template_id
1220 ));
1221 }
1222
1223 /**
1224 * Store page mapping for menu imports
1225 */
1226 private function store_page_mapping($slug, $page_id) {
1227 $mappings = get_transient('jltma_import_page_mappings') ?: array();
1228 $mappings[$slug] = $page_id;
1229 set_transient('jltma_import_page_mappings', $mappings, HOUR_IN_SECONDS);
1230 }
1231
1232 /**
1233 * Get page mapping
1234 */
1235 private function get_page_mapping($slug) {
1236 $mappings = get_transient('jltma_import_page_mappings') ?: array();
1237 return isset($mappings[$slug]) ? $mappings[$slug] : null;
1238 }
1239
1240 /**
1241 * Clear page mappings
1242 */
1243 private function clear_page_mappings() {
1244 delete_transient('jltma_import_page_mappings');
1245 }
1246
1247 /**
1248 * Assign imported menus to their specified locations
1249 */
1250 private function assign_menus_to_locations($menu_assignments, $created_menus = null) {
1251 if (empty($menu_assignments) || !is_array($menu_assignments)) {
1252 return;
1253 }
1254
1255 $locations = get_theme_mod('nav_menu_locations', array());
1256
1257 foreach ($menu_assignments as $location => $menu_name) {
1258 // Skip empty assignments
1259 if (empty($menu_name)) {
1260 continue;
1261 }
1262
1263 // If we have created menus map, use it first
1264 if ($created_menus && isset($created_menus[$menu_name])) {
1265 $locations[$location] = $created_menus[$menu_name];
1266 } else {
1267 // Fallback: Find the menu by name from all menus
1268 $menu = get_term_by('name', $menu_name, 'nav_menu');
1269 if ($menu) {
1270 $locations[$location] = $menu->term_id;
1271 }
1272 }
1273 }
1274
1275 set_theme_mod('nav_menu_locations', $locations);
1276 }
1277
1278 /**
1279 * Set homepage and posts page settings using page slugs
1280 */
1281 private function set_homepage_settings($homepage_settings) {
1282 if (empty($homepage_settings) || !is_array($homepage_settings)) {
1283 return;
1284 }
1285
1286 // Get page mappings to find imported pages
1287 $page_mappings = get_transient('jltma_import_page_mappings');
1288 if (!$page_mappings) {
1289 $page_mappings = array();
1290 }
1291
1292 // Set the homepage using slug
1293 if (!empty($homepage_settings['page_on_front'])) {
1294 $home_page_slug = $homepage_settings['page_on_front'];
1295
1296 // Find the imported page with this slug
1297 $home_page = get_page_by_path($home_page_slug);
1298
1299 // If not found by slug, try mappings (in case slug was modified)
1300 if (!$home_page && !empty($page_mappings)) {
1301 foreach ($page_mappings as $original_slug => $page_id) {
1302 if ($original_slug === $home_page_slug) {
1303 $home_page = get_post($page_id);
1304 break;
1305 }
1306 }
1307 }
1308
1309 if ($home_page && $home_page->post_type === 'page') {
1310 update_option('page_on_front', $home_page->ID);
1311 update_option('show_on_front', 'page');
1312 }
1313 }
1314
1315 // Set the posts page (blog page) using slug
1316 if (!empty($homepage_settings['page_for_posts'])) {
1317 $posts_page_slug = $homepage_settings['page_for_posts'];
1318
1319 // Find the imported page with this slug
1320 $posts_page = get_page_by_path($posts_page_slug);
1321
1322 // If not found by slug, try mappings (in case slug was modified)
1323 if (!$posts_page && !empty($page_mappings)) {
1324 foreach ($page_mappings as $original_slug => $page_id) {
1325 if ($original_slug === $posts_page_slug) {
1326 $posts_page = get_post($page_id);
1327 break;
1328 }
1329 }
1330 }
1331
1332 if ($posts_page && $posts_page->post_type === 'page') {
1333 update_option('page_for_posts', $posts_page->ID);
1334 update_option('show_on_front', 'page');
1335 }
1336 }
1337 }
1338
1339 /**
1340 * Import demo dependencies (Elementor templates, menus, settings)
1341 * This runs only once per session to avoid duplicates
1342 */
1343 private function import_demo_dependencies() {
1344 // Check if dependencies already imported in this session
1345 $dependencies_imported = get_transient('jltma_demo_dependencies_imported');
1346 if ($dependencies_imported) {
1347 return true;
1348 }
1349
1350 // Clear any previous page mappings
1351 $this->clear_page_mappings();
1352
1353 // Pre-import commonly used images (optional optimization)
1354 $this->preload_demo_images();
1355
1356 // Import Elementor library templates
1357 $this->import_elementor_library_templates();
1358
1359 // Import and set global kit styles
1360 $this->import_and_set_global_kit();
1361
1362 // Import menus - moved to after pages are imported
1363 // $this->import_demo_menus();
1364
1365 // Settings folder no longer exists, skip settings import
1366
1367 // Mark dependencies as imported for 1 hour
1368 set_transient('jltma_demo_dependencies_imported', true, HOUR_IN_SECONDS);
1369
1370 return true;
1371 }
1372
1373 /**
1374 * Preload common demo images for better performance
1375 */
1376 private function preload_demo_images() {
1377 $images_dir = $this->demo_path . '/images';
1378
1379 if (!file_exists($images_dir)) {
1380 return false;
1381 }
1382
1383 // Get a few common image types to preload
1384 $common_extensions = array('jpg', 'jpeg', 'png', 'gif', 'svg');
1385 $preloaded_count = 0;
1386 $max_preload = 10; // Limit to prevent timeout
1387
1388 foreach ($common_extensions as $ext) {
1389 $pattern = $images_dir . '/*.' . $ext;
1390 $files = glob($pattern);
1391
1392 if ($files) {
1393 foreach ($files as $file_path) {
1394 if ($preloaded_count >= $max_preload) {
1395 break 2;
1396 }
1397
1398 $filename = basename($file_path);
1399 // This will cache the image ID for faster access later
1400 $this->import_demo_image($filename);
1401 $preloaded_count++;
1402 }
1403 }
1404 }
1405
1406 return $preloaded_count;
1407 }
1408
1409 /**
1410 * Import Elementor library templates (header, footer, etc.)
1411 */
1412 private function import_elementor_library_templates() {
1413 $library_dir = $this->demo_path . '/elementor_library';
1414
1415 if (!file_exists($library_dir)) {
1416 return false;
1417 }
1418
1419 $template_files = glob($library_dir . '/*.json');
1420 $imported_count = 0;
1421
1422 foreach ($template_files as $template_file) {
1423 $filename = basename($template_file, '.json');
1424
1425 $json_content = file_get_contents($template_file);
1426 $template_data = json_decode($json_content, true);
1427
1428 if (!$template_data) {
1429 continue;
1430 }
1431
1432 // Skip kit files as they're handled separately
1433 if (isset($template_data['metadata']['template_type']) &&
1434 $template_data['metadata']['template_type'] === 'kit') {
1435 continue;
1436 }
1437
1438 // Check if template already exists
1439 $existing_template = get_posts(array(
1440 'post_type' => 'elementor_library',
1441 'title' => $template_data['title'] ?? $filename,
1442 'post_status' => 'publish',
1443 'numberposts' => 1
1444 ));
1445
1446 if (!empty($existing_template)) {
1447 continue; // Skip if already exists
1448 }
1449
1450 // Determine template type
1451 $template_type = 'section'; // default
1452 if (stripos($filename, 'header') !== false) {
1453 $template_type = 'header';
1454 } elseif (stripos($filename, 'footer') !== false) {
1455 $template_type = 'footer';
1456 } elseif (stripos($filename, 'post') !== false || stripos($filename, 'single') !== false) {
1457 $template_type = 'single';
1458 } elseif (stripos($filename, 'archive') !== false) {
1459 $template_type = 'archive';
1460 } elseif (stripos($filename, 'menu') !== false || stripos($filename, 'nav') !== false) {
1461 $template_type = 'section';
1462 }
1463
1464 // Process content for images before saving
1465 $processed_content = $this->process_demo_content($template_data['content'] ?? []);
1466
1467 // Create the Elementor library post
1468 $post_data = array(
1469 'post_title' => $template_data['title'] ?? ucfirst($filename),
1470 'post_type' => 'elementor_library',
1471 'post_status' => 'publish',
1472 'post_content' => '',
1473 'meta_input' => array(
1474 '_elementor_edit_mode' => 'builder',
1475 '_elementor_template_type' => $template_type,
1476 '_elementor_data' => wp_json_encode($processed_content),
1477 '_jltma_demo_import_item' => true,
1478 '_jltma_import_source' => 'demo_library'
1479 )
1480 );
1481
1482 $post_id = wp_insert_post($post_data);
1483
1484 if ($post_id && !is_wp_error($post_id)) {
1485 $imported_count++;
1486
1487 // Store the imported template ID for reference
1488 update_option('jltma_imported_' . $filename . '_id', $post_id);
1489 }
1490 }
1491
1492 return $imported_count;
1493 }
1494
1495 /**
1496 * Import and set global kit styles
1497 */
1498 private function import_and_set_global_kit() {
1499 $library_dir = $this->demo_path . '/elementor_library';
1500
1501 if (!file_exists($library_dir)) {
1502 return false;
1503 }
1504
1505 // Check if kit already imported
1506 $kit_imported = get_option('jltma_demo_kit_imported');
1507 if ($kit_imported) {
1508 return true;
1509 }
1510
1511 // Look for any file with kit metadata
1512 $library_files = glob($library_dir . '/*.json');
1513 $kit_file = null;
1514 $kit_data = null;
1515
1516 foreach ($library_files as $file) {
1517 $json_content = file_get_contents($file);
1518 $template_data = json_decode($json_content, true);
1519
1520 // Check if this is a kit by looking at metadata
1521 if ($template_data && isset($template_data['metadata']['template_type']) &&
1522 $template_data['metadata']['template_type'] === 'kit') {
1523 $kit_file = $file;
1524 $kit_data = $template_data;
1525 break;
1526 }
1527 }
1528
1529 if (!$kit_file || !$kit_data) {
1530 return false;
1531 }
1532
1533 // Create the kit post
1534 $kit_post = array(
1535 'post_title' => $kit_data['title'] ?? 'Global Kit Styles',
1536 'post_type' => 'elementor_library',
1537 'post_status' => 'publish',
1538 'meta_input' => array(
1539 '_elementor_edit_mode' => 'builder',
1540 '_elementor_template_type' => 'kit',
1541 '_elementor_data' => wp_json_encode($kit_data['content'] ?? [])
1542 )
1543 );
1544
1545 $kit_id = wp_insert_post($kit_post);
1546
1547 if ($kit_id && !is_wp_error($kit_id)) {
1548 // Set this kit as the active kit in Elementor
1549 update_option('elementor_active_kit', $kit_id);
1550 update_option('jltma_demo_kit_imported', $kit_id);
1551
1552 return true;
1553 }
1554
1555 return false;
1556 }
1557
1558 /**
1559 * Import demo menus - only import menus used in current variation
1560 */
1561 private function import_demo_menus() {
1562 // Get the current demo configuration to know which menus to import
1563 $manifest_data = apply_filters('jltma_demo_importer_variations', array());
1564 $current_demo = null;
1565
1566 if (!empty($manifest_data['demos']) && $this->current_variation) {
1567 foreach ($manifest_data['demos'] as $demo) {
1568 if ($demo['import_folder'] === $this->current_variation) {
1569 $current_demo = $demo;
1570 break;
1571 }
1572 }
1573 }
1574
1575 // If no current demo or no menu assignments, skip
1576 if (!$current_demo || empty($current_demo['menu_assignments'])) {
1577 return false;
1578 }
1579
1580 // Menus are in demo root/menus folder
1581 $menus_file = $this->demo_path . '/menus/menus.json';
1582
1583 if (!file_exists($menus_file)) {
1584 return false;
1585 }
1586
1587 // Check if menus already imported (use variation-specific key)
1588 $import_key = 'jltma_demo_menus_imported_' . $this->current_variation;
1589 $menus_imported = get_option($import_key);
1590 if ($menus_imported) {
1591 return true;
1592 }
1593
1594 $json_content = file_get_contents($menus_file);
1595 $menus_data = json_decode($json_content, true);
1596
1597 if (!$menus_data) {
1598 return false;
1599 }
1600
1601 // Get list of menu names that need to be imported for this variation
1602 $menus_to_import = array_filter(array_unique(array_values($current_demo['menu_assignments'])));
1603 if (empty($menus_to_import)) {
1604 return false;
1605 }
1606
1607 $created_menus = array();
1608
1609 // Only import menus that are assigned in this variation
1610 foreach ($menus_data as $menu_data) {
1611 // New format (object with name, slug, items)
1612 if (is_array($menu_data) && isset($menu_data['name'])) {
1613 $menu_name = $menu_data['name'];
1614
1615 // Skip this menu if it's not used in current variation
1616 if (!in_array($menu_name, $menus_to_import)) {
1617 continue;
1618 }
1619
1620 $menu_slug = $menu_data['slug'] ?? sanitize_title($menu_name);
1621 $menu_items = $menu_data['items'] ?? array();
1622
1623 // Check if menu already exists by name
1624 $menu_exists = get_term_by('name', $menu_name, 'nav_menu');
1625
1626 if (!$menu_exists) {
1627 // Create the menu
1628 $menu_id = wp_create_nav_menu($menu_name);
1629
1630 if (!is_wp_error($menu_id)) {
1631 // Add menu items
1632 $this->add_menu_items($menu_id, $menu_items);
1633 $created_menus[$menu_name] = $menu_id; // Store by name for assignment
1634 }
1635 } else {
1636 $created_menus[$menu_name] = $menu_exists->term_id;
1637 }
1638 }
1639 }
1640
1641 update_option($import_key, true);
1642 return $created_menus; // Return created menus for assignment
1643 }
1644
1645 /**
1646 * Helper function to add menu items recursively
1647 */
1648 private function add_menu_items($menu_id, $items, $parent_id = 0) {
1649 if (!is_array($items) || empty($items)) {
1650 return;
1651 }
1652
1653 // Build a map of old IDs to new IDs for parent-child relationships
1654 $id_map = array();
1655
1656 foreach ($items as $item) {
1657 $menu_item_data = array(
1658 'menu-item-title' => $item['title'] ?? $item['post_title'] ?? '',
1659 'menu-item-url' => $item['url'] ?? '#',
1660 'menu-item-status' => 'publish',
1661 'menu-item-parent-id' => isset($item['menu_item_parent']) && $item['menu_item_parent'] != '0' ?
1662 (isset($id_map[$item['menu_item_parent']]) ? $id_map[$item['menu_item_parent']] : 0) :
1663 0,
1664 'menu-item-type' => $item['type'] ?? 'custom',
1665 );
1666
1667 // Handle different menu item types
1668 if ($item['type'] === 'post_type') {
1669 $object_type = $item['object'] ?? 'page';
1670 $post = null;
1671
1672 // First try to find by imported page mapping
1673 if (isset($item['slug'])) {
1674 $mapped_id = $this->get_page_mapping($item['slug']);
1675 if ($mapped_id) {
1676 $post = get_post($mapped_id);
1677 }
1678 }
1679
1680 // If not found in mapping, try to find by slug
1681 if (!$post && isset($item['slug'])) {
1682 $post = get_page_by_path($item['slug'], OBJECT, $object_type);
1683 }
1684
1685 // If still not found, try to find by title as fallback
1686 if (!$post) {
1687 $post_title = $item['title'] ?? $item['post_title'] ?? '';
1688 // Remove any prefix like "Flowerry – " from the title
1689 $post_title = preg_replace('/^[^–]+–\s*/', '', $post_title);
1690
1691 $posts = get_posts(array(
1692 'post_type' => $object_type,
1693 'title' => $post_title,
1694 'post_status' => 'publish',
1695 'numberposts' => 1
1696 ));
1697 $post = !empty($posts) ? $posts[0] : null;
1698
1699 // Also try with the full title
1700 if (!$post) {
1701 $posts = get_posts(array(
1702 'post_type' => $object_type,
1703 's' => $item['title'] ?? $item['post_title'] ?? '',
1704 'post_status' => 'publish',
1705 'numberposts' => 1
1706 ));
1707 $post = !empty($posts) ? $posts[0] : null;
1708 }
1709 }
1710
1711 if ($post) {
1712 $menu_item_data['menu-item-object-id'] = $post->ID;
1713 $menu_item_data['menu-item-object'] = $object_type;
1714 // Update URL to the actual post URL
1715 $menu_item_data['menu-item-url'] = get_permalink($post->ID);
1716 } else {
1717 // Don't create menu items for pages that don't exist
1718 continue;
1719 }
1720 } elseif ($item['type'] === 'taxonomy') {
1721 // Handle taxonomy items (categories, tags, etc.)
1722 $taxonomy = $item['object'] ?? 'category';
1723
1724 if (isset($item['slug'])) {
1725 $term = get_term_by('slug', $item['slug'], $taxonomy);
1726 } else {
1727 // Try to find by name as fallback
1728 $term = get_term_by('name', $item['title'] ?? '', $taxonomy);
1729 }
1730
1731 if ($term) {
1732 $menu_item_data['menu-item-object-id'] = $term->term_id;
1733 $menu_item_data['menu-item-object'] = $taxonomy;
1734 }
1735 } elseif ($item['type'] === 'custom') {
1736 // Custom links don't need object IDs
1737 $menu_item_data['menu-item-url'] = $item['url'] ?? '#';
1738 }
1739
1740 $new_item_id = wp_update_nav_menu_item($menu_id, 0, $menu_item_data);
1741
1742 // Store the ID mapping for parent-child relationships
1743 if (!is_wp_error($new_item_id) && isset($item['ID'])) {
1744 $id_map[$item['ID']] = $new_item_id;
1745 }
1746 }
1747 }
1748
1749 /**
1750 * Import demo settings (customizer and site options)
1751 */
1752 private function import_demo_settings() {
1753 // Check if settings already imported
1754 $settings_imported = get_option('jltma_demo_settings_imported');
1755 if ($settings_imported) {
1756 return true;
1757 }
1758
1759 // Import customizer settings
1760 $this->import_customizer_settings();
1761
1762 // Import site options
1763 $this->import_site_options();
1764
1765 update_option('jltma_demo_settings_imported', true);
1766 return true;
1767 }
1768
1769 /**
1770 * Import customizer settings
1771 */
1772 private function import_customizer_settings() {
1773 $customizer_file = $this->demo_path . '/settings/customizer.json';
1774
1775 if (!file_exists($customizer_file)) {
1776 return false;
1777 }
1778
1779 $json_content = file_get_contents($customizer_file);
1780 $customizer_data = json_decode($json_content, true);
1781
1782 if (!$customizer_data) {
1783 return false;
1784 }
1785
1786 // Process nav_menu_locations to map slugs to new IDs
1787 if (isset($customizer_data['nav_menu_locations']) && is_array($customizer_data['nav_menu_locations'])) {
1788 $new_locations = array();
1789 foreach ($customizer_data['nav_menu_locations'] as $location => $menu_slug) {
1790 // Find the menu by slug
1791 $menu = get_term_by('slug', $menu_slug, 'nav_menu');
1792 if ($menu) {
1793 $new_locations[$location] = $menu->term_id;
1794 }
1795 }
1796 set_theme_mod('nav_menu_locations', $new_locations);
1797 unset($customizer_data['nav_menu_locations']);
1798 }
1799
1800 // Process custom_logo if it's a filename
1801 if (isset($customizer_data['custom_logo'])) {
1802 $logo_filename = $customizer_data['custom_logo'];
1803 // Import the logo image and get the attachment ID
1804 $attachment_id = $this->import_demo_image($logo_filename);
1805 if ($attachment_id && !is_wp_error($attachment_id)) {
1806 set_theme_mod('custom_logo', $attachment_id);
1807 }
1808 unset($customizer_data['custom_logo']);
1809 }
1810
1811 // Process custom CSS post slug
1812 if (isset($customizer_data['custom_css_post_slug'])) {
1813 $css_post_slug = $customizer_data['custom_css_post_slug'];
1814 // Find the custom CSS post by slug
1815 $css_post = get_page_by_path($css_post_slug, OBJECT, 'custom_css');
1816 if ($css_post) {
1817 // Store the ID in a way WordPress expects
1818 // Note: WordPress typically handles custom CSS differently, but we'll store for reference
1819 set_theme_mod('custom_css_post_id', $css_post->ID);
1820 }
1821 unset($customizer_data['custom_css_post_slug']);
1822 }
1823
1824 // Process any image fields that might be filenames
1825 foreach ($customizer_data as $option_name => &$option_value) {
1826 // Skip certain sensitive options
1827 if (in_array($option_name, array('admin_email', 'users_can_register'))) {
1828 continue;
1829 }
1830
1831 // Check if this looks like an image filename
1832 if (is_string($option_value) && preg_match('/\.(jpg|jpeg|png|gif|svg)$/i', $option_value)) {
1833 // Try to import as image
1834 $attachment_id = $this->import_demo_image($option_value);
1835 if ($attachment_id && !is_wp_error($attachment_id)) {
1836 $option_value = $attachment_id;
1837 }
1838 }
1839
1840 set_theme_mod($option_name, $option_value);
1841 }
1842
1843 return true;
1844 }
1845
1846 /**
1847 * Import site options
1848 */
1849 private function import_site_options() {
1850 $options_file = $this->demo_path . '/settings/site_options.json';
1851
1852 if (!file_exists($options_file)) {
1853 return false;
1854 }
1855
1856 $json_content = file_get_contents($options_file);
1857 $options_data = json_decode($json_content, true);
1858
1859 if (!$options_data) {
1860 return false;
1861 }
1862
1863 // Handle variation-specific landing and blog pages
1864 // Check for slug-based fields first (new format)
1865 if (isset($options_data['page_on_front_slug']) && !empty($options_data['page_on_front_slug'])) {
1866 // First check page mappings from import
1867 $mapped_id = $this->get_page_mapping($options_data['page_on_front_slug']);
1868 if ($mapped_id) {
1869 $landing_page = get_post($mapped_id);
1870 } else {
1871 // Fall back to finding by slug
1872 $landing_page = get_page_by_path($options_data['page_on_front_slug']);
1873 }
1874
1875 if ($landing_page) {
1876 update_option('page_on_front', $landing_page->ID);
1877 update_option('show_on_front', 'page');
1878 }
1879 }
1880 // Legacy support for old format
1881 elseif (isset($options_data['landing_page']) && !empty($options_data['landing_page'])) {
1882 $landing_page = get_page_by_path($options_data['landing_page']);
1883 if ($landing_page) {
1884 update_option('page_on_front', $landing_page->ID);
1885 update_option('show_on_front', 'page');
1886 }
1887 }
1888
1889 // Check for blog page slug (new format)
1890 if (isset($options_data['page_for_posts_slug']) && !empty($options_data['page_for_posts_slug'])) {
1891 // First check page mappings from import
1892 $mapped_id = $this->get_page_mapping($options_data['page_for_posts_slug']);
1893 if ($mapped_id) {
1894 $blog_page = get_post($mapped_id);
1895 } else {
1896 // Fall back to finding by slug
1897 $blog_page = get_page_by_path($options_data['page_for_posts_slug']);
1898 }
1899
1900 if ($blog_page) {
1901 update_option('page_for_posts', $blog_page->ID);
1902 }
1903 }
1904 // Legacy support for old format
1905 elseif (isset($options_data['blog_page']) && !empty($options_data['blog_page'])) {
1906 $blog_page = get_page_by_path($options_data['blog_page']);
1907 if ($blog_page) {
1908 update_option('page_for_posts', $blog_page->ID);
1909 }
1910 }
1911
1912 // Set show_on_front if specified
1913 if (isset($options_data['show_on_front'])) {
1914 update_option('show_on_front', $options_data['show_on_front']);
1915 }
1916
1917 // Handle posts per page
1918 if (isset($options_data['posts_per_page'])) {
1919 update_option('posts_per_page', intval($options_data['posts_per_page']));
1920 }
1921
1922 // Process other options
1923 foreach ($options_data as $option_name => $option_value) {
1924 // Skip certain critical options and already processed ones
1925 $skip_options = array(
1926 'siteurl',
1927 'home',
1928 'admin_email',
1929 'users_can_register',
1930 'default_role',
1931 'landing_page',
1932 'blog_page',
1933 'posts_per_page'
1934 );
1935
1936 if (in_array($option_name, $skip_options)) {
1937 continue;
1938 }
1939
1940 update_option($option_name, $option_value);
1941 }
1942
1943 return true;
1944 }
1945
1946 }
1947
1948 // Initialize
1949 Demo_Importer::get_instance();