← 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 | } |