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 +179 -24 4.2.3 → 4.5.6 View file →
@@ -45,23 +45,41 @@
45 45 }
46 46 return self::$instance;
47 47 }
48 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 +
49 59 // Initialize hooks
50 60 private function init_hooks() {
51 - 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' ) );
52 63 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
53 64 add_action( 'admin_init', array( $this, 'activate_default_widgets' ) );
54 65 add_action( 'admin_init', array( $this, 'maybe_display_setup_wizard' ) );
55 66 add_action( 'admin_init', array( $this, 'check_manual_wizard_request' ) );
56 67
57 - if ( function_exists( 'add_filter' ) ) {
58 - add_filter( 'auto_update_translation', '__return_false' );
59 - }
68 + // NOTE: WordPress manages plugin/translation updates. Do not add filters
69 + // that interfere with the built-in update pipeline (wp.org Guideline).
60 70 }
61 71
62 72 // Check for manual wizard requests
63 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.
64 82 $is_setup_wizard_request = isset($_GET['upk_setup_wizard']) && $_GET['upk_setup_wizard'] === 'show';
65 83
66 84 if ( $is_setup_wizard_request ) {
67 85 // Use the same approach as first activation - completely override the page
@@ -150,8 +168,15 @@
150 168 }
151 169
152 170 // Check if this is first activation and display setup wizard if needed
153 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 +
154 179 // Only check for first activation here
155 180 if ( get_option( 'bdtupk_setup_wizard_completed' ) === false ) {
156 181 // Set the flag so it doesn't run again
157 182 update_option( 'bdtupk_setup_wizard_completed', true );
@@ -214,12 +239,20 @@
214 239
215 240 // Enqueue necessary scripts
216 241 public function enqueue_scripts() {
217 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 +
218 251 $direction_suffix = is_rtl() ? '.rtl' : '';
219 252
220 253 wp_enqueue_style('bdt-uikit', BDTUPK_ADMIN_ASSETS_URL . 'css/bdt-uikit' . $direction_suffix . '.css', [], '3.17.0');
221 - 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);
222 255
223 256 wp_register_script( 'upk-setup-wizard', plugins_url( 'assets/js/setup-wizard.js', __FILE__ ), array( 'jquery' ), '1.0.0', true );
224 257 wp_register_style( 'upk-setup-wizard', plugins_url( 'assets/css/setup-wizard.css', __FILE__ ), array(), '1.0.0' );
225 258
@@ -227,12 +260,12 @@
227 260 wp_enqueue_style( 'upk-setup-wizard' );
228 261
229 262 wp_localize_script(
230 263 'upk-setup-wizard',
231 - 'BDT_SetupWizard',
264 + 'UPK_SetupWizard',
232 265 array(
233 266 'ajax_url' => admin_url( 'admin-ajax.php' ),
234 - 'nonce' => wp_create_nonce( 'setup_wizard_nonce' ),
267 + 'nonce' => wp_create_nonce( 'ultimate_post_kit_setup_wizard_nonce' ),
235 268 'is_fullscreen' => true
236 269 )
237 270 );
238 271 }
@@ -246,13 +279,110 @@
246 279 );
247 280 return $arr_obj;
248 281 }
249 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 +
250 380 // Install plugins
251 381 public function install_plugins() {
252 - check_ajax_referer( 'setup_wizard_nonce', 'nonce' );
382 + check_ajax_referer( 'ultimate_post_kit_setup_wizard_nonce', 'nonce' );
253 383
254 - $plugin_slugs = isset( $_POST['plugins'] ) ? $_POST['plugins'] : array();
384 + $plugin_slugs = isset( $_POST['plugins'] ) ? map_deep( wp_unslash( $_POST['plugins'] ), 'sanitize_text_field' ) : array();
255 385
256 386 if ( empty( $plugin_slugs ) || ! is_array( $plugin_slugs ) ) {
257 387 wp_send_json_error( array( 'message' => 'Invalid plugins array' ) );
258 388 }
@@ -311,14 +441,25 @@
311 441 continue;
312 442 }
313 443 }
314 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 +
315 456 // active the plugin
316 - if ( is_plugin_inactive($plugin_slug) ) {
457 + if ( is_plugin_inactive( $plugin_slug ) ) {
317 458 $activation_result = activate_plugin( $plugin_slug );
318 459 if ( is_wp_error( $activation_result ) ) {
319 460 $results[] = array(
320 - 'slug' => $slug,
461 + 'slug' => $plugin_slug,
321 462 'success' => false,
322 463 'message' => $activation_result->get_error_message(),
323 464 );
324 465 continue;
@@ -358,8 +499,15 @@
358 499 /**
359 500 * Activate default widgets in setup wizard
360 501 */
361 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 +
362 510 // List of widgets to activate by default
363 511 $default_active_widgets = array(
364 512 'alex-grid',
365 513 'alice-grid',
@@ -407,10 +555,10 @@
407 555 Setup_Wizard::get_instance();
408 556
409 557 use Elementor\TemplateLibrary\Source_Local;
410 558
411 -add_action('wp_ajax_import_elementor_template', function () {
412 - 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' );
413 561
414 562 if ( ! current_user_can( 'manage_options' ) ) {
415 563 wp_send_json_error( array( 'message' => esc_html__( 'Unauthorized', 'ultimate-post-kit' ) ) );
416 564 wp_die();
@@ -418,10 +566,9 @@
418 566
419 567 $json_url = isset( $_POST['import_url'] ) ? esc_url_raw( wp_unslash( $_POST['import_url'] ) ) : '';
420 568
421 569 $response = wp_safe_remote_get($json_url, array(
422 - 'timeout' => 60,
423 - 'sslverify' => false
570 + 'timeout' => 60,
424 571 ));
425 572
426 573 if (is_wp_error($response)) {
427 574 wp_send_json_error(['message' => esc_html__('Failed to fetch template from URL.', 'ultimate-post-kit')]);
@@ -457,9 +604,9 @@
457 604
458 605 $template_id = $templateData[0]['template_id'];
459 606 $metaData = get_post_meta($template_id);
460 607
461 - $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');
462 609
463 610 // Validate Elementor Data
464 611 if (!isset($metaData['_elementor_data'][0])) {
465 612 wp_send_json_error(['message' => esc_html__('Elementor data not found in template.', 'ultimate-post-kit')]);
@@ -485,9 +632,11 @@
485 632 update_post_meta($new_post_id, '_elementor_data', $_elementor_data);
486 633
487 634 // Import Page Settings if available
488 635 if (isset($metaData['_elementor_page_settings'][0])) {
489 - $_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];
490 639 update_post_meta($new_post_id, '_elementor_page_settings', $_elementor_page_settings);
491 640 }
492 641
493 642 update_post_meta($new_post_id, '_elementor_template_type', $sourceData2['type'] ?? '');
@@ -502,10 +651,10 @@
502 651 }
503 652 );
504 653
505 654
506 -add_action('wp_ajax_import_upk_elementor_bundle_template', function () {
507 - 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');
508 657
509 658 if ( ! current_user_can( 'manage_options' ) ) {
510 659 wp_send_json_error( array( 'message' => esc_html__( 'Unauthorized', 'ultimate-post-kit' ) ) );
511 660 wp_die();
@@ -517,10 +666,9 @@
517 666 wp_send_json_error(['message' => esc_html__('Invalid import URL', 'ultimate-post-kit')]);
518 667 }
519 668
520 669 $remote_zip_request = wp_safe_remote_get($file_url, array(
521 - 'timeout' => 60,
522 - 'sslverify' => false,
670 + 'timeout' => 60,
523 671 ));
524 672
525 673 if (is_wp_error($remote_zip_request)) {
526 674 wp_send_json_error(['message' => esc_html__('Failed to fetch template from URL.', 'ultimate-post-kit')]);
@@ -589,9 +737,14 @@
589 737 ];
590 738
591 739 $import = $import_export_module->import_kit($tmp_folder_id, $settings, true);
592 740
593 - 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.
594 747
595 748 wp_send_json_success($import);
596 749 } catch (\Throwable $e) {
597 750 wp_send_json_error(['message' => esc_html__('Import failed: ', 'ultimate-post-kit') . esc_html($e->getMessage())]);
@@ -597,10 +750,10 @@
597 750 wp_send_json_error(['message' => esc_html__('Import failed: ', 'ultimate-post-kit') . esc_html($e->getMessage())]);
598 751 }
599 752 });
600 753
601 -add_action('wp_ajax_import_upk_elementor_bundle_runner_template', function () {
602 - 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');
603 756
604 757 if ( ! current_user_can( 'manage_options' ) ) {
605 758 wp_send_json_error( array( 'message' => esc_html__( 'Unauthorized', 'ultimate-post-kit' ) ) );
606 759 wp_die();
@@ -618,13 +771,15 @@
618 771 wp_send_json_error(['message' => esc_html__('Elementor app not available.', 'ultimate-post-kit')]);
619 772 }
620 773
621 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.
622 776 @ini_set('max_execution_time', 60 * 5);
623 777
624 778 $import_export_module = $app->get_component('import-export');
625 779 $import = $import_export_module->import_kit_by_runner($sessionId, $runner);
626 780
781 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- hooking into Elementor's own action, not a plugin-defined hook.
627 782 do_action('elementor/import-export/import-kit/runner/after-run', $import);
628 783 wp_send_json_success($import);
629 784 } catch (\Throwable $throwable) {
630 785 wp_send_json_error(['message' => $throwable->getMessage()]);