PluginProbe
Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets / 4.5.6
Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets v4.5.6
4.5.6 4.5.5 4.5.4 4.2.1 4.2.2 4.2.3 4.5.0 4.5.2 4.5.3 4.2.0 4.1.18 4.1.17 4.1.16 4.1.15 4.1.14 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.0.9 4.1.0 4.1.1 4.1.2 All 148 releases
ultimate-post-kit / includes / setup-wizard / init.php

init.php in Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets 4.5.6, at includes/setup-wizard/init.php

788 lines 26.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace UltimatePostKit\Includes;
4
5 if ( ! defined( 'ABSPATH' ) ) {
6 exit;
7 }
8
9 // Load the Remote Data Handler
10 require_once __DIR__ . '/class-remote-data-handler.php';
11
12 use UltimatePostKit\Admin\ModuleService;
13 use Elementor\Plugin;
14 /**
15 * Overwrite the feedback method in the WP_Upgrader_Skin
16 * to suppress the normal feedback.
17 */
18
19 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
20
21 class Quiet_Upgrader_Skin extends \WP_Upgrader_Skin {
22 /*
23 * Suppress normal upgrader feedback / output
24 */
25 public function feedback( $string, ...$args ) {
26 /* no output */
27 }
28 }
29
30
31 class Setup_Wizard {
32
33 // Singleton instance
34 private static $instance = null;
35
36 // Constructor
37 private function __construct() {
38 $this->init_hooks();
39 }
40
41 // Get instance
42 public static function get_instance() {
43 if ( self::$instance == null ) {
44 self::$instance = new self();
45 }
46 return self::$instance;
47 }
48
49 /**
50 * Newsletter list endpoint the welcome step's opt-in posts to.
51 */
52 const SUBSCRIBE_ENDPOINT = 'https://marketing.sigmative.com/newsletter/rui/lists/6a9943aacbe70/embedded-form-subscribe';
53
54 /**
55 * Customer identifier required by the newsletter endpoint.
56 */
57 const SUBSCRIBE_CUSTOMER_UID = '6a93d39ce0ebd';
58
59 // Initialize hooks
60 private function init_hooks() {
61 add_action( 'wp_ajax_ultimate_post_kit_setup_wizard_install_plugins', array( $this, 'install_plugins' ) );
62 add_action( 'wp_ajax_ultimate_post_kit_setup_wizard_subscribe', array( $this, 'ajax_subscribe' ) );
63 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
64 add_action( 'admin_init', array( $this, 'activate_default_widgets' ) );
65 add_action( 'admin_init', array( $this, 'maybe_display_setup_wizard' ) );
66 add_action( 'admin_init', array( $this, 'check_manual_wizard_request' ) );
67
68 // NOTE: WordPress manages plugin/translation updates. Do not add filters
69 // that interfere with the built-in update pipeline (wp.org Guideline).
70 }
71
72 // Check for manual wizard requests
73 public function check_manual_wizard_request() {
74 // This runs on admin_init, which also fires on admin-ajax.php before any
75 // authentication, and on every admin screen for every logged-in role. The setup
76 // wizard is an administrator-only flow, so gate it explicitly.
77 if ( wp_doing_ajax() || ! current_user_can( 'manage_options' ) ) {
78 return;
79 }
80
81 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only check of a GET flag to decide whether to render the setup wizard screen, no form data processed.
82 $is_setup_wizard_request = isset($_GET['upk_setup_wizard']) && $_GET['upk_setup_wizard'] === 'show';
83
84 if ( $is_setup_wizard_request ) {
85 // Use the same approach as first activation - completely override the page
86 add_action('admin_head', function() {
87 ?>
88 <style>
89 html, body {
90 height: 100%;
91 margin: 0;
92 padding: 0;
93 overflow: hidden;
94 }
95 #wpwrap, #wpcontent, #wpbody, #wpbody-content {
96 height: 100%;
97 padding: 0;
98 margin: 0;
99 }
100 #adminmenumain, #wpadminbar {
101 display: none;
102 }
103 </style>
104 <script>
105 jQuery(document).ready(function($) {
106 $('body').addClass('bdt-setup-wizard-active');
107 });
108 </script>
109 <?php
110
111 // Display setup wizard using the same method as first activation
112 $this->display_page();
113 });
114 }
115 }
116
117 // Display wizard in fullscreen mode
118 public function display_wizard_fullscreen() {
119 ?>
120 <style>
121 html, body {
122 height: 100%;
123 margin: 0;
124 padding: 0;
125 overflow: hidden;
126 }
127 #wpwrap, #wpcontent, #wpbody, #wpbody-content {
128 height: 100%;
129 padding: 0;
130 margin: 0;
131 }
132 #adminmenumain, #wpadminbar {
133 display: none;
134 }
135 </style>
136 <?php
137 // Directly output the wizard content
138 add_action('admin_footer', function() {
139 echo '<div id="upk-setup-wizard-container">';
140 $this->display_page();
141 echo '</div>';
142 ?>
143 <script>
144 jQuery(document).ready(function($) {
145 $('body').addClass('bdt-setup-wizard-active');
146 // Hide all other content and show only our wizard
147 $('#wpbody-content').html($('#upk-setup-wizard-container').html());
148 $('#upk-setup-wizard-container').remove();
149 });
150 </script>
151 <?php
152 }, 999);
153 }
154
155 // Get wizard HTML content
156 public function get_wizard_html() {
157 ob_start();
158 ?>
159 <div class="bdt-setup-wizard-overlay upk-setup-wizard ">
160 <div class="bdt-setup-wizard content-loaded">
161 <?php
162 require_once plugin_dir_path( BDTUPK__FILE__ ) . 'includes/setup-wizard/views/render.php';
163 ?>
164 </div>
165 </div>
166 <?php
167 return ob_get_clean();
168 }
169
170 // Check if this is first activation and display setup wizard if needed
171 public function maybe_display_setup_wizard() {
172 // This runs on admin_init, which also fires on admin-ajax.php before any
173 // authentication, and on every admin screen for every logged-in role. The setup
174 // wizard is an administrator-only flow, so gate it explicitly.
175 if ( wp_doing_ajax() || ! current_user_can( 'manage_options' ) ) {
176 return;
177 }
178
179 // Only check for first activation here
180 if ( get_option( 'bdtupk_setup_wizard_completed' ) === false ) {
181 // Set the flag so it doesn't run again
182 update_option( 'bdtupk_setup_wizard_completed', true );
183
184 // Add a header to ensure proper full-page display
185 add_action('admin_head', function() {
186 ?>
187 <style>
188 html, body {
189 height: 100%;
190 margin: 0;
191 padding: 0;
192 overflow: hidden;
193 }
194 #wpwrap, #wpcontent, #wpbody, #wpbody-content {
195 height: 100%;
196 padding: 0;
197 margin: 0;
198 }
199 #adminmenumain, #wpadminbar {
200 display: none;
201 }
202 </style>
203 <script>
204 jQuery(document).ready(function($) {
205 $('body').addClass('bdt-setup-wizard-active');
206 });
207 </script>
208 <?php
209
210 // Display setup wizard
211 $this->display_page();
212 });
213 }
214 }
215
216 // Keep the admin_menu method for reference but not hooked
217 public function admin_menu() {
218 add_submenu_page(
219 'ultimate_post_kit_options',
220 esc_html__( 'Setup Wizard', 'ultimate-post-kit' ),
221 esc_html__( 'Setup Wizard', 'ultimate-post-kit' ),
222 'manage_options',
223 'ultimate-post-kit-setup-wizard',
224 array( $this, 'display_page' )
225 );
226 }
227
228 public function display_page() {
229 ?>
230 <div class="bdt-setup-wizard-overlay upk-setup-wizard">
231 <div class="bdt-setup-wizard content-loaded">
232 <?php
233 require_once plugin_dir_path( BDTUPK__FILE__ ) . 'includes/setup-wizard/views/render.php';
234 ?>
235 </div>
236 </div>
237 <?php
238 }
239
240 // Enqueue necessary scripts
241 public function enqueue_scripts() {
242
243 // Loaded on admin_enqueue_scripts for every admin screen and every role. The
244 // wizard's assets and its nonce have no business outside an administrator's
245 // wizard/settings screen.
246 if ( ! current_user_can( 'manage_options' ) ) {
247 return;
248 }
249
250
251 $direction_suffix = is_rtl() ? '.rtl' : '';
252
253 wp_enqueue_style('bdt-uikit', BDTUPK_ADMIN_ASSETS_URL . 'css/bdt-uikit' . $direction_suffix . '.css', [], '3.17.0');
254 wp_enqueue_script('bdt-uikit', BDTUPK_ADMIN_ASSETS_URL . 'js/bdt-uikit.min.js', ['jquery'], '3.17.0', true);
255
256 wp_register_script( 'upk-setup-wizard', plugins_url( 'assets/js/setup-wizard.js', __FILE__ ), array( 'jquery' ), '1.0.0', true );
257 wp_register_style( 'upk-setup-wizard', plugins_url( 'assets/css/setup-wizard.css', __FILE__ ), array(), '1.0.0' );
258
259 wp_enqueue_script( 'upk-setup-wizard' );
260 wp_enqueue_style( 'upk-setup-wizard' );
261
262 wp_localize_script(
263 'upk-setup-wizard',
264 'UPK_SetupWizard',
265 array(
266 'ajax_url' => admin_url( 'admin-ajax.php' ),
267 'nonce' => wp_create_nonce( 'ultimate_post_kit_setup_wizard_nonce' ),
268 'is_fullscreen' => true
269 )
270 );
271 }
272
273 public static function get_widget_map() {
274 $arr_obj = ModuleService::get_widget_settings(
275 function ( $settings ) {
276 $core_widgets = $settings['settings_fields']['ultimate_post_kit_active_modules'];
277 return $core_widgets;
278 }
279 );
280 return $arr_obj;
281 }
282
283 /**
284 * Handle the newsletter opt-in on the welcome step.
285 *
286 * Opt-in only: nothing is sent unless the administrator ticked the box,
287 * which is unticked by default. The choice is recorded either way so the
288 * wizard can show it again on a re-run. Runs server side so the
289 * cross-origin POST is not subject to CORS.
290 */
291 public function ajax_subscribe() {
292 check_ajax_referer( 'ultimate_post_kit_setup_wizard_nonce', 'nonce' );
293
294 if ( ! current_user_can( 'manage_options' ) ) {
295 wp_send_json_error( array( 'message' => esc_html__( 'Unauthorized', 'ultimate-post-kit' ) ) );
296 }
297
298 // Record the choice first, whichever way it went.
299 $consent = isset( $_POST['consent'] ) && 'yes' === sanitize_text_field( wp_unslash( $_POST['consent'] ) );
300
301 update_option( 'bdtupk_subscribe_optin', $consent ? 'yes' : 'no' );
302
303 if ( ! $consent ) {
304 // No opt-in: the choice is stored and nothing leaves the site.
305 wp_send_json_success(
306 array(
307 'subscribed' => false,
308 'message' => esc_html__( 'Preferences saved.', 'ultimate-post-kit' ),
309 )
310 );
311 }
312
313 // Keep the raw value: sanitize_email() flattens anything malformed to an
314 // empty string, which would otherwise be indistinguishable from "left blank".
315 $raw_email = isset( $_POST['email'] ) ? sanitize_text_field( wp_unslash( $_POST['email'] ) ) : '';
316 $email = sanitize_email( $raw_email );
317
318 if ( '' === trim( $raw_email ) ) {
319 wp_send_json_success(
320 array(
321 'subscribed' => false,
322 'message' => esc_html__( 'Preferences saved.', 'ultimate-post-kit' ),
323 )
324 );
325 }
326
327 if ( ! is_email( $email ) ) {
328 wp_send_json_error( array( 'message' => esc_html__( 'Please enter a valid email address.', 'ultimate-post-kit' ) ) );
329 }
330
331 // Never subscribe the same address twice from this site.
332 if ( get_option( 'bdtupk_subscribed_email' ) === $email ) {
333 wp_send_json_success(
334 array(
335 'subscribed' => true,
336 'message' => esc_html__( 'You are already subscribed.', 'ultimate-post-kit' ),
337 )
338 );
339 }
340
341 $current_user = wp_get_current_user();
342
343 $body = array(
344 'customer_uid' => apply_filters( 'bdtupk/setup_wizard/subscribe_customer_uid', self::SUBSCRIBE_CUSTOMER_UID ),
345 'EMAIL' => $email,
346 'FIRST_NAME' => $current_user ? $current_user->first_name : '',
347 'LAST_NAME' => $current_user ? $current_user->last_name : '',
348 );
349
350 $response = wp_safe_remote_post(
351 apply_filters( 'bdtupk/setup_wizard/subscribe_url', self::SUBSCRIBE_ENDPOINT ),
352 array(
353 'timeout' => 15,
354 'body' => apply_filters( 'bdtupk/setup_wizard/subscribe_body', $body, $email ),
355 'headers' => array( 'Accept' => '*/*' ),
356 'sslverify' => true,
357 )
358 );
359
360 if ( is_wp_error( $response ) ) {
361 wp_send_json_error( array( 'message' => esc_html__( 'Could not reach the subscription service. Please try again later.', 'ultimate-post-kit' ) ) );
362 }
363
364 $code = wp_remote_retrieve_response_code( $response );
365
366 if ( $code < 200 || $code >= 400 ) {
367 wp_send_json_error( array( 'message' => esc_html__( 'The subscription service rejected the request.', 'ultimate-post-kit' ) ) );
368 }
369
370 update_option( 'bdtupk_subscribed_email', $email );
371
372 wp_send_json_success(
373 array(
374 'subscribed' => true,
375 'message' => esc_html__( 'Thanks for subscribing!', 'ultimate-post-kit' ),
376 )
377 );
378 }
379
380 // Install plugins
381 public function install_plugins() {
382 check_ajax_referer( 'ultimate_post_kit_setup_wizard_nonce', 'nonce' );
383
384 $plugin_slugs = isset( $_POST['plugins'] ) ? map_deep( wp_unslash( $_POST['plugins'] ), 'sanitize_text_field' ) : array();
385
386 if ( empty( $plugin_slugs ) || ! is_array( $plugin_slugs ) ) {
387 wp_send_json_error( array( 'message' => 'Invalid plugins array' ) );
388 }
389
390 if ( ! current_user_can( 'install_plugins' ) ) {
391 wp_send_json_error( array( 'message' => 'Unauthorized' ) );
392 }
393
394 include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
395 include_once ABSPATH . 'wp-admin/includes/plugin-install.php';
396 include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader-skin.php';
397 include_once ABSPATH . 'wp-admin/includes/plugin.php';
398
399 // Replace new \Plugin_Installer_Skin with new Quiet_Upgrader_Skin when output needs to be suppressed.
400 $skin = new Quiet_Upgrader_Skin();
401 // $skin = new \Plugin_Installer_Skin( array( 'api' => $api ) );
402 $upgrader = new \Plugin_Upgrader( $skin );
403
404 // $upgrader = new \Plugin_Upgrader();
405
406 $installedPlugins = get_plugins();
407 $results = array();
408
409 foreach ( $plugin_slugs as $plugin_slug ) {
410 // skip when the plugin is already active
411 if (is_plugin_active($plugin_slug)) {
412 $results[] = array(
413 'slug' => $plugin_slug,
414 'success' => true,
415 'message' => 'Installed and activated successfully',
416 );
417 continue;
418 }
419
420 // Download the plugin if the plugin is not installed
421 if (!isset($installedPlugins[$plugin_slug])) {
422 $slug = explode('/', $plugin_slug)[0];
423 $api = plugins_api( 'plugin_information', array( 'slug' => $slug ) );
424
425 if ( is_wp_error( $api ) ) {
426 $results[] = array(
427 'slug' => $plugin_slug,
428 'success' => false,
429 'message' => $api->get_error_message(),
430 );
431 continue;
432 }
433
434 $result = $upgrader->install( $api->download_link );
435 if ( is_wp_error( $result ) ) {
436 $results[] = array(
437 'slug' => $plugin_slug,
438 'success' => false,
439 'message' => $result->get_error_message(),
440 );
441 continue;
442 }
443 }
444
445 // Activating a plugin is a separate capability from installing one, so it is
446 // checked on its own rather than being implied by 'install_plugins' above.
447 if ( ! current_user_can( 'activate_plugins' ) ) {
448 $results[] = array(
449 'slug' => $plugin_slug,
450 'success' => false,
451 'message' => esc_html__( 'You do not have permission to activate plugins on this site.', 'ultimate-post-kit' ),
452 );
453 continue;
454 }
455
456 // active the plugin
457 if ( is_plugin_inactive( $plugin_slug ) ) {
458 $activation_result = activate_plugin( $plugin_slug );
459 if ( is_wp_error( $activation_result ) ) {
460 $results[] = array(
461 'slug' => $plugin_slug,
462 'success' => false,
463 'message' => $activation_result->get_error_message(),
464 );
465 continue;
466 }
467
468 $results[] = array(
469 'slug' => $plugin_slug,
470 'success' => true,
471 'message' => 'Installed and activated successfully',
472 );
473 }
474 }
475
476 ob_clean();
477 wp_send_json_success( array( 'results' => $results ) );
478 wp_die();
479 }
480
481 /**
482 * Get the main plugin file path for a given slug.
483 *
484 * @param string $slug Plugin slug.
485 * @return string|false Plugin file path or false if not found.
486 */
487 private function get_plugin_file( $slug ) {
488 $plugins = get_plugins();
489
490 foreach ( $plugins as $file => $plugin ) {
491 if ( strpos( $file, $slug ) !== false ) {
492 return $file;
493 }
494 }
495
496 return false;
497 }
498
499 /**
500 * Activate default widgets in setup wizard
501 */
502 public function activate_default_widgets() {
503
504 // Also reached anonymously via admin-ajax.php, which fires admin_init before
505 // authentication. Enabling widget modules is an administrator action.
506 if ( wp_doing_ajax() || ! current_user_can( 'manage_options' ) ) {
507 return;
508 }
509
510 // List of widgets to activate by default
511 $default_active_widgets = array(
512 'alex-grid',
513 'alice-grid',
514 'alter-carousel',
515 'banner',
516 'buzz-list',
517 'carbon-slider',
518 'featured-list',
519 'news-ticker',
520 'pholox-slider',
521 'timeline',
522 'category',
523 'social-count',
524 'tag-cloud'
525 );
526
527 // Get current active modules
528 $active_modules = get_option('ultimate_post_kit_active_modules', array());
529
530 // Make sure $active_modules is an array
531 if (!is_array($active_modules)) {
532 $active_modules = array();
533 }
534
535 // Check if active_modules option exists and is not empty
536 // If it's a new installation or option doesn't exist, we'll set our defaults
537 $modified = false;
538
539 foreach ($default_active_widgets as $widget) {
540 // Only set if not already defined (prevents overriding user settings on existing installations)
541 if (!isset($active_modules[$widget])) {
542 $active_modules[$widget] = 'on';
543 $modified = true;
544 }
545 }
546
547 // Update the option if changes were made
548 if ($modified) {
549 update_option('ultimate_post_kit_active_modules', $active_modules);
550 }
551 }
552 }
553
554 // Initialize the Setup Wizard
555 Setup_Wizard::get_instance();
556
557 use Elementor\TemplateLibrary\Source_Local;
558
559 add_action('wp_ajax_ultimate_post_kit_import_elementor_template', function () {
560 check_ajax_referer( 'ultimate_post_kit_setup_wizard_nonce', 'nonce' );
561
562 if ( ! current_user_can( 'manage_options' ) ) {
563 wp_send_json_error( array( 'message' => esc_html__( 'Unauthorized', 'ultimate-post-kit' ) ) );
564 wp_die();
565 }
566
567 $json_url = isset( $_POST['import_url'] ) ? esc_url_raw( wp_unslash( $_POST['import_url'] ) ) : '';
568
569 $response = wp_safe_remote_get($json_url, array(
570 'timeout' => 60,
571 ));
572
573 if (is_wp_error($response)) {
574 wp_send_json_error(['message' => esc_html__('Failed to fetch template from URL.', 'ultimate-post-kit')]);
575 wp_die();
576 }
577
578 $sourceData = wp_remote_retrieve_body($response);
579 $sourceData2 = json_decode($sourceData, true);
580
581 if (!$sourceData2 || !is_array($sourceData2)) {
582 wp_send_json_error(['message' => esc_html__('Failed to fetch template from URL.', 'ultimate-post-kit')]);
583 wp_die();
584 }
585
586 $temp_file = wp_upload_dir()['path'] . '/elementor_import_' . time() . '.json';
587 file_put_contents($temp_file, $sourceData);
588
589 // Initialize Elementor's Template Importer
590 if (!class_exists('\Elementor\TemplateLibrary\Source_Local')) {
591 wp_delete_file($temp_file);
592 wp_send_json_error(['message' => esc_html__('Elementor is not installed or activated!', 'ultimate-post-kit')]);
593 wp_die();
594 }
595
596 $manager = new Source_Local();
597 $templateData = $manager->import_template('elementor_template', $temp_file);
598 wp_delete_file($temp_file); // Delete temp file after import
599
600 if (is_wp_error($templateData) || !is_array($templateData) || empty($templateData[0]['template_id'])) {
601 wp_send_json_error(['message' => esc_html__('Failed to import template!', 'ultimate-post-kit')]);
602 wp_die();
603 }
604
605 $template_id = $templateData[0]['template_id'];
606 $metaData = get_post_meta($template_id);
607
608 $page_title = isset($_POST['title']) ? sanitize_text_field(wp_unslash($_POST['title'])) : esc_html__("No Title", 'ultimate-post-kit');
609
610 // Validate Elementor Data
611 if (!isset($metaData['_elementor_data'][0])) {
612 wp_send_json_error(['message' => esc_html__('Elementor data not found in template.', 'ultimate-post-kit')]);
613 wp_die();
614 }
615
616 $_elementor_data = wp_slash($metaData['_elementor_data'][0]);
617
618 // Create New Page
619 $new_post_id = wp_insert_post([
620 'post_type' => 'page',
621 'post_status' => empty($page_title) ? 'draft' : 'publish',
622 'post_title' => $page_title,
623 'post_content' => '',
624 ]);
625
626 if (is_wp_error($new_post_id)) {
627 wp_send_json_error(['message' => esc_html__('Failed to create page!', 'ultimate-post-kit')]);
628 wp_die();
629 }
630
631 // Assign Elementor Template Data
632 update_post_meta($new_post_id, '_elementor_data', $_elementor_data);
633
634 // Import Page Settings if available
635 if (isset($metaData['_elementor_page_settings'][0])) {
636 $_elementor_page_settings = is_serialized($metaData['_elementor_page_settings'][0])
637 ? unserialize($metaData['_elementor_page_settings'][0], ['allowed_classes' => false])
638 : $metaData['_elementor_page_settings'][0];
639 update_post_meta($new_post_id, '_elementor_page_settings', $_elementor_page_settings);
640 }
641
642 update_post_meta($new_post_id, '_elementor_template_type', $sourceData2['type'] ?? '');
643 update_post_meta($new_post_id, '_elementor_edit_mode', 'builder');
644 // update_post_meta($new_post_id, '_wp_page_template', !empty($pageTemplate) ? $pageTemplate : 'elementor_header_footer');
645
646 wp_send_json_success([
647 'message' => esc_html__('The template was imported successfully.', 'ultimate-post-kit'),
648 'ids' => $new_post_id,
649 'edit_link' => admin_url('post.php?post=' . $new_post_id . '&action=elementor'),
650 ]);
651 }
652 );
653
654
655 add_action('wp_ajax_ultimate_post_kit_import_elementor_bundle_template', function () {
656 check_ajax_referer('ultimate_post_kit_setup_wizard_nonce', 'nonce');
657
658 if ( ! current_user_can( 'manage_options' ) ) {
659 wp_send_json_error( array( 'message' => esc_html__( 'Unauthorized', 'ultimate-post-kit' ) ) );
660 wp_die();
661 }
662
663 $file_url = isset($_POST['import_url']) ? esc_url_raw(wp_unslash($_POST['import_url'])) : '';
664
665 if (!filter_var($file_url, FILTER_VALIDATE_URL) || 0 !== strpos($file_url, 'http')) {
666 wp_send_json_error(['message' => esc_html__('Invalid import URL', 'ultimate-post-kit')]);
667 }
668
669 $remote_zip_request = wp_safe_remote_get($file_url, array(
670 'timeout' => 60,
671 ));
672
673 if (is_wp_error($remote_zip_request)) {
674 wp_send_json_error(['message' => esc_html__('Failed to fetch template from URL.', 'ultimate-post-kit')]);
675 }
676
677
678 if (200 !== $remote_zip_request['response']['code']) {
679 wp_send_json_error(['message' => esc_html__('Failed to fetch template from URL.', 'ultimate-post-kit')]);
680 }
681
682 $kit_zip_path = Plugin::$instance->uploads_manager->create_temp_file($remote_zip_request['body'], 'kit.zip');
683
684 $app = Plugin::$instance->app;
685 if (!$app) {
686 wp_send_json_error(['message' => esc_html__('Elementor app not available', 'ultimate-post-kit')]);
687 }
688
689 $import_export_module = $app->get_component('import-export');
690
691 try {
692 $result = $import_export_module->upload_kit($kit_zip_path, 'local');
693 $manifest = $result['manifest'] ?? [];
694 $plugins = $manifest['plugins'];
695
696 $missingPlugins = [];
697 foreach ($plugins as $plugin) {
698 $pluginSlug = $plugin['plugin'].".php";
699 if (is_plugin_inactive($pluginSlug)) {
700 $missingPlugins[] = $plugin;
701 }
702 }
703
704 if (count($missingPlugins)) {
705 wp_send_json_error([
706 'plugins' => $missingPlugins,
707 'message' => esc_html__('Missing plugins', 'ultimate-post-kit'),
708 ]);
709 }
710
711 $tmp_folder_id = $result['session'];
712 $includes = [];
713 $selectedCustomPostTypes = [];
714
715 if (isset($manifest['templates'])) {
716 $includes[] = 'templates';
717 }
718
719 if (isset($manifest['content'])) {
720 $includes[] = 'content';
721 }
722
723 if (isset($manifest['site-settings'])) {
724 $includes[] = 'settings';
725 }
726
727 if (isset($manifest['custom-post-type-title'])) {
728 $selectedCustomPostTypes = array_keys($manifest['custom-post-type-title']);
729 }
730
731 $settings = [
732 'id' => '',
733 'session' => $tmp_folder_id,
734 'include' => $includes,
735 'overrideConditions' => [],
736 'selectedCustomPostTypes' => $selectedCustomPostTypes,
737 ];
738
739 $import = $import_export_module->import_kit($tmp_folder_id, $settings, true);
740
741 // Deliberately NOT calling
742 // Plugin::$instance->uploads_manager->enable_unfiltered_files_upload() here.
743 // That permanently sets Elementor's `elementor_unfiltered_files_upload` option,
744 // which Elementor itself surfaces behind an explicit security warning and an
745 // opt-in confirmation. Importing a template must not silently relax another
746 // plugin's upload filtering for the whole site.
747
748 wp_send_json_success($import);
749 } catch (\Throwable $e) {
750 wp_send_json_error(['message' => esc_html__('Import failed: ', 'ultimate-post-kit') . esc_html($e->getMessage())]);
751 }
752 });
753
754 add_action('wp_ajax_ultimate_post_kit_import_elementor_bundle_runner_template', function () {
755 check_ajax_referer('ultimate_post_kit_setup_wizard_nonce', 'nonce');
756
757 if ( ! current_user_can( 'manage_options' ) ) {
758 wp_send_json_error( array( 'message' => esc_html__( 'Unauthorized', 'ultimate-post-kit' ) ) );
759 wp_die();
760 }
761
762 $runner = isset($_POST['runner']) ? sanitize_text_field(wp_unslash($_POST['runner'])) : '';
763 $sessionId = isset($_POST['sessionId']) ? sanitize_text_field(wp_unslash($_POST['sessionId'])) : '';
764
765 if (!$runner || !$sessionId) {
766 wp_send_json_error(['message' => esc_html__('Required Param Is Missing.', 'ultimate-post-kit')]);
767 }
768
769 $app = Plugin::$instance->app;
770 if (!$app) {
771 wp_send_json_error(['message' => esc_html__('Elementor app not available.', 'ultimate-post-kit')]);
772 }
773
774 try {
775 // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged, WordPress.PHP.IniSet.max_execution_time_Disallowed -- raise the limit only for this admin-triggered template import, which can exceed the default.
776 @ini_set('max_execution_time', 60 * 5);
777
778 $import_export_module = $app->get_component('import-export');
779 $import = $import_export_module->import_kit_by_runner($sessionId, $runner);
780
781 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- hooking into Elementor's own action, not a plugin-defined hook.
782 do_action('elementor/import-export/import-kit/runner/after-run', $import);
783 wp_send_json_success($import);
784 } catch (\Throwable $throwable) {
785 wp_send_json_error(['message' => $throwable->getMessage()]);
786 }
787 });
788