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/wishlist-compare.php +246 -137 1.6.13.1.4 View file →
@@ -1,181 +1,290 @@
1 1 <?php
2 2
3 3 namespace UltimateStoreKit;
4 4
5 -if (!defined('ABSPATH')) {
6 - exit;
5 +if (! defined('ABSPATH')) {
6 + exit;
7 7 }
8 8
9 9 // Exit if accessed directly
10 10
11 -if (!function_exists('is_plugin_active')) {
12 - include_once ABSPATH . 'wp-admin/includes/plugin.php';
13 -}
11 +final class WishlistCompare {
12 + public function __construct() {
13 + add_action('wp_ajax_usk_add_to_wishlist', [$this, 'usk_add_to_wishlist']);
14 + add_action('wp_ajax_nopriv_usk_add_to_wishlist', [$this, 'usk_add_to_wishlist']);
14 15
15 -final class WishlistCompare {
16 - public function __construct() {
17 - add_action('wp_ajax_usk_add_to_wishlist', [$this, 'usk_add_to_wishlist']);
18 - add_action('wp_ajax_nopriv_usk_add_to_wishlist', [$this, 'usk_add_to_wishlist']);
16 + add_action('wp_ajax_usk_remove_wishlist', [$this, 'usk_remove_wishlist']);
17 + add_action('wp_ajax_nopriv_usk_remove_wishlist', [$this, 'usk_remove_wishlist']);
19 18
20 - add_action('wp_ajax_usk_add_to_compare_products', [$this, 'usk_add_to_compare_products']);
21 - add_action('wp_ajax_nopriv_usk_add_to_compare_products', [$this, 'usk_add_to_compare_products']);
19 + add_action('wp_ajax_usk_add_to_compare_products', [$this, 'usk_add_to_compare_products']);
20 + add_action('wp_ajax_nopriv_usk_add_to_compare_products', [$this, 'usk_add_to_compare_products']);
22 21
23 - add_action('wp_ajax_usk_remove_from_compare_products', [$this, 'usk_remove_from_compare_products']);
24 - add_action('wp_ajax_nopriv_usk_remove_from_compare_products', [$this, 'usk_remove_from_compare_products']);
22 + add_action('wp_ajax_usk_remove_from_compare_products', [$this, 'usk_remove_from_compare_products']);
23 + add_action('wp_ajax_nopriv_usk_remove_from_compare_products', [$this, 'usk_remove_from_compare_products']);
25 24
26 - //wishlist
27 - // add_action('woocommerce_account_wishlist_endpoint', [$this, 'usk_wishlist_content']);
28 - // add_filter('woocommerce_account_menu_items', [$this, 'usk_wishlist_link_my_account']);
29 - // add_filter('query_vars', [$this, 'usk_wishlist_query_vars'], 0);
30 - // add_action('init', [$this, 'usk_add_rewrite_flash_rules_endpoint']);
31 - }
25 + //wishlist
26 + // add_action('woocommerce_account_wishlist_endpoint', [$this, 'usk_wishlist_content']);
27 + // add_filter('woocommerce_account_menu_items', [$this, 'usk_wishlist_link_my_account']);
28 + // add_filter('query_vars', [$this, 'usk_wishlist_query_vars'], 0);
29 + // add_action('init', [$this, 'usk_add_rewrite_flash_rules_endpoint']);
30 + }
32 31
33 - public function usk_add_to_wishlist() {
34 - $response = [
35 - 'status' => 0,
36 - 'message' => __('Unauthorized!', 'ultimate-store-kit'),
37 - ];
32 + /**
33 + * CSRF check for the wishlist/compare endpoints.
34 + *
35 + * A logged-out visitor's list lives entirely in their own cookie, and their
36 + * pages are routinely served from a full-page cache where an embedded nonce
37 + * would already be stale — enforcing one there breaks the feature without
38 + * protecting any server-side state. A logged-in request writes to user meta,
39 + * so that path gets the check.
40 + */
41 + private function verify_request() {
42 + if (! is_user_logged_in()) {
43 + return;
44 + }
38 45
39 - if (!isset($_POST['product_id'])) {
40 - $response['message'] = __('No product selected!', 'ultimate-store-kit');
41 - wp_send_json($response);
42 - }
46 + check_ajax_referer('usk_wishlist_compare', 'nonce');
47 + }
43 48
44 - $product_id = isset($_POST['product_id']) ? sanitize_text_field($_POST['product_id']) : '';
49 + // Every handler below calls verify_request() first, which PHPCS cannot follow.
50 + // See its docblock for why the logged-out path is deliberately exempt.
51 + // phpcs:disable WordPress.Security.NonceVerification.Missing
45 52
46 - $user_id = get_current_user_id();
47 - $wishlist = ultimate_store_kit_get_wishlist($user_id);
53 + public function usk_add_to_wishlist() {
54 + $this->verify_request();
48 55
49 - $wishlistCounter = count($wishlist);
50 - // print_r(count($wishlist));
56 + $response = [
57 + 'status' => 0,
58 + 'message' => __('Unauthorized!', 'ultimate-store-kit'),
59 + ];
51 60
52 - if (($key = array_search($product_id, $wishlist)) !== false) {
53 - $response['action'] = 'removed';
54 - $response['count'] = --$wishlistCounter;
55 - unset($wishlist[$key]);
56 - } else {
57 - $response['action'] = 'added';
58 - $response['count'] = ++$wishlistCounter;
59 - $wishlist[] = $product_id;
60 - }
61 + if (! isset($_POST['product_id'])) {
62 + $response['message'] = __('No product selected!', 'ultimate-store-kit');
63 + wp_send_json($response);
64 + }
61 65
62 - $wishlist = array_unique($wishlist);
66 + $product_id = isset($_POST['product_id']) ? absint($_POST['product_id']) : 0;
63 67
64 - // update wishlist
65 - $this->ultimate_store_kit_set_wishlist($wishlist, $user_id);
68 + $user_id = get_current_user_id();
69 + $wishlist = ultimate_store_kit_get_wishlist($user_id);
66 70
67 - // send response
68 - $response['status'] = 1;
71 + $wishlistCounter = count($wishlist);
69 72
70 - if ($response['action'] == 'added') {
71 - $response['message'] = __("Wishlist item Added!", "ultimate-store-kit");
72 - wp_send_json($response);
73 - } else {
74 - $response['message'] = __("Add To Wishlist", "ultimate-store-kit");
75 - wp_send_json($response);
76 - }
77 - }
73 + if (($key = array_search($product_id, $wishlist)) !== false) {
74 + $response['action'] = 'removed';
75 + $response['count'] = --$wishlistCounter;
76 + unset($wishlist[$key]);
77 + } else {
78 + // Only gate additions. A removal just drops an id the visitor already
79 + // holds, so it must keep working even if the product was since unpublished.
80 + if (! ultimate_store_kit_is_public_product($product_id)) {
81 + $response['message'] = __('Invalid product!', 'ultimate-store-kit');
82 + wp_send_json($response);
83 + }
78 84
79 - public function ultimate_store_kit_set_wishlist($wishlist, $user_id = 0) {
80 - $_wishlist_key = '_ultimate_store_kit_wishlist';
81 - $_wishlist = [];
82 - // if ($user_id != 0) {
83 - // update_user_meta($user_id, $_wishlist_key, $wishlist);
84 - // } else {
85 - setcookie($_wishlist_key, serialize($wishlist), time() + MONTH_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN);
86 - // }
87 - }
85 + if ($wishlistCounter >= ultimate_store_kit_get_list_item_limit()) {
86 + $response['message'] = __('Wishlist is full!', 'ultimate-store-kit');
87 + wp_send_json($response);
88 + }
88 89
90 + $response['action'] = 'added';
91 + $response['count'] = ++$wishlistCounter;
92 + $wishlist[] = $product_id;
93 + }
94 +
95 + $wishlist = array_unique($wishlist);
96 +
97 + // update wishlist
98 + $this->ultimate_store_kit_set_wishlist($wishlist, $user_id);
99 +
100 + // send response
101 + $response['status'] = 1;
102 +
103 + if ($response['action'] == 'added') {
104 + $response['message'] = __("Wishlist item Added!", "ultimate-store-kit");
105 + wp_send_json($response);
106 + } else {
107 + $response['message'] = __("Add To Wishlist", "ultimate-store-kit");
108 + wp_send_json($response);
109 + }
110 + }
111 +
112 + /**
113 + * Remove a single product from the wishlist.
114 + *
115 + * Deliberately idempotent rather than a toggle: the remove control is bound by
116 + * more than one script, so a click can fire this twice. A toggle would remove
117 + * the product and then immediately put it back.
118 + */
119 + public function usk_remove_wishlist() {
120 + $this->verify_request();
121 +
122 + $response = [
123 + 'status' => 0,
124 + 'message' => __('Unauthorized!', 'ultimate-store-kit'),
125 + ];
126 +
127 + if (! isset($_POST['product_id'])) {
128 + $response['message'] = __('No product selected!', 'ultimate-store-kit');
129 + wp_send_json($response);
130 + }
131 +
132 + $product_id = absint($_POST['product_id']);
133 + $user_id = get_current_user_id();
134 + $wishlist = ultimate_store_kit_get_wishlist($user_id);
135 +
136 + if (($key = array_search($product_id, $wishlist)) !== false) {
137 + unset($wishlist[$key]);
138 + }
139 +
140 + $this->ultimate_store_kit_set_wishlist($wishlist, $user_id);
141 +
142 + // Report the post-condition, not what this particular call changed, so a
143 + // duplicate request still tells the UI the row is gone.
144 + $response['status'] = 1;
145 + $response['action'] = 'removed';
146 + $response['count'] = count($wishlist);
147 + $response['message'] = __('Wishlist item removed!', 'ultimate-store-kit');
148 +
149 + wp_send_json($response);
150 + }
151 +
152 + /**
153 + * Persist the wishlist.
154 + *
155 + * @param array $wishlist Full list to store — this replaces what is stored.
156 + * @param int $user_id Unused; kept for signature compatibility. The wishlist
157 + * is cookie-backed for every visitor, logged in or not.
158 + */
159 + public function ultimate_store_kit_set_wishlist($wishlist, $user_id = 0) {
160 + $_wishlist_key = '_ultimate_store_kit_wishlist';
161 +
162 + // Straight write, not a merge. This previously merged the incoming list back
163 + // into the stored one, which silently undid every removal — nothing could
164 + // ever leave a wishlist. Reindexed because unset() leaves a gap, and a gapped
165 + // array json_encodes to an object that the getter then discards.
166 + $wishlist = array_values(array_unique($wishlist));
167 +
168 + setcookie($_wishlist_key, wp_json_encode($wishlist), time() + MONTH_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN);
169 + }
170 +
89 171 public function get_compare_product_page_id() {
90 - if($comparePage = ultimate_store_kit_compare_product_page()){
91 - return $comparePage->ID;
172 + // ultimate_store_kit_compare_product_page() already returns an int post id.
173 + // Reading ->ID off it warned under PHP 8 and evaluated to null, so the
174 + // "Added" response never carried a compare page URL.
175 + return ultimate_store_kit_compare_product_page();
176 + }
177 +
178 +
179 + //======================================
180 + //=========COMPARE PRODUCTS=============
181 + //======================================
182 + public function usk_add_to_compare_products() {
183 + $this->verify_request();
184 +
185 + $response = [
186 + 'status' => 0,
187 + 'message' => __('Unauthorized!', 'ultimate-store-kit'),
188 + ];
189 +
190 + if (! isset($_POST['product_id'])) {
191 + $response['message'] = __('No product selected!', 'ultimate-store-kit');
192 + wp_send_json($response);
92 193 }
93 - }
94 194
195 + $product_id = absint($_POST['product_id']);
95 196
96 - //======================================
97 - //=========COMPARE PRODUCTS=============
98 - //======================================
99 - public function usk_add_to_compare_products() {
100 - $response = [
101 - 'status' => 0,
102 - 'message' => __('Unauthorized!', 'usk'),
103 - ];
197 + if (! ultimate_store_kit_is_public_product($product_id)) {
198 + $response['message'] = __('Invalid product!', 'ultimate-store-kit');
199 + wp_send_json($response);
200 + }
104 201
105 - if (!isset($_POST['product_id'])) {
106 - $response['message'] = __('No product selected!', 'usk');
107 - wp_send_json($response);
108 - }
202 + $user_id = get_current_user_id();
203 + $compare_products = ultimate_store_kit_get_compare_products($user_id);
109 204
110 - $user_id = get_current_user_id();
111 - $compare_products = usk_get_compare_products($user_id);
205 + if (! is_array($compare_products)) {
206 + $compare_products = [];
207 + }
112 208
113 - // count compare products
114 - if (is_array($compare_products)) {
115 - $response['count'] = count($compare_products) + 1;
116 - }
209 + if (count($compare_products) >= ultimate_store_kit_get_list_item_limit() && ! in_array($product_id, $compare_products)) {
210 + $response['message'] = __('Compare list is full!', 'ultimate-store-kit');
211 + wp_send_json($response);
212 + }
117 213
118 - //add to compare products
119 - $response['action'] = 'added';
120 - $compare_products[] = $_POST['product_id'];
214 + // count compare products
215 + $response['count'] = count($compare_products) + 1;
121 216
122 - $compare_products = array_unique($compare_products);
217 + //add to compare products
218 + $response['action'] = 'added';
219 + $compare_products[] = $product_id;
123 220
124 - // update compare_productsusk_add_to_compare_products
125 - $this->ultimate_store_kit_set_compare_products($compare_products, $user_id);
221 + $compare_products = array_unique($compare_products);
126 222
127 - // send response
128 - $response['status'] = 1;
129 - if ($response['action'] == 'added') {
130 - $response['message'] = __("Added", "ultimate-store-kit");
131 - $response['url'] = '';
132 - if($pageId = $this->get_compare_product_page_id()){
133 - $response['url'] = get_permalink($pageId);
223 + // update compare_productsusk_add_to_compare_products
224 + $this->ultimate_store_kit_set_compare_products($compare_products, $user_id);
225 +
226 + // send response
227 + $response['status'] = 1;
228 + if ($response['action'] == 'added') {
229 + $response['message'] = __("Added", "ultimate-store-kit");
230 + $response['url'] = '';
231 + if ($pageId = $this->get_compare_product_page_id()) {
232 + $response['url'] = get_permalink($pageId);
134 233 }
135 234
136 - wp_send_json($response);
137 - } else {
138 - $response['message'] = __("Compare", "ultimate-store-kit");
139 - wp_send_json($response);
140 - }
141 - }
142 - public function usk_remove_from_compare_products() {
143 - $response = [
144 - 'status' => 0,
145 - 'message' => __('Unauthorized!', 'ultimate-store-kit'),
146 - ];
147 - if (!isset($_POST['product_id'])) {
148 - $response['message'] = __('No product selected!', 'ultimate-store-kit');
149 - wp_send_json($response);
150 - }
151 - $user_id = get_current_user_id();
152 - $compare_products = usk_get_compare_products($user_id);
235 + wp_send_json($response);
236 + } else {
237 + $response['message'] = __("Compare", "ultimate-store-kit");
238 + wp_send_json($response);
239 + }
240 + }
241 + public function usk_remove_from_compare_products() {
242 + $this->verify_request();
153 243
154 - //add remove from compare products
155 - if (($key = array_search($_POST['product_id'], $compare_products)) !== false) {
156 - $response['action'] = 'removed';
157 - unset($compare_products[$key]);
158 - }
159 - $compare_products = array_unique($compare_products);
244 + $response = [
245 + 'status' => 0,
246 + 'message' => __('Unauthorized!', 'ultimate-store-kit'),
247 + ];
248 + if (! isset($_POST['product_id'])) {
249 + $response['message'] = __('No product selected!', 'ultimate-store-kit');
250 + wp_send_json($response);
251 + }
252 + $product_id = absint($_POST['product_id']);
253 + $user_id = get_current_user_id();
254 + $compare_products = ultimate_store_kit_get_compare_products($user_id);
160 255
161 - // update compare_products
162 - $this->ultimate_store_kit_set_compare_products($compare_products, $user_id);
256 + //add remove from compare products
257 + if (($key = array_search($product_id, $compare_products)) !== false) {
258 + $response['action'] = 'removed';
259 + unset($compare_products[$key]);
260 + }
163 261
164 - // send response
165 - $response['status'] = 1;
166 - $response['message'] = sprintf(__('compare products item %s!', 'ultimate-store-kit'), $response['action']);
167 - wp_send_json($response);
168 - }
169 - public function ultimate_store_kit_set_compare_products($compare_products, $user_id = 0) {
170 - $_compare_products_key = '_ultimate_store_kit_compare_products';
171 - $_compare_products = [];
262 + // Reindex: unset() leaves a gap, and a gapped array json_encodes to an
263 + // object, which breaks the cookie read back in ultimate_store_kit_get_compare_products().
264 + $compare_products = array_values(array_unique($compare_products));
172 265
173 - if ($user_id != 0) {
174 - update_user_meta($user_id, $_compare_products_key, $compare_products);
175 - } else {
176 - setcookie($_compare_products_key, serialize($compare_products), time() + MONTH_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN);
177 - }
178 - }
266 + // update compare_products
267 + $this->ultimate_store_kit_set_compare_products($compare_products, $user_id);
268 +
269 + // send response
270 + $response['status'] = 1;
271 + /* translators: %s is the action performed on the compared product */
272 + $response['message'] = sprintf(__('Compared product item: %s.', 'ultimate-store-kit'), $response['action']);
273 +
274 + wp_send_json($response);
275 + }
276 + public function ultimate_store_kit_set_compare_products($compare_products, $user_id = 0) {
277 + $_compare_products_key = '_ultimate_store_kit_compare_products';
278 + $_compare_products = [];
279 +
280 + if ($user_id != 0) {
281 + update_user_meta($user_id, $_compare_products_key, $compare_products);
282 + } else {
283 + setcookie($_compare_products_key, json_encode($compare_products), time() + MONTH_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN);
284 + }
285 + }
286 +
287 + // phpcs:enable WordPress.Security.NonceVerification.Missing
179 288 }
180 289
181 290 new WishlistCompare();