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 +168 -17 4.5.0 → 4.5.6 View file →
@@ -45,11 +45,22 @@
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' ) );
@@ -59,8 +70,15 @@
59 70 }
60 71
61 72 // Check for manual wizard requests
62 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 +
63 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 ) {
@@ -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,8 +239,16 @@
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 254 wp_enqueue_script('bdt-uikit', BDTUPK_ADMIN_ASSETS_URL . 'js/bdt-uikit.min.js', ['jquery'], '3.17.0', true);
@@ -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,11 +279,108 @@
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 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 ) ) {
@@ -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) && current_user_can( 'activate_plugins' ) ) {
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')]);
@@ -504,10 +651,10 @@
504 651 }
505 652 );
506 653
507 654
508 -add_action('wp_ajax_import_upk_elementor_bundle_template', function () {
509 - 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');
510 657
511 658 if ( ! current_user_can( 'manage_options' ) ) {
512 659 wp_send_json_error( array( 'message' => esc_html__( 'Unauthorized', 'ultimate-post-kit' ) ) );
513 660 wp_die();
@@ -519,10 +666,9 @@
519 666 wp_send_json_error(['message' => esc_html__('Invalid import URL', 'ultimate-post-kit')]);
520 667 }
521 668
522 669 $remote_zip_request = wp_safe_remote_get($file_url, array(
523 - 'timeout' => 60,
524 - 'sslverify' => false,
670 + 'timeout' => 60,
525 671 ));
526 672
527 673 if (is_wp_error($remote_zip_request)) {
528 674 wp_send_json_error(['message' => esc_html__('Failed to fetch template from URL.', 'ultimate-post-kit')]);
@@ -591,9 +737,14 @@
591 737 ];
592 738
593 739 $import = $import_export_module->import_kit($tmp_folder_id, $settings, true);
594 740
595 - 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.
596 747
597 748 wp_send_json_success($import);
598 749 } catch (\Throwable $e) {
599 750 wp_send_json_error(['message' => esc_html__('Import failed: ', 'ultimate-post-kit') . esc_html($e->getMessage())]);
@@ -599,10 +750,10 @@
599 750 wp_send_json_error(['message' => esc_html__('Import failed: ', 'ultimate-post-kit') . esc_html($e->getMessage())]);
600 751 }
601 752 });
602 753
603 -add_action('wp_ajax_import_upk_elementor_bundle_runner_template', function () {
604 - 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');
605 756
606 757 if ( ! current_user_can( 'manage_options' ) ) {
607 758 wp_send_json_error( array( 'message' => esc_html__( 'Unauthorized', 'ultimate-post-kit' ) ) );
608 759 wp_die();