PluginProbe
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster / 2.8.0
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster v2.8.0
2.8.0 2.7.0 2.6.0 trunk 1.1.0 1.1.4 1.2.1 1.2.2 1.2.3 1.2.5 1.2.9 1.3.1 1.3.2 1.5.0 1.5.1 1.5.4 1.5.7 1.5.8 1.5.9 1.6.2 1.6.5 1.6.8 1.7.2 1.7.4 1.7.5 All 44 releases
← All changes | includes/elementor/modules/checkout/classes/global-hooks.php +386 -51 1.7.2 → 2.8.0 View file →
@@ -4,8 +4,10 @@
4 4
5 5 defined( 'ABSPATH' ) || exit;
6 6
7 7 use Sellkit\Elementor\Modules\Checkout\Classes\{ Local_Hooks, Helper };
8 +use Sellkit\Funnel\Analytics\Data_Updater;
9 +use Sellkit\Funnel\Contacts\Base_Contacts;
8 10 use Sellkit\Funnel\Steps\Checkout;
9 11 use Sellkit_Funnel;
10 12
11 13 /**
@@ -14,12 +16,20 @@
14 16 * @SuppressWarnings(PHPMD.UnusedFormalParameter)
15 17 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
16 18 * @SuppressWarnings(PHPMD.TooManyMethods)
17 19 * @SuppressWarnings(PHPMD.NPathComplexity)
20 + * @SuppressWarnings(PHPMD.ExcessiveClassLength)
18 21 * @since 1.1.0
19 22 */
20 23 class Global_Hooks {
21 24 /**
25 + * Helper id during checkout process.
26 + *
27 + * @var int
28 + */
29 + private $helper_id = 0;
30 +
31 + /**
22 32 * Create instance of class without construct.
23 33 *
24 34 * @since 1.1.0
25 35 * @return object
@@ -58,8 +68,12 @@
58 68
59 69 // Save user defined fields.
60 70 add_action( 'woocommerce_checkout_update_order_meta', [ $this, 'save_user_defined_fields' ] );
61 71
72 + // Display user defined fields in order edit page.
73 + add_action( 'woocommerce_admin_order_data_after_billing_address', [ $this, 'add_item_to_order_details' ], 10, 1 );
74 + add_action( 'woocommerce_admin_order_data_after_shipping_address', [ $this, 'add_item_to_order_details' ], 10, 1 );
75 +
62 76 // Add custom shipping field to email.
63 77 add_filter( 'woocommerce_order_get_formatted_shipping_address', [ $this, 'attach_user_shipping_fields_to_order_email' ], 10, 3 );
64 78
65 79 // Add custom billing field to email.
@@ -79,8 +93,20 @@
79 93 add_filter( 'sellkit-shipping-methods-choosen-method', [ $this, 'sellkit_default_shipping_method' ], 10, 1 );
80 94
81 95 // Modify discounted products.
82 96 add_action( 'woocommerce_before_calculate_totals', [ $this, 'before_cart_calculate' ], 999 );
97 +
98 + // Allow viewing of Checkout page in the Editor with an empty cart.
99 + $action = isset( $_REQUEST['action'] ) ? filter_var( $_REQUEST['action'], FILTER_SANITIZE_FULL_SPECIAL_CHARS ) : ''; // phpcs:ignore
100 + $elementor_preview = isset( $_REQUEST['elementor-preview'] ) ? filter_var( $_REQUEST['elementor-preview'], FILTER_SANITIZE_FULL_SPECIAL_CHARS ) : ''; // phpcs:ignore
101 +
102 + if (
103 + ( 'elementor' === $action && is_admin() ) // Elementor Editor.
104 + || 'elementor_ajax' === $action // Elementor Editor Preview - Ajax Render Widget.
105 + || ! empty( $elementor_preview ) // Elementor Editor Preview.
106 + ) {
107 + add_filter( 'woocommerce_checkout_redirect_empty_cart', '__return_false', 5 );
108 + }
83 109 }
84 110
85 111 /**
86 112 * Handle ajax calls.
@@ -246,8 +272,16 @@
246 272 if ( empty( $optimization_data ) ) {
247 273 return;
248 274 }
249 275
276 + $auto_apply_coupon_cookie_value = null;
277 +
278 + if ( isset( $_COOKIE['sellkit_rejected_coupon'] ) ) {
279 + // Get the value of the cookie.
280 + $auto_apply_coupon_cookie_value = sanitize_text_field( wp_unslash( $_COOKIE['sellkit_rejected_coupon'] ) );
281 + $auto_apply_coupon_cookie_value = explode( ',', $auto_apply_coupon_cookie_value );
282 + }
283 +
250 284 // Help to apply funnel discount prices and coupons when changing product quantity.
251 285 self::apply_discounted_prices( wc()->cart, $funnel_id );
252 286 self::apply_discounted_prices( wc()->cart, $funnel_id, 'bumps' );
253 287
@@ -254,8 +288,11 @@
254 288 if ( Checkout::apply_coupon_validation( $optimization_data ) ) {
255 289 WC()->cart->remove_coupons();
256 290
257 291 foreach ( $optimization_data['auto_apply_coupons'] as $auto_apply_coupon ) {
292 + if ( ! empty( $auto_apply_coupon_cookie_value ) && in_array( $auto_apply_coupon['label'], $auto_apply_coupon_cookie_value, true ) ) {
293 + continue;
294 + }
258 295 wc()->cart->add_discount( get_the_title( $auto_apply_coupon['value'] ) );
259 296 }
260 297
261 298 wc_clear_notices();
@@ -270,9 +307,9 @@
270 307 */
271 308 public function add_shipping_total_to_review_order() {
272 309 ?>
273 310 <tr class="sellkit-shipping-total">
274 - <th><?php echo __( 'Shipping', 'sellkit' ); ?></th>
311 + <th><?php echo esc_html__( 'Shipping', 'sellkit' ); ?></th>
275 312 <td>
276 313 <?php
277 314 $price = WC()->cart->get_shipping_total();
278 315
@@ -279,9 +316,9 @@
279 316 if ( WC()->cart->display_prices_including_tax() ) {
280 317 $price = WC()->cart->get_shipping_total() + WC()->cart->shipping_tax_total;
281 318 }
282 319
283 - echo wc_price( $price );
320 + echo wp_kses_post( wc_price( $price ) );
284 321 ?>
285 322 </td>
286 323 </tr>
287 324 <?php
@@ -806,8 +843,9 @@
806 843 * Modify cart contents using bundled products.
807 844 *
808 845 * @since 1.1.0
809 846 * @return void
847 + * @SuppressWarnings(PHPMD.CyclomaticComplexity)
810 848 */
811 849 public function sellkit_checkout_modify_cart_by_bundle_products() {
812 850 $qty = filter_input( INPUT_POST, 'qty', FILTER_SANITIZE_NUMBER_INT );
813 851 $id = filter_input( INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT );
@@ -827,11 +865,31 @@
827 865 wp_send_json_success();
828 866 }
829 867
830 868 if ( 'radio' === $type ) {
831 - $step_data = get_post_meta( $checkout_id, 'step_data', true );
832 - $products = $step_data['data']['products']['list'];
833 - $selected = $products[ $id ];
869 + $step_data = get_post_meta( $checkout_id, 'step_data', true );
870 + $products = $step_data['data']['products']['list'];
871 + $reset_cart = isset( $step_data['data']['products']['reset_cart'] ) ? $step_data['data']['products']['reset_cart'] : 'true';
872 +
873 + if ( 'true' === $reset_cart ) {
874 + foreach ( WC()->cart->get_cart() as $item ) {
875 + if ( empty( $item['product_id'] ) ) {
876 + continue;
877 + }
878 +
879 + if ( array_key_exists( $item['product_id'], $products ) ) {
880 + continue;
881 + }
882 +
883 + $products[ $item['product_id'] ] = [
884 + 'quantity' => $item['quantity'],
885 + 'discount' => '',
886 + 'discountType' => 'fixed',
887 + ];
888 + }
889 + }
890 +
891 + $selected = ! empty( $products[ $id ] ) ? $products[ $id ] : [];
834 892 $real_quantity = ( empty( $selected['quantity'] ) ) ? 1 : $selected['quantity'];
835 893
836 894 if ( (int) $real_quantity !== (int) $qty ) {
837 895 wp_send_json_error( esc_html__( 'Oh do not be tricky.', 'sellkit' ) );
@@ -886,8 +944,9 @@
886 944 * @param int $checkout_id_ajax checkout id.
887 945 * @param string $source source of products, bump or default products to be checked.
888 946 * @param array $upsell_data upsell product data coming from popup.
889 947 * @since 1.2.8
948 + * @SuppressWarnings(PHPMD.CyclomaticComplexity)
890 949 */
891 950 public static function apply_discounted_prices( $cart = [], $checkout_id_ajax = null, $source = 'default', $upsell_data = [] ) {
892 951 $checkout_id = get_queried_object_id();
893 952
@@ -909,9 +968,9 @@
909 968
910 969 if (
911 970 'default' === $source &&
912 971 array_key_exists( 'data', $step_data ) &&
913 - array_key_exists( 'list', $step_data['data']['products'] )
972 + ( ! empty( $step_data['data']['products'] ) && array_key_exists( 'list', $step_data['data']['products'] ) )
914 973 ) {
915 974 $products = $step_data['data']['products']['list'];
916 975 }
917 976
@@ -922,9 +981,14 @@
922 981 if ( ! array_key_exists( 'products', $bump['data'] ) ) {
923 982 continue;
924 983 }
925 984
926 - $key = array_key_first( $bump['data']['products']['list'] );
985 + $key = ! empty( $bump['data']['products']['list'] ) ? array_key_first( $bump['data']['products']['list'] ) : null;
986 +
987 + if ( empty( $key ) ) {
988 + continue;
989 + }
990 +
927 991 $products[ $key ] = $bump['data']['products']['list'][ $key ];
928 992 }
929 993 }
930 994
@@ -947,10 +1011,10 @@
947 1011 }
948 1012
949 1013 foreach ( $products as $product_id => $product_details ) {
950 1014 if ( $item_id === $product_id ) {
951 - $discount_type = $product_details['discountType'];
952 - $discount_value = $product_details['discount'];
1015 + $discount_type = isset( $product_details['discountType'] ) ? $product_details['discountType'] : '';
1016 + $discount_value = isset( $product_details['discount'] ) ? $product_details['discount'] : '';
953 1017
954 1018 $price = Helper::calculate_discount( $item_id, $discount_type, $discount_value );
955 1019 }
956 1020 }
@@ -958,9 +1022,9 @@
958 1022 if ( false !== $price ) {
959 1023 $details['data']->set_price( $price );
960 1024 }
961 1025
962 - $final_price += $details['data']->get_price( $price ) * $details['quantity'];
1026 + $final_price += $details['data']->get_price() * $details['quantity'];
963 1027 }
964 1028
965 1029 self::modify_products_price_before_woo_apply_discounts( $final_price );
966 1030 }
@@ -1093,8 +1157,9 @@
1093 1157 /**
1094 1158 * Recalculate checkout and cart prices.
1095 1159 *
1096 1160 * @since 1.2.8
1161 + * @SuppressWarnings(PHPMD.CyclomaticComplexity)
1097 1162 */
1098 1163 public function before_cart_calculate() {
1099 1164 // More than twice isn't necessary. this is called multiple times by WooCommerce.
1100 1165 if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 && wp_doing_ajax() ) {
@@ -1100,43 +1165,46 @@
1100 1165 if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 && wp_doing_ajax() ) {
1101 1166 return;
1102 1167 }
1103 1168
1104 - // Gather checkout id.
1105 - $checkout_id = get_queried_object_id();
1169 + $checkout_id = self::get_valid_checkout_id();
1106 1170
1107 - if ( wp_doing_ajax() ) {
1108 - // First try to catch id using our ajax call.
1109 - $checkout_id = filter_input( INPUT_POST, 'related_checkout', FILTER_SANITIZE_NUMBER_INT );
1171 + if ( 0 === $checkout_id ) {
1172 + return;
1173 + }
1110 1174
1111 - // Second try to catch id using woocommerce ajax.
1112 - if ( empty( $checkout_id ) ) {
1113 - $checkout_id = filter_input( INPUT_POST, 'sellkit_current_page_id', FILTER_SANITIZE_NUMBER_INT );
1114 - }
1175 + // Apply funnel prices.
1176 + self::apply_discounted_prices( wc()->cart, $checkout_id );
1177 + self::apply_discounted_prices( wc()->cart, $checkout_id, 'bumps' );
1115 1178
1116 - // Catch id after pressing place order button. none of above methods worked.
1117 - if ( empty( $checkout_id ) ) {
1118 - $data = filter_input( INPUT_POST, 'post_data', FILTER_DEFAULT );
1179 + // Apply upsell discount.
1180 + $sellkit_upsell_ids = null;
1181 + if ( isset( $_POST['woocommerce-process-checkout-nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['woocommerce-process-checkout-nonce'] ) ), 'woocommerce-process_checkout' ) ) {
1182 + $sellkit_upsell_ids = isset( $_POST['sellkit_upsell_ids'] ) ? sanitize_textarea_field( wp_unslash( $_POST['sellkit_upsell_ids'] ) ) : null;
1183 + }
1119 1184
1120 - parse_str( $data, $data );
1185 + if ( ! empty( $sellkit_upsell_ids ) ) {
1186 + $upsell_ids = explode( ',', $sellkit_upsell_ids );
1187 + $list = self::upsell_downsell_ids_of_funnel( $checkout_id );
1121 1188
1122 - if ( array_key_exists( 'sellkit_current_page_id', $data ) ) {
1123 - $checkout_id = $data['sellkit_current_page_id'];
1189 + if ( ! empty( $list ) ) {
1190 + foreach ( $upsell_ids as $id ) {
1191 +
1192 + if ( in_array( $id, $list, true ) ) {
1193 + self::applay_upsell_downsell_discount( $id );
1194 + }
1124 1195 }
1125 1196 }
1126 1197 }
1127 1198
1128 - // Empty id ? no action.
1129 - if ( empty( $checkout_id ) ) {
1130 - return;
1199 + $auto_apply_coupon_cookie_value = null;
1200 +
1201 + if ( isset( $_COOKIE['sellkit_rejected_coupon'] ) ) {
1202 + // Get the value of the cookie.
1203 + $auto_apply_coupon_cookie_value = sanitize_text_field( wp_unslash( $_COOKIE['sellkit_rejected_coupon'] ) );
1204 + $auto_apply_coupon_cookie_value = explode( ',', $auto_apply_coupon_cookie_value );
1131 1205 }
1132 1206
1133 - $checkout_id = (int) $checkout_id;
1134 -
1135 - // Apply funnel prices.
1136 - self::apply_discounted_prices( wc()->cart, $checkout_id );
1137 - self::apply_discounted_prices( wc()->cart, $checkout_id, 'bumps' );
1138 -
1139 1207 // We should re apply coupons after each price changes. to make sure everything is correct.
1140 1208 $optimization_data = ! empty( $funnel_data['data']['optimization'] ) ? $funnel_data['data']['optimization'] : '';
1141 1209
1142 1210 if ( Checkout::apply_coupon_validation( $optimization_data ) ) {
@@ -1142,8 +1210,11 @@
1142 1210 if ( Checkout::apply_coupon_validation( $optimization_data ) ) {
1143 1211 WC()->cart->remove_coupons();
1144 1212
1145 1213 foreach ( $optimization_data['auto_apply_coupons'] as $auto_apply_coupon ) {
1214 + if ( ! empty( $auto_apply_coupon_cookie_value ) && in_array( $auto_apply_coupon['label'], $auto_apply_coupon_cookie_value, true ) ) {
1215 + continue;
1216 + }
1146 1217 wc()->cart->add_discount( get_the_title( $auto_apply_coupon['value'] ) );
1147 1218 }
1148 1219
1149 1220 wc_clear_notices();
@@ -1150,8 +1221,109 @@
1150 1221 }
1151 1222 }
1152 1223
1153 1224 /**
1225 + * Extract upsell and downsell ides from funnel.
1226 + *
1227 + * @since 2.8.0
1228 + *
1229 + * @param int $checkout_id id of checkout.
1230 + *
1231 + * @return array
1232 + */
1233 + private static function upsell_downsell_ids_of_funnel( $checkout_id ) {
1234 + $step_data = get_post_meta( $checkout_id, 'step_data', true );
1235 + $funnel_id = $step_data['funnel_id'];
1236 + $nodes = get_post_meta( $funnel_id, 'nodes', true );
1237 + $ids = [];
1238 +
1239 + foreach ( $nodes as $node ) {
1240 + if ( isset( $node['type'] ) && in_array( $node['type']['key'], [ 'upsell', 'downsell' ], true ) ) {
1241 + $ids[] = $node['page_id'];
1242 + }
1243 + }
1244 +
1245 + return $ids;
1246 + }
1247 +
1248 + /**
1249 + * Apply discount to upsell or downsell.
1250 + *
1251 + * @param int $id id of checkout.
1252 + *
1253 + * @since 2.8.0
1254 + */
1255 + private static function applay_upsell_downsell_discount( $id ) {
1256 + $upsell_data = get_post_meta( $id, 'step_data', true );
1257 + $product = $upsell_data['data']['products']['list'];
1258 +
1259 + foreach ( $product as $key => $value ) {
1260 + foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
1261 + $product = $cart_item['data'];
1262 + $product_id = $product->get_id();
1263 +
1264 + if ( $key === $product_id ) {
1265 + $discount_type = isset( $value['discountType'] ) ? $value['discountType'] : '';
1266 + $discount_value = isset( $value['discount'] ) ? $value['discount'] : '';
1267 +
1268 + $price = Helper::calculate_discount( $product_id, $discount_type, $discount_value );
1269 + $product->set_price( $price );
1270 + }
1271 + }
1272 + }
1273 + }
1274 +
1275 + /**
1276 + * Get the checkout step associated with the current visitor.
1277 + *
1278 + * @since 2.8.0
1279 + *
1280 + * @return int
1281 + */
1282 + private static function get_valid_checkout_id() {
1283 + $checkout_id = get_queried_object_id();
1284 +
1285 + if ( wp_doing_ajax() ) {
1286 + $checkout_id = filter_input( INPUT_POST, 'related_checkout', FILTER_SANITIZE_NUMBER_INT );
1287 +
1288 + if ( empty( $checkout_id ) ) {
1289 + $checkout_id = filter_input( INPUT_POST, 'sellkit_current_page_id', FILTER_SANITIZE_NUMBER_INT );
1290 + }
1291 +
1292 + if ( empty( $checkout_id ) ) {
1293 + $data = filter_input( INPUT_POST, 'post_data', FILTER_DEFAULT );
1294 +
1295 + if ( ! empty( $data ) ) {
1296 + parse_str( $data, $data );
1297 + }
1298 +
1299 + if ( is_array( $data ) && array_key_exists( 'sellkit_current_page_id', $data ) ) {
1300 + $checkout_id = $data['sellkit_current_page_id'];
1301 + }
1302 + }
1303 + }
1304 +
1305 + $checkout_id = absint( $checkout_id );
1306 + $step_data = get_post_meta( $checkout_id, 'step_data', true );
1307 +
1308 + if (
1309 + 0 === $checkout_id ||
1310 + 'publish' !== get_post_status( $checkout_id ) ||
1311 + ! is_array( $step_data ) ||
1312 + ! isset( $step_data['type']['key'] ) ||
1313 + 'checkout' !== $step_data['type']['key']
1314 + ) {
1315 + return 0;
1316 + }
1317 +
1318 + if ( wp_doing_ajax() && ! function_exists( 'WC' ) ) {
1319 + return 0;
1320 + }
1321 +
1322 + return $checkout_id;
1323 + }
1324 +
1325 + /**
1154 1326 * Check sellkit step through ajax.
1155 1327 * And in order decide to show upsell or downsell steps through a popup.
1156 1328 *
1157 1329 * @since 1.6.2
@@ -1161,16 +1333,24 @@
1161 1333 $funnel = new Sellkit_Funnel( $step );
1162 1334 $next_step = $funnel->next_step_data;
1163 1335 $popups = [ 'upsell', 'downsell' ];
1164 1336
1337 + $funnel->next_step_data['type'] = (array) $funnel->next_step_data['type'];
1338 +
1165 1339 if ( 'decision' === $funnel->next_step_data['type']['key'] ) {
1166 - $this->take_care_of_decision_step( $funnel->next_step_data['page_id'] );
1340 + $page_id = isset( $funnel->next_step_data['page_id'] ) ? apply_filters( 'wpml_object_id', $funnel->next_step_data['page_id'], 'sellkit_step', true ) : 0; // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML API.
1341 +
1342 + $this->helper_id = $step;
1343 + $this->take_care_of_decision_step( $page_id, $funnel->funnel_id );
1167 1344 return;
1168 1345 }
1169 1346
1170 1347 if ( in_array( $funnel->next_step_data['type']['key'], $popups, true ) ) {
1348 +
1349 + $next_step = $this->is_set_upsell_product( $next_step );
1350 +
1171 1351 wp_send_json_success( [
1172 - 'next_id' => $next_step['page_id'],
1352 + 'next_id' => apply_filters( 'wpml_object_id', $next_step['page_id'], 'sellkit_step', true ), // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML API.
1173 1353 'next_type' => $next_step['type']['key'],
1174 1354 ] );
1175 1355 }
1176 1356 }
@@ -1175,14 +1355,51 @@
1175 1355 }
1176 1356 }
1177 1357
1178 1358 /**
1359 + * Check the upsell or downsell have product or not, ignore upsell/downsell step if product doesn't set.
1360 + *
1361 + * @param array $next_step Next step data.
1362 + * @since 2.6.0
1363 + */
1364 + private function is_set_upsell_product( $next_step ) {
1365 + if ( isset( $next_step['type']['key'] ) && ! in_array( $next_step['type']['key'], [ 'upsell', 'downsell' ], true ) ) {
1366 + return $next_step;
1367 + }
1368 +
1369 + $products = isset( $next_step['data'] ) ? $next_step['data']['products'] : [];
1370 + $list = isset( $products['list'] ) ? $products['list'] : [];
1371 +
1372 + if ( empty( $list ) ) {
1373 + $funnel = isset( $next_step['page_id'] ) ? new Sellkit_Funnel( $next_step['page_id'] ) : null;
1374 + $next_step = ! empty( $funnel ) ? $funnel->next_no_step_data : [];
1375 +
1376 + if ( empty( $next_step ) && ! empty( $funnel ) ) {
1377 + return $funnel->end_node_step_data;
1378 + }
1379 +
1380 + if ( isset( $next_step['type']['key'] ) && in_array( $next_step['type']['key'], [ 'upsell', 'downsell' ], true ) ) {
1381 +
1382 + $next_step = $this->is_set_upsell_product( $next_step );
1383 + }
1384 + }
1385 +
1386 + return $next_step;
1387 + }
1388 +
1389 + /**
1179 1390 * Gets step id before the decision step and return result.
1180 1391 *
1181 1392 * @param int $step_id id of the step before decision step.
1393 + * @param int $funnel_id id of the funnel.
1182 1394 * @since 1.6.2
1183 1395 */
1184 - private function take_care_of_decision_step( $step_id ) {
1396 + private function take_care_of_decision_step( $step_id, $funnel_id = null ) {
1397 + // Failed decision step due to no page id should be checked a little different.
1398 + if ( empty( $step_id ) && ! empty( $funnel_id ) ) {
1399 + $this->check_decision_step_with_no_page_id();
1400 + }
1401 +
1185 1402 $funnel = new Sellkit_Funnel( $step_id );
1186 1403 $conditions = ! empty( $funnel->current_step_data['data']['conditions'] ) ? $funnel->current_step_data['data']['conditions'] : [];
1187 1404 $is_valid = sellkit_conditions_validation( $conditions );
1188 1405 $next_step = $funnel->next_no_step_data;
@@ -1190,21 +1407,51 @@
1190 1407 if ( $is_valid ) {
1191 1408 $next_step = $funnel->next_step_data;
1192 1409 }
1193 1410
1411 + $next_step = $this->is_set_upsell_product( $next_step );
1412 + $next_step['type'] = (array) $next_step['type'];
1413 +
1194 1414 if ( 'decision' === $next_step['type']['key'] ) {
1195 - $this->take_care_of_decision_step( $next_step['page_id'] );
1415 + $this->take_care_of_decision_step( apply_filters( 'wpml_object_id', $next_step['page_id'], 'sellkit_step', true ) ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML API.
1196 1416
1197 1417 return;
1198 1418 }
1199 1419
1200 1420 wp_send_json_success( [
1201 - 'next_id' => $next_step['page_id'],
1421 + 'next_id' => apply_filters( 'wpml_object_id', $next_step['page_id'], 'sellkit_step', true ), // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML API.
1202 1422 'next_type' => $next_step['type']['key'],
1203 1423 ] );
1204 1424 }
1205 1425
1206 1426 /**
1427 + * Decision next step directly using funnel data.
1428 + *
1429 + * @since 1.8.6
1430 + */
1431 + private function check_decision_step_with_no_page_id() {
1432 + $funnel = new Sellkit_Funnel( $this->helper_id );
1433 + $decicion_data = $funnel->next_step_data;
1434 + $conditions = $decicion_data['data']['conditions'];
1435 + $funnel_data = get_post_meta( $funnel->funnel_id, 'nodes', true );
1436 + $next_no = 'none' !== $decicion_data['targets'][1]['nodeId'] ? $funnel_data[ $decicion_data['targets'][1]['nodeId'] ] : $funnel->end_node_step_data;
1437 + $next_yes = 'none' !== $decicion_data['targets'][0]['nodeId'] ? $funnel_data[ $decicion_data['targets'][0]['nodeId'] ] : $funnel->end_node_step_data;
1438 + $is_valid = sellkit_conditions_validation( $conditions );
1439 + $next_step = $next_no;
1440 +
1441 + if ( $is_valid ) {
1442 + $next_step = $next_yes;
1443 + }
1444 +
1445 + $next_step = $this->is_set_upsell_product( $next_step );
1446 +
1447 + wp_send_json_success( [
1448 + 'next_id' => apply_filters( 'wpml_object_id', $next_step['page_id'], 'sellkit_step', true ), // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML API.
1449 + 'next_type' => $next_step['type']['key'],
1450 + ] );
1451 + }
1452 +
1453 + /**
1207 1454 * Perform upsell popup accept button.
1208 1455 *
1209 1456 * @since 1.6.2
1210 1457 */
@@ -1224,29 +1471,62 @@
1224 1471 wp_send_json_error( esc_html__( 'Empty Step Data.', 'sellkit' ) );
1225 1472 }
1226 1473
1227 1474 // Add product to cart.
1228 - $product_id = array_key_first( $upsell_data['data']['products']['list'] );
1229 - $qty = $upsell_data['data']['products']['list'][ $product_id ]['quantity'];
1475 + $product_id = '';
1476 + $qty = '';
1230 1477
1478 + if (
1479 + is_array( $upsell_data['data'] ) &&
1480 + array_key_exists( 'products', $upsell_data['data'] ) &&
1481 + ! empty( $upsell_data['data']['products']['list'] )
1482 + ) {
1483 + $product_id = array_key_first( $upsell_data['data']['products']['list'] );
1484 + $qty = ! empty( $upsell_data['data']['products']['list'][ $product_id ] ) ? $upsell_data['data']['products']['list'][ $product_id ]['quantity'] : 0;
1485 + }
1486 +
1231 1487 if ( empty( $qty ) ) {
1232 1488 $qty = 1;
1233 1489 }
1234 1490
1235 - WC()->cart->add_to_cart( $product_id, $qty );
1236 - // Take care of cart and applied discounts.
1237 - self::apply_discounted_prices( WC()->cart, $checkout_id, 'upsell', $upsell_data );
1238 - self::make_changes_after_cart_item_edit( $checkout_id );
1491 + $added_product_hash = '';
1492 +
1493 + if ( ! in_array( $product_id, array_column( WC()->cart->get_cart(), 'product_id' ), true ) ) {
1494 + $added_product_hash = WC()->cart->add_to_cart( $product_id, $qty );
1495 +
1496 + // Take care of cart and applied discounts.
1497 + self::apply_discounted_prices( WC()->cart, $checkout_id, 'upsell', $upsell_data );
1498 + self::make_changes_after_cart_item_edit( $checkout_id );
1499 + }
1500 +
1239 1501 // Response.
1240 - $funnel = new Sellkit_Funnel( $upsell_id );
1241 - $next_step = $funnel->next_step_data;
1502 + $funnel = new Sellkit_Funnel( $upsell_id );
1503 + $next_step = $funnel->next_step_data;
1504 + $next_step = $this->is_set_upsell_product( $next_step );
1505 + $next_step['type'] = ! empty( $next_step['type'] ) ? (array) $next_step['type'] : null;
1242 1506
1507 + if ( 'upsell' === $funnel->current_step_data['type']['key'] && isset( WC()->cart->cart_contents[ $added_product_hash ] ) ) {
1508 + $analytics_updater = new Data_Updater();
1509 + $total = empty( WC()->cart->cart_contents[ $added_product_hash ]['line_total'] ) ? 0 : WC()->cart->cart_contents[ $added_product_hash ]['line_total'];
1510 +
1511 + $analytics_updater->set_funnel_id( $funnel->funnel_id );
1512 + $analytics_updater->add_new_upsell_revenue_log( $total );
1513 + }
1514 +
1515 + $contact_data = [
1516 + 'key' => $funnel->current_step_data['type']['key'],
1517 + 'page_id' => $funnel->current_step_data['page_id'],
1518 + ];
1519 +
1520 + Base_Contacts::step_is_passed( $contact_data );
1521 +
1243 1522 // Step with empty data, will be redirected to thankyou page.
1244 - if ( empty( $next_step ) ) {
1523 + if ( empty( $next_step ) || empty( $next_step['type'] ) ) {
1245 1524 wp_send_json_success(
1246 1525 [
1247 1526 'next_id' => $funnel->end_node_step_data['page_id'],
1248 1527 'next_type' => $funnel->end_node_step_data['type']['key'],
1528 + 'upsell_ids' => $upsell_id,
1249 1529 ]
1250 1530 );
1251 1531 }
1252 1532
@@ -1255,10 +1535,11 @@
1255 1535 return;
1256 1536 }
1257 1537
1258 1538 $response = [
1259 - 'next_id' => $next_step['page_id'],
1539 + 'next_id' => apply_filters( 'wpml_object_id', $next_step['page_id'], 'sellkit_step', true ), // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML API.
1260 1540 'next_type' => $next_step['type']['key'],
1541 + 'upsell_ids' => $upsell_id,
1261 1542 ];
1262 1543
1263 1544 wp_send_json_success( $response );
1264 1545 }
@@ -1276,11 +1557,16 @@
1276 1557 }
1277 1558
1278 1559 $funnel = new Sellkit_Funnel( $upsell_id );
1279 1560 $next_step = $funnel->next_no_step_data;
1561 + $next_step = $this->is_set_upsell_product( $next_step );
1280 1562
1563 + if ( $next_step ) {
1564 + $next_step['type'] = (array) $next_step['type'];
1565 + }
1566 +
1281 1567 // Step with empty data, will be redirected to thankyou page.
1282 - if ( empty( $next_step ) ) {
1568 + if ( empty( $next_step ) || empty( $next_step['type'] ) ) {
1283 1569 wp_send_json_success(
1284 1570 [
1285 1571 'next_id' => $funnel->end_node_step_data['page_id'],
1286 1572 'next_type' => $funnel->end_node_step_data['type']['key'],
@@ -1293,11 +1579,60 @@
1293 1579 return;
1294 1580 }
1295 1581
1296 1582 $response = [
1297 - 'next_id' => $next_step['page_id'],
1583 + 'next_id' => apply_filters( 'wpml_object_id', $next_step['page_id'], 'sellkit_step', true ), // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML API.
1298 1584 'next_type' => $next_step['type']['key'],
1299 1585 ];
1300 1586
1301 1587 wp_send_json_success( $response );
1588 + }
1589 +
1590 + /**
1591 + * Add new item to the WooCommerce order details on the edit page.
1592 + *
1593 + * @param object $order woocommerce order object.
1594 + * @since 1.8.9
1595 + */
1596 + public function add_item_to_order_details( $order ) {
1597 + $order_id = $order->get_id();
1598 + $custom_fields = get_post_meta( $order_id, 'sellkit_checkout_widget_custom_field_of_order', true );
1599 + $current_action = current_action();
1600 +
1601 + if ( empty( $custom_fields ) || ! is_array( $custom_fields ) ) {
1602 + return;
1603 + }
1604 +
1605 + foreach ( $custom_fields as $field ) {
1606 + $key_name = $field['key_name'];
1607 + $label = $field['label'];
1608 +
1609 + // For old users data.
1610 + if ( empty( $label ) ) {
1611 + $label = $key_name;
1612 + $label = str_replace( '_', ' ', $label );
1613 + $label = str_replace( '-', ' ', $label );
1614 + $label = ucwords( $label );
1615 + }
1616 +
1617 + if (
1618 + 'woocommerce_admin_order_data_after_shipping_address' === $current_action &&
1619 + 'shipping' === substr( $key_name, 0, 8 )
1620 + ) {
1621 + ?>
1622 + <h3><?php echo esc_html( $label ); ?></h3>
1623 + <div><?php echo esc_html( get_post_meta( $order_id, $key_name, true ) ); ?></div>
1624 + <?php
1625 + }
1626 +
1627 + if (
1628 + 'woocommerce_admin_order_data_after_billing_address' === $current_action &&
1629 + 'billing' === substr( $key_name, 0, 7 )
1630 + ) {
1631 + ?>
1632 + <h3><?php echo esc_html( $label ); ?></h3>
1633 + <div><?php echo esc_html( get_post_meta( $order_id, $key_name, true ) ); ?></div>
1634 + <?php
1635 + }
1636 + }
1302 1637 }
1303 1638 }