PluginProbe
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce / 3.2.0
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce v3.2.0
3.3.0 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.1 trunk 1.0.4 1.1.0 1.1.0.1 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 All 162 releases
cartflows / admin-core / ajax / meta-data.php

meta-data.php in CartFlows – Funnel Builder & Checkout Plugin for WooCommerce 3.2.0, at admin-core/ajax/meta-data.php

376 lines 11.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * CartFlows Flows ajax actions.
4 *
5 * @package CartFlows
6 */
7
8 namespace CartflowsAdmin\AdminCore\Ajax;
9
10 // Exit if accessed directly.
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit;
13 }
14
15 use CartflowsAdmin\AdminCore\Ajax\AjaxBase;
16 use CartflowsAdmin\AdminCore\Inc\AdminHelper;
17
18 /**
19 * Class Steps.
20 */
21 class MetaData extends AjaxBase {
22
23 /**
24 * Instance
25 *
26 * @access private
27 * @var object Class object.
28 * @since 1.0.0
29 */
30 private static $instance;
31
32 /**
33 * Initiator
34 *
35 * @since 1.0.0
36 * @return object initialized object of class.
37 */
38 public static function get_instance() {
39 if ( ! isset( self::$instance ) ) {
40 self::$instance = new self();
41 }
42 return self::$instance;
43 }
44
45 /**
46 * Register ajax events.
47 *
48 * @since 1.0.0
49 * @return void
50 */
51 public function register_ajax_events() {
52
53 $ajax_events = array(
54 'json_search_products',
55 'json_search_coupons',
56 );
57
58 $this->init_ajax_events( $ajax_events );
59 }
60
61 /**
62 * Clone step with its meta.
63 */
64 public function json_search_products() {
65
66 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
67 return;
68 }
69
70 check_ajax_referer( 'cartflows_json_search_products', 'security' );
71
72 global $wpdb;
73
74 if ( ! isset( $_POST['term'] ) ) {
75 return;
76 }
77
78 $term = ! empty( $_POST['term'] ) ? sanitize_text_field( wp_unslash( $_POST['term'] ) ) : '';
79
80 // CartFlows supported product types.
81 $supported_product_types = apply_filters( 'cartflows_supported_product_types_for_search', array( 'simple', 'variable', 'variation', 'subscription', 'variable-subscription', 'subscription_variation', 'course' ) );
82
83 // Allowed product types.
84 if ( isset( $_POST['allowed_products'] ) && ! empty( $_POST['allowed_products'] ) ) {
85
86 $allowed_product_types = sanitize_text_field( ( wp_unslash( $_POST['allowed_products'] ) ) );
87
88 $allowed_product_types = $this->sanitize_data_attributes( $allowed_product_types );
89
90 $supported_product_types = $allowed_product_types;
91 }
92
93 // Include product types.
94 if ( isset( $_POST['include_products'] ) && ! empty( $_POST['include_products'] ) ) {
95
96 $include_product_types = sanitize_text_field( ( wp_unslash( $_POST['include_products'] ) ) );
97
98 $include_product_types = $this->sanitize_data_attributes( $include_product_types );
99
100 $supported_product_types = array_merge( $supported_product_types, $include_product_types );
101 }
102
103 // Exclude product types.
104 if ( isset( $_POST['exclude_products'] ) && ! empty( $_POST['exclude_products'] ) ) {
105
106 $excluded_product_types = sanitize_text_field( ( wp_unslash( $_POST['exclude_products'] ) ) );
107
108 $excluded_product_types = $this->sanitize_data_attributes( $excluded_product_types );
109
110 $supported_product_types = array_diff( $supported_product_types, $excluded_product_types );
111 }
112
113 // Get all products data.
114 $data = \WC_Data_Store::load( 'product' );
115 $ids = $data->search_products( $term, '', true, false, 11 );
116
117 // Get all product objects.
118 $product_objects = array_filter( array_map( 'wc_get_product', $ids ), 'wc_products_array_filter_readable' );
119
120 // Remove the product objects whose product type are not in supported array.
121 $product_objects = array_filter(
122 $product_objects,
123 function ( $arr ) use ( $supported_product_types ) {
124 return $arr && is_a( $arr, 'WC_Product' ) && in_array( $arr->get_type(), $supported_product_types, true );
125 }
126 );
127
128 $products_found = array();
129
130 foreach ( $product_objects as $product_object ) {
131 $formatted_name = $product_object->get_name();
132 $managing_stock = $product_object->managing_stock();
133 $is_in_stock = $product_object->is_in_stock();
134 $product_type = $product_object->get_type();
135
136 $availibility_text = $this->get_stock_availability_text( $product_object );
137 $product_price_range = $this->get_formatted_product_price_range( $product_object );
138
139
140 if ( $managing_stock && ! empty( $_GET['display_stock'] ) ) {
141 $stock_amount = $product_object->get_stock_quantity();
142 /* Translators: %d stock amount */
143 $formatted_name .= ' &ndash; ' . sprintf( __( 'Stock: %d', 'cartflows' ), wc_format_stock_quantity_for_display( $stock_amount, $product_object ) );
144 }
145
146 array_push(
147 $products_found,
148 array(
149 'value' => $product_object->get_id(),
150 'label' => $formatted_name,
151 'original_price' => \Cartflows_Helper::get_product_original_price( $product_object ),
152 'product_name' => $product_object->get_name(),
153 'product_desc' => $product_object->get_short_description(),
154 'product_image' => get_the_post_thumbnail_url( $product_object->get_id() ),
155 'product_type' => $product_type,
156 'type_label' => $this->get_product_type_label( $product_object ),
157 'stock_status' => $availibility_text,
158 'in_stock' => $is_in_stock,
159 'price_range' => $product_price_range,
160 )
161 );
162 }
163
164 wp_send_json( $products_found );
165 }
166
167 /**
168 * Function to search coupons
169 */
170 public function json_search_coupons() {
171
172 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
173 return;
174 }
175
176 check_ajax_referer( 'cartflows_json_search_coupons', 'security' );
177
178 global $wpdb;
179
180 if ( ! isset( $_POST['term'] ) ) {
181 return;
182 }
183
184 $term = ! empty( $_POST['term'] ) ? sanitize_text_field( wp_unslash( $_POST['term'] ) ) : '';
185 if ( empty( $term ) ) {
186 die();
187 }
188
189 $posts = wp_cache_get( 'wcf_search_coupons', 'wcf_funnel_Cart' );
190
191 if ( false === $posts ) {
192 $posts = $wpdb->get_results(
193 $wpdb->prepare(
194 "SELECT *
195 FROM {$wpdb->prefix}posts
196 WHERE post_type = %s
197 AND post_title LIKE %s
198 AND post_status = %s",
199 'shop_coupon',
200 $wpdb->esc_like( $term ) . '%',
201 'publish'
202 )
203 ); // db call ok.
204 wp_cache_set( 'wcf_search_coupons', $posts, 'wcf_funnel_Cart' );
205 }
206
207 $coupons_found = array();
208 $all_discount_types = wc_get_coupon_types();
209
210 if ( $posts ) {
211 foreach ( $posts as $post ) {
212
213 $discount_type = get_post_meta( $post->ID, 'discount_type', true );
214
215 if ( ! empty( $all_discount_types[ $discount_type ] ) ) {
216 array_push(
217 $coupons_found,
218 array(
219 'value' => get_the_title( $post->ID ),
220 'label' => get_the_title( $post->ID ) . ' (Type: ' . $all_discount_types[ $discount_type ] . ')',
221 )
222 );
223 }
224 }
225 }
226
227 wp_send_json( $coupons_found );
228 }
229
230 /**
231 * Function to sanitize the product type data attribute.
232 *
233 * @param array $product_types product types.
234 */
235 public function sanitize_data_attributes( $product_types = array() ) {
236
237 if ( ! is_array( $product_types ) ) {
238 $product_types = explode( ',', $product_types );
239 }
240
241 // Sanitize the excluded types against valid product types.
242 foreach ( $product_types as $index => $value ) {
243 $product_types[ $index ] = strtolower( trim( $value ) );
244 }
245 return $product_types;
246 }
247
248 /**
249 * Function to get the stock availability text for a given product.
250 *
251 * This function checks the stock status of a product and returns a string indicating its availability.
252 * The availability text is translated to ensure it is user-friendly and consistent with the plugin's language.
253 *
254 * @param \WC_Product $product The product object to check the stock availability for.
255 * @return string The stock availability text for the product.
256 */
257 public function get_stock_availability_text( $product ) {
258
259 $availability_text = '';
260
261 if ( ! is_a( $product, 'WC_Product' ) ) {
262 return ''; // Return empty if product is not valid.
263 }
264
265 $availability = $product->get_availability();
266
267 if ( ! empty( $availability['class'] ) ) {
268
269 switch ( $availability['class'] ) {
270 case 'available-on-backorder':
271 $availability_text = __( 'On backorder', 'cartflows' );
272 break;
273 case 'in-stock':
274 $availability_text = __( 'In stock', 'cartflows' );
275 break;
276 case 'out-of-stock':
277 $availability_text = __( 'Out of stock', 'cartflows' );
278 break;
279 default:
280 break;
281 }
282 }
283
284 return $availability_text;
285 }
286
287 /**
288 * Get a short, human readable label for product types that behave differently in a funnel.
289 *
290 * Returned as its own field rather than appended to the product name, because the name is
291 * also used for the selected-product chip. Only types that need distinguishing are labelled;
292 * everything else returns an empty string so no tag is rendered.
293 *
294 * @since 3.1.4
295 * @param \WC_Product $product The product object.
296 * @return string Label to show beside the product, or an empty string.
297 */
298 public function get_product_type_label( $product ) {
299
300 if ( ! is_a( $product, 'WC_Product' ) ) {
301 return '';
302 }
303
304 $labels = apply_filters(
305 'cartflows_product_type_labels_for_search',
306 array(
307 'bundle' => __( 'Bundle', 'cartflows' ),
308 )
309 );
310
311 $product_type = $product->get_type();
312
313 return isset( $labels[ $product_type ] ) ? $labels[ $product_type ] : '';
314 }
315
316 /**
317 * Function to generate a formatted price range for a product.
318 *
319 * This function determines the price range for a product based on its type. For variable products, it calculates the price range from the minimum and maximum prices of its variations. For other product types, it uses the product's price or regular price if available.
320 * The function returns a formatted string representing the price range.
321 *
322 * @param \WC_Product $product The product object to generate the price range for.
323 * @return string The formatted price range for the product.
324 */
325 public function get_formatted_product_price_range( $product ) {
326 $original_price = 0;
327
328 $product_type = $product->get_type();
329
330 if ( 'variable' === $product_type ) {
331 // Code for variation product.
332 $variation_price_range = $product->get_variation_prices( true );
333
334 $price = array();
335 $min_price = isset( $variation_price_range['price'] ) ? current( $variation_price_range['price'] ) : null;
336 $max_price = isset( $variation_price_range['price'] ) ? end( $variation_price_range['price'] ) : null;
337 $min_reg_price = isset( $variation_price_range['regular_price'] ) ? current( $variation_price_range['regular_price'] ) : null;
338 $max_reg_price = isset( $variation_price_range['regular_price'] ) ? end( $variation_price_range['regular_price'] ) : null;
339
340 if ( $min_price !== $max_price && ! is_null( $min_price ) && ! is_null( $max_price ) ) {
341 // Build the range from each bound directly so WooCommerce's screen-reader "Price range: ... through ..." text is not appended.
342 $original_price = html_entity_decode( wp_strip_all_tags( wc_price( $min_price ) ) ) . ' – ' . html_entity_decode( wp_strip_all_tags( wc_price( $max_price ) ) );
343 }
344
345 if ( $min_reg_price !== $max_reg_price && ! is_null( $min_reg_price ) && ! is_null( $max_reg_price ) ) {
346 if ( ! empty( $original_price ) ) {
347 $original_price = html_entity_decode( wp_strip_all_tags( wc_price( $min_reg_price ) ) ) . ' – ' . html_entity_decode( wp_strip_all_tags( wc_price( $max_reg_price ) ) );
348 }
349 }
350 } elseif ( 'bundle' === $product_type && method_exists( $product, 'get_bundle_price' ) ) {
351 /*
352 * A bundle's own price is only its container base price, which is 0 whenever the
353 * bundled items are priced individually. Use the bundle's min/max price instead so
354 * the selector shows what the customer would actually pay, as it does for variable
355 * products. Guarded on the method so this stays inert without Product Bundles.
356 */
357 $min_price = $product->get_bundle_price( 'min', false );
358 $max_price = $product->get_bundle_price( 'max', false );
359
360 if ( '' !== $min_price && null !== $min_price ) {
361 $original_price = html_entity_decode( wp_strip_all_tags( wc_price( $min_price ) ) );
362
363 if ( '' !== $max_price && null !== $max_price && (float) $max_price !== (float) $min_price ) {
364 $original_price .= ' – ' . html_entity_decode( wp_strip_all_tags( wc_price( $max_price ) ) );
365 }
366 }
367 } else {
368 $price = $product->get_price();
369 $original_price = ! empty( $price ) ? $price : $product->get_regular_price();
370 $original_price = html_entity_decode( wp_strip_all_tags( wc_price( $original_price ) ) );
371 }
372
373 return $original_price;
374 }
375 }
376