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
← All changes | includes/setup-wizard/init.php +200 -31 4.0.9 → 4.5.6 View file →
@@ -5,8 +5,11 @@
5 5 if ( ! defined( 'ABSPATH' ) ) {
6 6 exit;
7 7 }
8 8
9 +// Load the Remote Data Handler
10 +require_once __DIR__ . '/class-remote-data-handler.php';
11 +
9 12 use UltimatePostKit\Admin\ModuleService;
10 13 use Elementor\Plugin;
11 14 /**
12 15 * Overwrite the feedback method in the WP_Upgrader_Skin
@@ -14,12 +17,8 @@
14 17 */
15 18
16 19 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
17 20
18 -// Include our plugin API fetcher and cache manager
19 -require_once __DIR__ . '/class-plugin-api-fetcher.php';
20 -require_once __DIR__ . '/class-plugin-cache-manager.php';
21 -
22 21 class Quiet_Upgrader_Skin extends \WP_Upgrader_Skin {
23 22 /*
24 23 * Suppress normal upgrader feedback / output
25 24 */
@@ -46,23 +45,41 @@
46 45 }
47 46 return self::$instance;
48 47 }
49 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 +
50 59 // Initialize hooks
51 60 private function init_hooks() {
52 - add_action( 'wp_ajax_setup_wizard_install_plugins', array( $this, 'install_plugins' ) );
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' ) );
53 63 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
54 64 add_action( 'admin_init', array( $this, 'activate_default_widgets' ) );
55 65 add_action( 'admin_init', array( $this, 'maybe_display_setup_wizard' ) );
56 66 add_action( 'admin_init', array( $this, 'check_manual_wizard_request' ) );
57 67
58 - if ( function_exists( 'add_filter' ) ) {
59 - add_filter( 'auto_update_translation', '__return_false' );
60 - }
68 + // NOTE: WordPress manages plugin/translation updates. Do not add filters
69 + // that interfere with the built-in update pipeline (wp.org Guideline).
61 70 }
62 71
63 72 // Check for manual wizard requests
64 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.
65 82 $is_setup_wizard_request = isset($_GET['upk_setup_wizard']) && $_GET['upk_setup_wizard'] === 'show';
66 83
67 84 if ( $is_setup_wizard_request ) {
68 85 // Use the same approach as first activation - completely override the page
@@ -151,8 +168,15 @@
151 168 }
152 169
153 170 // Check if this is first activation and display setup wizard if needed
154 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 +
155 179 // Only check for first activation here
156 180 if ( get_option( 'bdtupk_setup_wizard_completed' ) === false ) {
157 181 // Set the flag so it doesn't run again
158 182 update_option( 'bdtupk_setup_wizard_completed', true );
@@ -215,12 +239,20 @@
215 239
216 240 // Enqueue necessary scripts
217 241 public function enqueue_scripts() {
218 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 +
219 251 $direction_suffix = is_rtl() ? '.rtl' : '';
220 252
221 253 wp_enqueue_style('bdt-uikit', BDTUPK_ADMIN_ASSETS_URL . 'css/bdt-uikit' . $direction_suffix . '.css', [], '3.17.0');
222 - wp_enqueue_script('bdt-uikit', BDTUPK_ADMIN_ASSETS_URL . 'js/bdt-uikit.min.js', ['jquery'], '3.17.0');
254 + wp_enqueue_script('bdt-uikit', BDTUPK_ADMIN_ASSETS_URL . 'js/bdt-uikit.min.js', ['jquery'], '3.17.0', true);
223 255
224 256 wp_register_script( 'upk-setup-wizard', plugins_url( 'assets/js/setup-wizard.js', __FILE__ ), array( 'jquery' ), '1.0.0', true );
225 257 wp_register_style( 'upk-setup-wizard', plugins_url( 'assets/css/setup-wizard.css', __FILE__ ), array(), '1.0.0' );
226 258
@@ -228,12 +260,12 @@
228 260 wp_enqueue_style( 'upk-setup-wizard' );
229 261
230 262 wp_localize_script(
231 263 'upk-setup-wizard',
232 - 'BDT_SetupWizard',
264 + 'UPK_SetupWizard',
233 265 array(
234 266 'ajax_url' => admin_url( 'admin-ajax.php' ),
235 - 'nonce' => wp_create_nonce( 'setup_wizard_nonce' ),
267 + 'nonce' => wp_create_nonce( 'ultimate_post_kit_setup_wizard_nonce' ),
236 268 'is_fullscreen' => true
237 269 )
238 270 );
239 271 }
@@ -247,13 +279,110 @@
247 279 );
248 280 return $arr_obj;
249 281 }
250 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 +
251 380 // Install plugins
252 381 public function install_plugins() {
253 - check_ajax_referer( 'setup_wizard_nonce', 'nonce' );
382 + check_ajax_referer( 'ultimate_post_kit_setup_wizard_nonce', 'nonce' );
254 383
255 - $plugin_slugs = isset( $_POST['plugins'] ) ? $_POST['plugins'] : array();
384 + $plugin_slugs = isset( $_POST['plugins'] ) ? map_deep( wp_unslash( $_POST['plugins'] ), 'sanitize_text_field' ) : array();
256 385
257 386 if ( empty( $plugin_slugs ) || ! is_array( $plugin_slugs ) ) {
258 387 wp_send_json_error( array( 'message' => 'Invalid plugins array' ) );
259 388 }
@@ -312,14 +441,25 @@
312 441 continue;
313 442 }
314 443 }
315 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 +
316 456 // active the plugin
317 - if ( is_plugin_inactive($plugin_slug) ) {
457 + if ( is_plugin_inactive( $plugin_slug ) ) {
318 458 $activation_result = activate_plugin( $plugin_slug );
319 459 if ( is_wp_error( $activation_result ) ) {
320 460 $results[] = array(
321 - 'slug' => $slug,
461 + 'slug' => $plugin_slug,
322 462 'success' => false,
323 463 'message' => $activation_result->get_error_message(),
324 464 );
325 465 continue;
@@ -359,8 +499,15 @@
359 499 /**
360 500 * Activate default widgets in setup wizard
361 501 */
362 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 +
363 510 // List of widgets to activate by default
364 511 $default_active_widgets = array(
365 512 'alex-grid',
366 513 'alice-grid',
@@ -408,16 +555,20 @@
408 555 Setup_Wizard::get_instance();
409 556
410 557 use Elementor\TemplateLibrary\Source_Local;
411 558
412 -add_action('wp_ajax_import_elementor_template', function () {
413 - check_ajax_referer( 'setup_wizard_nonce', 'nonce' );
559 +add_action('wp_ajax_ultimate_post_kit_import_elementor_template', function () {
560 + check_ajax_referer( 'ultimate_post_kit_setup_wizard_nonce', 'nonce' );
414 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 +
415 567 $json_url = isset( $_POST['import_url'] ) ? esc_url_raw( wp_unslash( $_POST['import_url'] ) ) : '';
416 568
417 - $response = wp_remote_get($json_url, array(
418 - 'timeout' => 60,
419 - 'sslverify' => false
569 + $response = wp_safe_remote_get($json_url, array(
570 + 'timeout' => 60,
420 571 ));
421 572
422 573 if (is_wp_error($response)) {
423 574 wp_send_json_error(['message' => esc_html__('Failed to fetch template from URL.', 'ultimate-post-kit')]);
@@ -436,9 +587,9 @@
436 587 file_put_contents($temp_file, $sourceData);
437 588
438 589 // Initialize Elementor's Template Importer
439 590 if (!class_exists('\Elementor\TemplateLibrary\Source_Local')) {
440 - unlink($temp_file);
591 + wp_delete_file($temp_file);
441 592 wp_send_json_error(['message' => esc_html__('Elementor is not installed or activated!', 'ultimate-post-kit')]);
442 593 wp_die();
443 594 }
444 595
@@ -443,9 +594,9 @@
443 594 }
444 595
445 596 $manager = new Source_Local();
446 597 $templateData = $manager->import_template('elementor_template', $temp_file);
447 - unlink($temp_file); // Delete temp file after import
598 + wp_delete_file($temp_file); // Delete temp file after import
448 599
449 600 if (is_wp_error($templateData) || !is_array($templateData) || empty($templateData[0]['template_id'])) {
450 601 wp_send_json_error(['message' => esc_html__('Failed to import template!', 'ultimate-post-kit')]);
451 602 wp_die();
@@ -453,9 +604,9 @@
453 604
454 605 $template_id = $templateData[0]['template_id'];
455 606 $metaData = get_post_meta($template_id);
456 607
457 - $page_title = isset($_POST['title']) ? sanitize_text_field($_POST['title']) : esc_html__("No Title", 'ultimate-post-kit');
608 + $page_title = isset($_POST['title']) ? sanitize_text_field(wp_unslash($_POST['title'])) : esc_html__("No Title", 'ultimate-post-kit');
458 609
459 610 // Validate Elementor Data
460 611 if (!isset($metaData['_elementor_data'][0])) {
461 612 wp_send_json_error(['message' => esc_html__('Elementor data not found in template.', 'ultimate-post-kit')]);
@@ -481,9 +632,11 @@
481 632 update_post_meta($new_post_id, '_elementor_data', $_elementor_data);
482 633
483 634 // Import Page Settings if available
484 635 if (isset($metaData['_elementor_page_settings'][0])) {
485 - $_elementor_page_settings = maybe_unserialize($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];
486 639 update_post_meta($new_post_id, '_elementor_page_settings', $_elementor_page_settings);
487 640 }
488 641
489 642 update_post_meta($new_post_id, '_elementor_template_type', $sourceData2['type'] ?? '');
@@ -498,11 +651,16 @@
498 651 }
499 652 );
500 653
501 654
502 -add_action('wp_ajax_import_upk_elementor_bundle_template', function () {
503 - check_ajax_referer('setup_wizard_nonce', 'nonce');
655 +add_action('wp_ajax_ultimate_post_kit_import_elementor_bundle_template', function () {
656 + check_ajax_referer('ultimate_post_kit_setup_wizard_nonce', 'nonce');
504 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 +
505 663 $file_url = isset($_POST['import_url']) ? esc_url_raw(wp_unslash($_POST['import_url'])) : '';
506 664
507 665 if (!filter_var($file_url, FILTER_VALIDATE_URL) || 0 !== strpos($file_url, 'http')) {
508 666 wp_send_json_error(['message' => esc_html__('Invalid import URL', 'ultimate-post-kit')]);
@@ -508,10 +666,9 @@
508 666 wp_send_json_error(['message' => esc_html__('Invalid import URL', 'ultimate-post-kit')]);
509 667 }
510 668
511 669 $remote_zip_request = wp_safe_remote_get($file_url, array(
512 - 'timeout' => 60,
513 - 'sslverify' => false,
670 + 'timeout' => 60,
514 671 ));
515 672
516 673 if (is_wp_error($remote_zip_request)) {
517 674 wp_send_json_error(['message' => esc_html__('Failed to fetch template from URL.', 'ultimate-post-kit')]);
@@ -580,9 +737,14 @@
580 737 ];
581 738
582 739 $import = $import_export_module->import_kit($tmp_folder_id, $settings, true);
583 740
584 - Plugin::$instance->uploads_manager->enable_unfiltered_files_upload();
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.
585 747
586 748 wp_send_json_success($import);
587 749 } catch (\Throwable $e) {
588 750 wp_send_json_error(['message' => esc_html__('Import failed: ', 'ultimate-post-kit') . esc_html($e->getMessage())]);
@@ -588,11 +750,16 @@
588 750 wp_send_json_error(['message' => esc_html__('Import failed: ', 'ultimate-post-kit') . esc_html($e->getMessage())]);
589 751 }
590 752 });
591 753
592 -add_action('wp_ajax_import_upk_elementor_bundle_runner_template', function () {
593 - check_ajax_referer('setup_wizard_nonce', 'nonce');
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');
594 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 +
595 762 $runner = isset($_POST['runner']) ? sanitize_text_field(wp_unslash($_POST['runner'])) : '';
596 763 $sessionId = isset($_POST['sessionId']) ? sanitize_text_field(wp_unslash($_POST['sessionId'])) : '';
597 764
598 765 if (!$runner || !$sessionId) {
@@ -604,13 +771,15 @@
604 771 wp_send_json_error(['message' => esc_html__('Elementor app not available.', 'ultimate-post-kit')]);
605 772 }
606 773
607 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.
608 776 @ini_set('max_execution_time', 60 * 5);
609 777
610 778 $import_export_module = $app->get_component('import-export');
611 779 $import = $import_export_module->import_kit_by_runner($sessionId, $runner);
612 780
781 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- hooking into Elementor's own action, not a plugin-defined hook.
613 782 do_action('elementor/import-export/import-kit/runner/after-run', $import);
614 783 wp_send_json_success($import);
615 784 } catch (\Throwable $throwable) {
616 785 wp_send_json_error(['message' => $throwable->getMessage()]);