PluginProbe
Ultimate Store Kit – Store Builder Addons for Elementor, WooCommerce Store Builder, EDD Store Builder / 3.1.4
Ultimate Store Kit – Store Builder Addons for Elementor, WooCommerce Store Builder, EDD Store Builder v3.1.4
3.1.4 3.0.8 3.0.9 3.1.0 3.1.2 3.1.3 3.0.7 3.0.5 3.0.4 3.0.3 3.0.2 trunk 1.5.0 1.5.1 1.5.2 1.6.1 1.6.2 1.6.3 1.6.4 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 All 93 releases
← All changes | includes/Admin/Biggopties.php +95 -20 3.0.93.1.4 View file →
@@ -1,8 +1,12 @@
1 1 <?php
2 2
3 3 namespace UltimateStoreKit\Admin;
4 4
5 +if (! defined('ABSPATH')) {
6 + exit; // Exit if accessed directly
7 +}
8 +
5 9 use UltimateStoreKit\Base\Singleton;
6 10
7 11 /**
8 12 * Biggopties class
@@ -9,10 +13,32 @@
9 13 */
10 14 class Biggopties {
11 15 use Singleton;
12 16
17 + /**
18 + * Max seconds to wait for the remote API.
19 + */
20 + const REQUEST_TIMEOUT = 5;
21 +
22 + /**
23 + * How long to skip remote requests after a failure.
24 + */
25 + const FAILURE_BACKOFF = HOUR_IN_SECONDS;
26 +
27 + /**
28 + * How long a successful response stays cached.
29 + */
30 + const CACHE_LIFETIME = HOUR_IN_SECONDS;
31 +
13 32 private static $biggopties = [];
14 33
34 + /**
35 + * Every notice id this plugin renders starts with this. The dismiss handler
36 + * writes the id straight into a transient / user-meta key, so it will only
37 + * accept keys that carry the prefix.
38 + */
39 + const DISMISS_KEY_PREFIX = 'bdt-admin-biggopti-';
40 +
15 41 public function __construct() {
16 42
17 43 // add_action('admin_notices', [$this, 'show_biggopties']);
18 44 add_action('wp_ajax_ultimate-store-kit-biggopties', [$this, 'dismiss']);
@@ -25,14 +51,14 @@
25 51 /**
26 52 * Enqueue admin scripts
27 53 */
28 54 public function enqueue_admin_scripts() {
29 - wp_enqueue_style('bdt-product-feed', BDTUSK_ASSETS_URL . 'admin/others/css/product-feed.css', [], BDTUSK_VER);
55 + wp_enqueue_style('ultimate-store-kit-product-feed', BDTUSK_ASSETS_URL . 'admin/others/css/product-feed.css', [], BDTUSK_VER);
30 56 wp_enqueue_script('usk-biggopti', BDTUSK_ASSETS_URL . 'admin/others/js/biggopti.js', ['jquery'], BDTUSK_VER, true);
31 57
32 58 $dismissals = get_option('bdt_biggopti_dismissals', []);
33 59 $dismissed_display_ids = [];
34 - $prefix = 'bdt-admin-biggopti-api-biggopti-';
60 + $prefix = self::DISMISS_KEY_PREFIX . 'api-biggopti-';
35 61 foreach (array_keys($dismissals) as $key) {
36 62 if (strpos($key, $prefix) === 0) {
37 63 $dismissed_display_ids[] = substr($key, strlen($prefix));
38 64 } else {
@@ -40,8 +66,9 @@
40 66 }
41 67 }
42 68
43 69 $current_sector = '';
70 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only check of which admin screen is showing.
44 71 if (isset($_GET['page']) && $_GET['page'] === 'ultimate_store_kit_options') {
45 72 $current_sector = 'plugin_dashboard';
46 73 }
47 74
@@ -47,9 +74,9 @@
47 74
48 75 $script_config = [
49 76 'ajaxurl' => admin_url('admin-ajax.php'),
50 77 'nonce' => wp_create_nonce('ultimate-store-kit'),
51 - 'isPro' => function_exists('usk_license_validation') && usk_license_validation(),
78 + 'isPro' => function_exists('ultimate_store_kit_license_validation') && ultimate_store_kit_license_validation(),
52 79 'assetsUrl' => defined('BDTUSK_ASSETS_URL') ? BDTUSK_ASSETS_URL : '',
53 80 'dismissedDisplayIds' => $dismissed_display_ids,
54 81 'currentSector' => $current_sector,
55 82 ];
@@ -63,27 +90,59 @@
63 90 * @return array|mixed
64 91 */
65 92 private function get_api_biggopties_data() {
66 93 // API endpoint for biggopties - you can change this to your actual endpoint
67 - $api_url = '';
94 + $api_url = '';
95 + $transient_key = 'ultimate_store_kit_biggopties_api';
68 96
97 + $cached_data = get_transient($transient_key);
98 +
99 + if (! empty($cached_data)) {
100 + $biggopties = json_decode($cached_data);
101 + return $this->extract_biggopties($biggopties);
102 + }
103 +
104 + /**
105 + * A recent request failed, so don't retry on every admin page load.
106 + */
107 + if (get_transient($transient_key . '_failed')) {
108 + return [];
109 + }
110 +
69 111 $response = wp_remote_get($api_url, [
70 - 'timeout' => 30,
112 + 'timeout' => self::REQUEST_TIMEOUT,
71 113 'headers' => [
72 114 'Accept' => 'application/json',
73 115 ],
74 116 ]);
75 117
76 - if (is_wp_error($response)) {
118 + if (is_wp_error($response) || 200 !== (int) wp_remote_retrieve_response_code($response)) {
119 + set_transient($transient_key . '_failed', 1, self::FAILURE_BACKOFF);
77 120 return [];
78 121 }
79 122
80 - $response_code = wp_remote_retrieve_response_code($response);
81 -
82 123 $response_body = wp_remote_retrieve_body($response);
83 124
84 125 $biggopties = json_decode($response_body);
85 126
127 + if (null === $biggopties) {
128 + set_transient($transient_key . '_failed', 1, self::FAILURE_BACKOFF);
129 + return [];
130 + }
131 +
132 + set_transient($transient_key, $response_body, self::CACHE_LIFETIME);
133 +
134 + return $this->extract_biggopties($biggopties);
135 + }
136 +
137 + /**
138 + * Pull this plugin's records out of the decoded API payload.
139 + *
140 + * @param mixed $biggopties Decoded API response.
141 + * @return array
142 + */
143 + private function extract_biggopties($biggopties) {
144 +
86 145 if (isset($biggopties) && isset($biggopties->{'ultimate-store-kit'})) {
87 146 $data = $biggopties->{'ultimate-store-kit'};
88 147 if (is_array($data)) {
89 148 return $data;
@@ -159,9 +218,9 @@
159 218 */
160 219 private function is_biggopti_compatible_with_plugin($biggopti) {
161 220 // Get current plugin info
162 221 $current_plugin_slug = $this->get_current_plugin_slug();
163 - $is_pro_active = function_exists('_is_usk_pro_activated') ? _is_usk_pro_activated() : false;
222 + $is_pro_active = function_exists('ultimate_store_kit_is_pro_activated') ? ultimate_store_kit_is_pro_activated() : false;
164 223 $is_lite_active = $current_plugin_slug === 'ultimate-store-kit';
165 224 $is_pro_plugin = $current_plugin_slug === 'ultimate-store-kit-pro';
166 225
167 226 // Get client targets, default to ['both'] if not set or not an array
@@ -240,18 +299,18 @@
240 299 $background_style = '';
241 300 $wrapper_classes = 'bdt-biggopti-wrapper';
242 301
243 302 if (isset($biggopti->background_color) && !empty($biggopti->background_color)) {
244 - $background_style .= 'background-color: ' . esc_attr($biggopti->background_color) . ';';
303 + $background_style .= 'background-color: ' . sanitize_text_field($biggopti->background_color) . ';';
245 304 }
246 305
247 306 if (isset($biggopti->image) && !empty($biggopti->image)) {
248 - $background_style .= 'background-image: url(' . esc_url($biggopti->image) . ');';
307 + $background_style .= 'background-image: url(' . esc_url_raw($biggopti->image) . ');';
249 308 $wrapper_classes .= ' has-background-image';
250 309 }
251 310
252 311 ?>
253 - <div class="<?php echo esc_attr($wrapper_classes); ?>" <?php echo $background_style ? 'style="' . $background_style . '"' : ''; ?>>
312 + <div class="<?php echo esc_attr($wrapper_classes); ?>" <?php echo $background_style ? 'style="' . esc_attr($background_style) . '"' : ''; ?>>
254 313
255 314
256 315 <?php $title = (isset($biggopti->title) && !empty($biggopti->title)) ? $biggopti->title : ''; ?>
257 316
@@ -323,9 +382,9 @@
323 382 /**
324 383 * AJAX: Build and return API biggopties HTML for dynamic injection
325 384 */
326 385 public function ajax_fetch_api_biggopties() {
327 - $nonce = isset($_POST['_wpnonce']) ? sanitize_text_field($_POST['_wpnonce']) : '';
386 + $nonce = isset($_POST['_wpnonce']) ? sanitize_text_field(wp_unslash($_POST['_wpnonce'])) : '';
328 387 if (!wp_verify_nonce($nonce, 'ultimate-store-kit')) {
329 388 wp_send_json_error(['message' => 'invalid_nonce']);
330 389 }
331 390
@@ -333,9 +392,9 @@
333 392 wp_send_json_error(['message' => 'forbidden']);
334 393 }
335 394
336 395 // Don't show biggopties on plugin/theme install and upload pages
337 - $current_url = isset($_POST['current_url']) ? sanitize_text_field($_POST['current_url']) : '';
396 + $current_url = isset($_POST['current_url']) ? sanitize_text_field(wp_unslash($_POST['current_url'])) : '';
338 397
339 398 if (!empty($current_url)) {
340 399 $excluded_patterns = [
341 400 'plugin-install.php',
@@ -390,12 +449,15 @@
390 449 /**
391 450 * Dismiss Biggopti.
392 451 */
393 452 public function dismiss() {
394 - $nonce = (isset($_POST['_wpnonce'])) ? sanitize_text_field($_POST['_wpnonce']) : '';
395 - $id = (isset($_POST['id'])) ? esc_attr($_POST['id']) : '';
396 - $time = (isset($_POST['time'])) ? esc_attr($_POST['time']) : '';
397 - $meta = (isset($_POST['meta'])) ? esc_attr($_POST['meta']) : '';
453 + $nonce = (isset($_POST['_wpnonce'])) ? sanitize_text_field(wp_unslash($_POST['_wpnonce'])) : '';
454 + // Not sanitize_key(): the id is echoed back from the notice markup and has to
455 + // match the key show_biggopties() reads verbatim, so it is validated below
456 + // rather than rewritten here.
457 + $id = (isset($_POST['id'])) ? sanitize_text_field(wp_unslash($_POST['id'])) : '';
458 + $time = (isset($_POST['time'])) ? absint(wp_unslash($_POST['time'])) : 0;
459 + $meta = (isset($_POST['meta'])) ? sanitize_key(wp_unslash($_POST['meta'])) : '';
398 460
399 461 if (! wp_verify_nonce($nonce, 'ultimate-store-kit')) {
400 462 wp_send_json_error();
401 463 }
@@ -404,8 +466,21 @@
404 466 wp_send_json_error();
405 467 }
406 468
407 469 /**
470 + * The id becomes a transient or user-meta key, so it has to be one of ours.
471 + * Every notice rendered by show_biggopties() carries this prefix; anything
472 + * else is a request to write a key this handler has no business writing.
473 + */
474 + if (! preg_match('/^' . preg_quote(self::DISMISS_KEY_PREFIX, '/') . '[A-Za-z0-9_.-]{1,120}$/', $id)) {
475 + wp_send_json_error();
476 + }
477 +
478 + // Likewise the lifetime is request-supplied — keep it inside a sane window
479 + // so a dismissal cannot be made effectively permanent.
480 + $time = min(max($time, MINUTE_IN_SECONDS), YEAR_IN_SECONDS);
481 +
482 + /**
408 483 * Valid inputs?
409 484 */
410 485 if (!empty($id)) {
411 486 // Handle regular biggopties
@@ -473,12 +548,12 @@
473 548 $biggopti['data'] = ' dismissible-time=' . esc_attr($biggopti['dismissible-time']) . ' ';
474 549 }
475 550
476 551 // Biggopti ID.
477 - $biggopti_id = 'bdt-admin-biggopti-' . $biggopti['id'];
552 + $biggopti_id = self::DISMISS_KEY_PREFIX . $biggopti['id'];
478 553 $biggopti['id'] = $biggopti_id;
479 554 if (!isset($biggopti['id'])) {
480 - $biggopti_id = 'bdt-admin-biggopti-' . $biggopti['id'];
555 + $biggopti_id = self::DISMISS_KEY_PREFIX . $biggopti['id'];
481 556 $biggopti['id'] = $biggopti_id;
482 557 } else {
483 558 $biggopti_id = $biggopti['id'];
484 559 }