plugin = $plugin; } /** Register admin and runtime behavior. */ public function register() { add_action( 'admin_menu', array( $this, 'add_menu' ) ); add_action( 'admin_init', array( $this, 'register_settings' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) ); add_action( 'add_meta_boxes_product', array( $this, 'add_product_meta_box' ) ); add_action( 'woocommerce_admin_process_product_object', array( $this, 'save_product_controls' ) ); add_filter( 'divi_powers_woo_profile_cart_definition', array( $this, 'filter_profile_cart_definition' ), 20, 2 ); add_filter( 'divi_powers_woo_profile_product_quantity', array( $this, 'filter_profile_product_quantity' ), 20, 4 ); add_filter( 'divi_powers_woo_profile_sync_product_quantity', array( $this, 'filter_sync_profile_product_quantity' ), 20, 5 ); add_filter( 'divi_powers_woo_profile_extra_diagnostics', array( $this, 'filter_profile_diagnostics' ), 20, 2 ); add_filter( 'woocommerce_payment_complete_order_status', array( $this, 'filter_payment_complete_status' ), 20, 3 ); /* * woocommerce_checkout_order_processed fires before WooCommerce finishes * its payment/no-payment branch. Completing the order there can leave the * browser with an AJAX error even though the order was saved. */ add_action( 'woocommerce_payment_complete', array( $this, 'maybe_complete_zero_order_after_checkout' ), 999, 1 ); add_action( 'woocommerce_order_status_processing', array( $this, 'maybe_complete_processing_order' ), 999 ); add_action( 'woocommerce_order_status_changed', array( $this, 'maybe_add_completion_note' ), 30, 4 ); add_filter( 'woocommerce_is_sold_individually', array( $this, 'filter_sold_individually' ), 30, 2 ); add_action( 'woocommerce_before_calculate_totals', array( $this, 'normalize_cart_quantities' ), 30 ); add_filter( 'woocommerce_product_is_visible', array( $this, 'filter_product_visibility' ), 30, 2 ); add_filter( 'woocommerce_is_purchasable', array( $this, 'filter_repeat_purchase_purchasable' ), 30, 2 ); add_action( 'woocommerce_after_checkout_validation', array( $this, 'validate_repeat_purchases' ), 30, 2 ); add_action( 'template_redirect', array( $this, 'capture_buy_now_context' ), -30 ); add_filter( 'woocommerce_add_to_cart_redirect', array( $this, 'filter_add_to_cart_redirect' ), 40, 2 ); add_action( 'woocommerce_add_to_cart', array( $this, 'maybe_isolate_added_item' ), 40, 6 ); add_filter( 'woocommerce_checkout_registration_enabled', array( $this, 'filter_registration_enabled' ), 20 ); add_filter( 'woocommerce_checkout_registration_required', array( $this, 'filter_registration_required' ), 20 ); add_filter( 'woocommerce_checkout_posted_data', array( $this, 'filter_checkout_posted_data' ), 20 ); add_filter( 'woocommerce_order_button_text', array( $this, 'filter_order_button_text' ), 20 ); /* * Free checkout utilities run late so they can safely resolve conflicts * with payment and subscription plugins that alter WooCommerce defaults. */ add_filter( 'woocommerce_cart_needs_payment', array( $this, 'filter_cart_needs_payment' ), 9999, 2 ); add_filter( 'woocommerce_order_needs_payment', array( $this, 'filter_order_needs_payment' ), 9999, 3 ); add_filter( 'woocommerce_available_payment_gateways', array( $this, 'filter_free_checkout_gateways' ), 9999 ); add_filter( 'woocommerce_checkout_fields', array( $this, 'filter_free_checkout_fields' ), 50 ); /* * Flexible Subscriptions exposes this filter for deciding whether a free * subscription checkout must still collect a payment method. */ add_filter( 'fsub/cart/require_payment_on_trial', array( $this, 'filter_flexible_subscription_payment_requirement' ), 9999 ); add_action( 'wp_loaded', array( $this, 'capture_campaign_coupon' ), 5 ); add_action( 'template_redirect', array( $this, 'capture_campaign_coupon' ), -20 ); add_action( 'wp_loaded', array( $this, 'apply_pending_coupon' ), 40 ); add_action( 'template_redirect', array( $this, 'apply_pending_coupon' ), 0 ); add_action( 'woocommerce_add_to_cart', array( $this, 'apply_pending_coupon' ), 90 ); add_action( 'woocommerce_thankyou', array( $this, 'clear_buy_now_context' ), 100 ); add_filter( 'woocommerce_coupons_enabled', array( $this, 'filter_coupons_enabled' ), 30 ); } /** Global defaults. */ public static function defaults() { return array( 'auto_complete_virtual' => 0, 'auto_complete_downloadable' => 0, 'auto_complete_zero' => 0, 'automation_notes' => 1, 'free_checkout_no_payment' => 1, 'free_checkout_trim_billing' => 0, 'free_checkout_button_text' => 'Complete order', 'free_checkout_subscription_mode' => 'auto', 'direct_checkout' => 0, 'force_quantity_one' => 0, 'cart_isolation' => 0, 'account_behavior' => 'default', 'coupon_links' => 1, 'hide_coupon_after_apply' => 1, 'order_button_text' => '', ); } /** Normalized global settings. */ public function settings() { $saved = get_option( self::OPTION_KEY, array() ); return wp_parse_args( $this->sanitize_settings( is_array( $saved ) ? $saved : array() ), self::defaults() ); } public function add_menu() { add_submenu_page( 'divi-powers', __( 'Commerce Utilities', '5d-powers-for-divi' ), __( 'Commerce Utilities', '5d-powers-for-divi' ), 'manage_options', 'divi-powers-commerce-utilities', array( $this, 'render_page' ) ); } public function register_settings() { register_setting( 'divi_powers_commerce_utilities_group', self::OPTION_KEY, array( 'type' => 'array', 'sanitize_callback' => array( $this, 'sanitize_settings' ), 'default' => self::defaults(), ) ); } /** Sanitize global controls. */ public function sanitize_settings( $input ) { $input = is_array( $input ) ? $input : array(); $account = isset( $input['account_behavior'] ) ? sanitize_key( $input['account_behavior'] ) : 'default'; if ( ! in_array( $account, array( 'default', 'guest', 'optional', 'required', 'auto' ), true ) ) { $account = 'default'; } $subscription_mode = isset( $input['free_checkout_subscription_mode'] ) ? sanitize_key( $input['free_checkout_subscription_mode'] ) : 'auto'; if ( ! in_array( $subscription_mode, array( 'auto', 'skip', 'require' ), true ) ) { $subscription_mode = 'auto'; } return array( 'auto_complete_virtual' => empty( $input['auto_complete_virtual'] ) ? 0 : 1, 'auto_complete_downloadable' => empty( $input['auto_complete_downloadable'] ) ? 0 : 1, 'auto_complete_zero' => empty( $input['auto_complete_zero'] ) ? 0 : 1, 'automation_notes' => empty( $input['automation_notes'] ) ? 0 : 1, 'free_checkout_no_payment' => empty( $input['free_checkout_no_payment'] ) ? 0 : 1, 'free_checkout_trim_billing' => empty( $input['free_checkout_trim_billing'] ) ? 0 : 1, 'free_checkout_button_text' => isset( $input['free_checkout_button_text'] ) ? substr( sanitize_text_field( $input['free_checkout_button_text'] ), 0, 80 ) : 'Complete order', 'free_checkout_subscription_mode' => $subscription_mode, 'direct_checkout' => empty( $input['direct_checkout'] ) ? 0 : 1, 'force_quantity_one' => empty( $input['force_quantity_one'] ) ? 0 : 1, 'cart_isolation' => empty( $input['cart_isolation'] ) ? 0 : 1, 'account_behavior' => $account, 'coupon_links' => empty( $input['coupon_links'] ) ? 0 : 1, 'hide_coupon_after_apply' => empty( $input['hide_coupon_after_apply'] ) ? 0 : 1, 'order_button_text' => isset( $input['order_button_text'] ) ? substr( sanitize_text_field( $input['order_button_text'] ), 0, 80 ) : '', ); } public function enqueue_admin_assets() { $page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( 'divi-powers-commerce-utilities' !== $page ) { return; } wp_enqueue_style( 'divi-powers-commerce-utilities', DIVI_POWERS_URL . 'assets/css/commerce-utilities.css', array(), DIVI_POWERS_VERSION ); wp_enqueue_script( 'divi-powers-commerce-utilities', DIVI_POWERS_URL . 'assets/js/commerce-utilities.js', array( 'wp-i18n' ), DIVI_POWERS_VERSION, true ); wp_set_script_translations( 'divi-powers-commerce-utilities', '5d-powers-for-divi' ); } /** Commerce Utilities workspace. */ public function render_page() { if ( ! current_user_can( 'manage_options' ) ) { return; } $settings = $this->settings(); $enabled = $this->runtime_enabled(); $woo = $this->woo_available(); $profiles = array_values( apply_filters( 'divi_powers_funnel_woo_profiles', array() ) ); $products = $this->get_buy_now_products(); ?>

1

render_checkbox( 'auto_complete_virtual', __( 'Auto-complete paid virtual orders', '5d-powers-for-divi' ), __( 'Marks an order complete after confirmed payment when every item is virtual and no item requires fulfillment.', '5d-powers-for-divi' ), $settings ); ?> render_checkbox( 'auto_complete_downloadable', __( 'Auto-complete paid downloadable orders', '5d-powers-for-divi' ), __( 'Allows shipping-free downloadable products to qualify, while physical downloadable products still remain in the normal workflow.', '5d-powers-for-divi' ), $settings ); ?> render_checkbox( 'auto_complete_zero', __( 'Auto-complete zero-total digital orders', '5d-powers-for-divi' ), __( 'Completes eligible one-time free digital orders after WooCommerce finishes checkout. Subscription parent and renewal orders remain controlled by the subscription extension.', '5d-powers-for-divi' ), $settings ); ?> render_checkbox( 'automation_notes', __( 'Add transparent order notes', '5d-powers-for-divi' ), __( 'Records exactly when 5D Powers for Divi completed an eligible order automatically.', '5d-powers-for-divi' ), $settings ); ?>
2

render_checkbox( 'free_checkout_no_payment', __( 'Skip payment methods when the payable total is zero', '5d-powers-for-divi' ), __( 'Removes card and gateway requirements for safe zero-total carts and zero-total order-payment screens. Detected subscriptions with a future non-zero renewal remain protected.', '5d-powers-for-divi' ), $settings ); ?> render_checkbox( 'free_checkout_trim_billing', __( 'Simplify billing fields for free digital orders', '5d-powers-for-divi' ), __( 'On Classic Checkout, keeps name and email while removing company, address, city, state, postcode, country, and phone when the cart is free and requires no shipping.', '5d-powers-for-divi' ), $settings ); ?>
3

render_checkbox( 'direct_checkout', __( 'Send matching Add to Cart clicks directly to checkout', '5d-powers-for-divi' ), __( 'Uses an active Pro checkout profile when one matches; otherwise it opens the standard WooCommerce checkout.', '5d-powers-for-divi' ), $settings ); ?> render_checkbox( 'force_quantity_one', __( 'Force quantity to one', '5d-powers-for-divi' ), __( 'Treats matching products as individually sold unless a Pro checkout profile overrides this setting.', '5d-powers-for-divi' ), $settings ); ?> render_checkbox( 'cart_isolation', __( 'Remove unrelated items from Buy Now carts', '5d-powers-for-divi' ), __( 'Starts a dedicated cart when a customer follows a generated Buy Now link or a matching Pro checkout profile.', '5d-powers-for-divi' ), $settings ); ?>
4

5

render_checkbox( 'coupon_links', __( 'Allow coupon codes in 5D Powers for Divi links', '5d-powers-for-divi' ), __( 'Reads the coupon code from the link and applies it when the customer’s cart is eligible.', '5d-powers-for-divi' ), $settings ); ?> render_checkbox( 'hide_coupon_after_apply', __( 'Hide coupon entry after a campaign coupon is applied', '5d-powers-for-divi' ), __( 'Keeps the applied discount but removes the extra coupon prompt from Classic Checkout.', '5d-powers-for-divi' ), $settings ); ?>
ID, self::PRODUCT_FUNNEL_ONLY_META, true ); $force_one = (bool) get_post_meta( $post->ID, self::PRODUCT_FORCE_ONE_META, true ); $repeat = (bool) get_post_meta( $post->ID, self::PRODUCT_REPEAT_META, true ); $completion = sanitize_key( (string) get_post_meta( $post->ID, self::PRODUCT_COMPLETE_META, true ) ); if ( ! in_array( $completion, array( 'inherit', 'include', 'exclude' ), true ) ) { $completion = 'inherit'; } ?>



get_id() ); if ( ! $id || ! current_user_can( 'edit_post', $id ) ) { return; } $funnel_only = empty( $_POST['divi_powers_funnel_only'] ) ? 0 : 1; $product->update_meta_data( self::PRODUCT_FUNNEL_ONLY_META, $funnel_only ); $product->update_meta_data( self::PRODUCT_FORCE_ONE_META, empty( $_POST['divi_powers_force_quantity_one'] ) ? 0 : 1 ); $product->update_meta_data( self::PRODUCT_REPEAT_META, empty( $_POST['divi_powers_prevent_repeat_purchase'] ) ? 0 : 1 ); $completion = isset( $_POST['divi_powers_auto_complete'] ) ? sanitize_key( wp_unslash( $_POST['divi_powers_auto_complete'] ) ) : 'inherit'; if ( ! in_array( $completion, array( 'inherit', 'include', 'exclude' ), true ) ) { $completion = 'inherit'; } $product->update_meta_data( self::PRODUCT_COMPLETE_META, $completion ); if ( method_exists( $product, 'get_catalog_visibility' ) && method_exists( $product, 'set_catalog_visibility' ) ) { $current = $product->get_catalog_visibility(); $previous = sanitize_key( (string) $product->get_meta( self::PRODUCT_PREVIOUS_VISIBILITY_META, true ) ); if ( $funnel_only ) { if ( 'hidden' !== $current && ! $previous ) { $product->update_meta_data( self::PRODUCT_PREVIOUS_VISIBILITY_META, $current ); } $product->set_catalog_visibility( 'hidden' ); } elseif ( $previous ) { $product->set_catalog_visibility( in_array( $previous, array( 'visible', 'catalog', 'search', 'hidden' ), true ) ? $previous : 'visible' ); $product->delete_meta_data( self::PRODUCT_PREVIOUS_VISIBILITY_META ); } } } /** Preserve a generated Buy Now selection across the clean-route redirect. */ public function capture_buy_now_context() { if ( ! $this->runtime_enabled() || ! function_exists( 'WC' ) || ! WC() || ! WC()->session ) { return; } if ( isset( $_GET['dp_order'], $_GET['key'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended WC()->session->__unset( self::BUY_NOW_PRODUCT_SESSION ); WC()->session->__unset( self::BUY_NOW_QUANTITY_SESSION ); return; } $route = get_query_var( 'dp_checkout_journey' ); $product_id = $this->request_buy_now_product_id( false ); if ( $product_id ) { WC()->session->set( self::BUY_NOW_PRODUCT_SESSION, $product_id ); WC()->session->set( self::BUY_NOW_QUANTITY_SESSION, $this->request_buy_now_quantity( false ) ); } elseif ( $route ) { WC()->session->__unset( self::BUY_NOW_PRODUCT_SESSION ); WC()->session->__unset( self::BUY_NOW_QUANTITY_SESSION ); } } public function clear_buy_now_context() { if ( function_exists( 'WC' ) && WC() && WC()->session ) { WC()->session->__unset( self::BUY_NOW_PRODUCT_SESSION ); WC()->session->__unset( self::BUY_NOW_QUANTITY_SESSION ); } } /** Make generated route links and journey isolation use one coherent cart definition. */ public function filter_profile_cart_definition( $definition, $profile ) { if ( ! $this->runtime_enabled() || ! is_array( $definition ) || ! is_array( $profile ) ) { return $definition; } $product_id = $this->request_buy_now_product_id(); $isolation = $this->effective_bool( 'cart_isolation', $profile ); if ( $product_id ) { if ( $isolation ) { $definition['product_ids'] = array( $product_id ); $definition['cart_behavior'] = 'replace'; } else { $definition['product_ids'] = array_values( array_unique( array_merge( isset( $definition['product_ids'] ) ? $definition['product_ids'] : array(), array( $product_id ) ) ) ); if ( 'preserve' === ( $definition['cart_behavior'] ?? 'preserve' ) ) { $definition['cart_behavior'] = 'append'; } } } elseif ( $isolation && ! empty( $definition['product_ids'] ) ) { $definition['cart_behavior'] = 'replace'; } return $definition; } public function filter_profile_product_quantity( $quantity, $product_id, $profile, $definition ) { $requested = $this->request_buy_now_quantity(); if ( ! $requested ) { $requested = absint( $quantity ); } $requested = max( 1, min( 99, $requested ? $requested : 1 ) ); $product = function_exists( 'wc_get_product' ) ? wc_get_product( absint( $product_id ) ) : null; if ( ( $product && $this->product_force_one( $product ) ) || $this->effective_bool( 'force_quantity_one', is_array( $profile ) ? $profile : null ) ) { return 1; } return $requested; } public function filter_sync_profile_product_quantity( $sync, $product_id, $quantity, $profile, $definition ) { return absint( $this->request_buy_now_product_id() ) === absint( $product_id ) ? true : $sync; } public function filter_profile_diagnostics( $issues, $definition ) { $issues = is_array( $issues ) ? $issues : array(); if ( ! $this->plugin->is_power_enabled( 'commerce_utilities' ) ) { return $issues; } $profile = array( 'definition' => is_array( $definition ) ? $definition : array() ); if ( $this->effective_bool( 'cart_isolation', $profile ) && empty( $definition['product_ids'] ) ) { $issues[] = array( 'severity' => 'warning', 'code' => 'commerce_isolation_products', 'message' => __( 'Commerce cart isolation is enabled, but this profile has no assigned products. Use a generated Buy Now link with a product or assign the dedicated offer products.', '5d-powers-for-divi' ) ); } return $issues; } /** Determine final order status immediately after confirmed payment. */ public function filter_payment_complete_status( $status, $order_id, $order ) { if ( ! $this->runtime_enabled() || 'completed' === $status || ! $this->order_is_eligible_for_completion( $order, false ) ) { return $status; } if ( is_object( $order ) && method_exists( $order, 'update_meta_data' ) ) { $order->update_meta_data( self::AUTO_COMPLETE_REASON_META, 'paid-digital' ); $order->save_meta_data(); } return 'completed'; } /** * Complete eligible non-recurring free digital orders only after * WooCommerce has finished its payment/no-payment processing. */ public function maybe_complete_zero_order_after_checkout( $order_id ) { if ( ! $this->runtime_enabled() || ! function_exists( 'wc_get_order' ) ) { return; } $order = wc_get_order( $order_id ); if ( ! $order || (float) $order->get_total() > 0 || ! $this->order_is_eligible_for_completion( $order, true ) ) { return; } $status = $order->get_status(); if ( in_array( $status, array( 'pending', 'processing' ), true ) ) { $order->update_meta_data( self::AUTO_COMPLETE_REASON_META, 'zero-total-digital' ); $order->save_meta_data(); $order->update_status( 'completed' ); } } public function maybe_complete_processing_order( $order_id ) { if ( ! $this->runtime_enabled() || ! function_exists( 'wc_get_order' ) ) { return; } $order = wc_get_order( $order_id ); if ( ! $order || 'processing' !== $order->get_status() || ! $order->is_paid() || ! $this->order_is_eligible_for_completion( $order, false ) ) { return; } $order->update_meta_data( self::AUTO_COMPLETE_REASON_META, 'paid-digital' ); $order->save_meta_data(); $order->update_status( 'completed' ); } public function maybe_add_completion_note( $order_id, $from, $to, $order ) { if ( 'completed' !== $to || ! is_object( $order ) || ! method_exists( $order, 'get_meta' ) ) { return; } $reason = sanitize_key( (string) $order->get_meta( self::AUTO_COMPLETE_REASON_META, true ) ); if ( ! $reason || $order->get_meta( self::AUTO_COMPLETE_NOTE_META, true ) ) { return; } $profile = $this->profile_for_order( $order ); if ( $this->effective_bool( 'automation_notes', $profile ) && method_exists( $order, 'add_order_note' ) ) { $message = 'zero-total-digital' === $reason ? __( '5D Powers for Divi automatically completed this zero-total digital order.', '5d-powers-for-divi' ) : __( '5D Powers for Divi automatically completed this paid digital order after payment was confirmed.', '5d-powers-for-divi' ); $order->add_order_note( $message ); } $order->update_meta_data( self::AUTO_COMPLETE_NOTE_META, 1 ); $order->save_meta_data(); } private function order_is_eligible_for_completion( $order, $zero_total ) { if ( ! is_object( $order ) || ! method_exists( $order, 'get_items' ) ) { return false; } $status = method_exists( $order, 'get_status' ) ? $order->get_status() : ''; if ( in_array( $status, array( 'on-hold', 'failed', 'cancelled', 'refunded', 'trash' ), true ) ) { return false; } if ( $zero_total ) { if ( (float) $order->get_total() > 0 || ! $this->effective_bool( 'auto_complete_zero', $this->profile_for_order( $order ) ) ) { return false; } } elseif ( ! method_exists( $order, 'is_paid' ) || ! $order->is_paid() ) { return false; } if ( $this->order_has_incompatible_fulfillment( $order ) ) { return false; } $profile = $this->profile_for_order( $order ); $allow_virtual = $this->effective_bool( 'auto_complete_virtual', $profile ); $allow_downloadable = $this->effective_bool( 'auto_complete_downloadable', $profile ); $items = $order->get_items( 'line_item' ); if ( empty( $items ) ) { return false; } foreach ( $items as $item ) { $product = is_object( $item ) && method_exists( $item, 'get_product' ) ? $item->get_product() : null; if ( ! $product ) { return false; } $setting = $this->product_completion_setting( $product ); if ( 'exclude' === $setting ) { return false; } $needs_shipping = method_exists( $product, 'needs_shipping' ) ? $product->needs_shipping() : ! $product->is_virtual(); if ( $needs_shipping ) { return false; } if ( 'include' === $setting ) { continue; } $is_virtual = method_exists( $product, 'is_virtual' ) && $product->is_virtual(); $is_downloadable = method_exists( $product, 'is_downloadable' ) && $product->is_downloadable(); if ( ! ( ( $allow_virtual && $is_virtual ) || ( $allow_downloadable && $is_downloadable ) ) ) { return false; } } return (bool) apply_filters( 'divi_powers_commerce_order_auto_complete_eligible', true, $order, $profile, $zero_total ); } private function order_has_incompatible_fulfillment( $order ) { if ( function_exists( 'wcs_order_contains_subscription' ) && wcs_order_contains_subscription( $order ) ) { return true; } if ( function_exists( 'wcs_order_contains_renewal' ) && wcs_order_contains_renewal( $order ) ) { return true; } foreach ( $order->get_items( 'line_item' ) as $item ) { $product = is_object( $item ) && method_exists( $item, 'get_product' ) ? $item->get_product() : null; /* * Never force-complete a recurring parent or renewal order. Flexible * Subscriptions must finish its own subscription transaction, schedule, * and redirect before any fulfillment automation runs. */ if ( $product && $this->product_is_recurring( $product, $item ) ) { return true; } $type = $product && method_exists( $product, 'get_type' ) ? strtolower( (string) $product->get_type() ) : ''; foreach ( array( 'subscription', 'booking', 'appointment', 'pre_order', 'pre-order', 'deposit', 'rental' ) as $blocked ) { if ( $type && false !== strpos( $type, $blocked ) ) { return true; } } } return (bool) apply_filters( 'divi_powers_commerce_order_has_incompatible_fulfillment', false, $order ); } public function filter_sold_individually( $sold_individually, $product ) { if ( ! $this->runtime_enabled() || ! is_object( $product ) ) { return $sold_individually; } return ( $this->product_force_one( $product ) || $this->effective_bool( 'force_quantity_one', $this->active_profile() ) ) ? true : $sold_individually; } public function normalize_cart_quantities( $cart ) { if ( ! $this->runtime_enabled() || ! is_object( $cart ) || ! method_exists( $cart, 'get_cart' ) ) { return; } $profile = $this->active_profile(); foreach ( $cart->get_cart() as $key => $item ) { $product = isset( $item['data'] ) ? $item['data'] : null; if ( $product && ( $this->product_force_one( $product ) || $this->effective_bool( 'force_quantity_one', $profile ) ) && isset( $item['quantity'] ) && (int) $item['quantity'] !== 1 ) { $cart->set_quantity( $key, 1, false ); } } } public function filter_product_visibility( $visible, $product_id ) { return $this->runtime_enabled() && $this->product_meta_bool( $product_id, self::PRODUCT_FUNNEL_ONLY_META ) ? false : $visible; } public function filter_repeat_purchase_purchasable( $purchasable, $product ) { if ( ! $purchasable || ! $this->runtime_enabled() || ! is_user_logged_in() || ! is_object( $product ) || ! function_exists( 'wc_customer_bought_product' ) ) { return $purchasable; } $product_id = $this->product_parent_aware_id( $product ); if ( ! $this->product_meta_bool( $product_id, self::PRODUCT_REPEAT_META ) ) { return $purchasable; } $user = wp_get_current_user(); return wc_customer_bought_product( $user->user_email, $user->ID, $product_id ) ? false : $purchasable; } public function validate_repeat_purchases( $data, $errors ) { if ( ! $this->runtime_enabled() || ! function_exists( 'WC' ) || ! WC() || ! WC()->cart || ! function_exists( 'wc_customer_bought_product' ) || ! is_object( $errors ) ) { return; } $email = isset( $data['billing_email'] ) ? sanitize_email( $data['billing_email'] ) : ''; $user_id = get_current_user_id(); if ( ! $email && $user_id ) { $email = wp_get_current_user()->user_email; } if ( ! $email ) { return; } foreach ( WC()->cart->get_cart() as $item ) { $product = isset( $item['data'] ) ? $item['data'] : null; if ( ! $product ) { continue; } $product_id = $this->product_parent_aware_id( $product ); if ( $this->product_meta_bool( $product_id, self::PRODUCT_REPEAT_META ) && wc_customer_bought_product( $email, $user_id, $product_id ) ) { $errors->add( 'divi_powers_repeat_purchase', sprintf( /* translators: %s: purchased product name. */ __( 'You have already purchased “%s”. Please sign in with the original account or contact support if you need another license.', '5d-powers-for-divi' ), $product->get_name() ) ); } } } public function filter_add_to_cart_redirect( $url, $adding_to_cart = null ) { if ( ! $this->runtime_enabled() ) { return $url; } $is_buy_now = isset( $_REQUEST['dp_buy_now'] ) || isset( $_REQUEST['dp_direct_checkout'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( ! $is_buy_now && ! $this->settings()['direct_checkout'] ) { return $url; } $product_id = $this->product_id_from_add_request( $adding_to_cart ); $profile = $product_id ? apply_filters( 'divi_powers_woo_profile_for_product', null, $product_id ) : null; if ( is_array( $profile ) && ! empty( $profile['checkout_route_url'] ) ) { if ( ! $is_buy_now ) { return $profile['checkout_route_url']; } $args = array( 'dp_buy_now' => $product_id, 'quantity' => $this->request_buy_now_quantity( false ) ); foreach ( array( 'dp_coupon', 'utm_source', 'utm_campaign' ) as $key ) { if ( isset( $_REQUEST[ $key ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended $request_value = sanitize_text_field( wp_unslash( $_REQUEST[ $key ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( '' !== $request_value ) { $args[ $key ] = $request_value; } } } return add_query_arg( $args, $profile['checkout_route_url'] ); } return function_exists( 'wc_get_checkout_url' ) ? wc_get_checkout_url() : $url; } public function maybe_isolate_added_item( $cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart_item_data ) { if ( ! $this->runtime_enabled() || ! function_exists( 'WC' ) || ! WC() || ! WC()->cart ) { return; } $is_buy_now = isset( $_REQUEST['dp_buy_now'] ) || isset( $_REQUEST['dp_direct_checkout'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended $profile = apply_filters( 'divi_powers_woo_profile_for_product', null, $variation_id ? $variation_id : $product_id ); $is_profile = is_array( $profile ); $definition = $is_profile && isset( $profile['definition'] ) && is_array( $profile['definition'] ) ? $profile['definition'] : array(); $replace_profile_cart = $is_profile && 'replace' === sanitize_key( isset( $definition['cart_behavior'] ) ? $definition['cart_behavior'] : '' ); $isolate_profile_cart = $is_profile && $this->effective_bool( 'cart_isolation', $profile ); $isolate_buy_now = $is_buy_now && $this->effective_bool( 'cart_isolation', $is_profile ? $profile : null ); if ( ! $replace_profile_cart && ! $isolate_profile_cart && ! $isolate_buy_now ) { return; } foreach ( array_keys( WC()->cart->get_cart() ) as $key ) { if ( (string) $key !== (string) $cart_item_key ) { WC()->cart->remove_cart_item( $key ); } } } public function filter_registration_enabled( $enabled ) { $behavior = $this->effective_account_behavior(); if ( 'guest' === $behavior ) { return false; } if ( in_array( $behavior, array( 'optional', 'required', 'auto' ), true ) ) { return true; } return $enabled; } public function filter_registration_required( $required ) { $behavior = $this->effective_account_behavior(); if ( 'required' === $behavior ) { return true; } if ( in_array( $behavior, array( 'guest', 'optional', 'auto' ), true ) ) { return false; } return $required; } public function filter_checkout_posted_data( $data ) { if ( 'auto' === $this->effective_account_behavior() && ! is_user_logged_in() ) { $data['createaccount'] = 1; } return $data; } public function filter_order_button_text( $text ) { if ( ! $this->runtime_enabled() ) { return $text; } $profile = $this->active_profile(); $profile_text = is_array( $profile ) && ! empty( $profile['definition']['commerce_order_button_text'] ) ? $profile['definition']['commerce_order_button_text'] : ''; if ( $profile_text ) { return $profile_text; } $settings = $this->settings(); if ( $this->cart_is_safe_free_checkout() ) { $free_text = $this->effective_free_checkout_button_text( $profile ); if ( $free_text ) { return $free_text; } } return $settings['order_button_text'] ? $settings['order_button_text'] : $text; } /** * WooCommerce normally skips payment for a zero total. This late filter * restores that behavior when another extension has forced payment back on. */ public function filter_cart_needs_payment( $needs_payment, $cart ) { if ( ! $this->runtime_enabled() || ! $this->effective_bool( 'free_checkout_no_payment', $this->active_profile() ) ) { return $needs_payment; } return $this->cart_is_safe_free_checkout( $cart ) ? false : $needs_payment; } /** * Apply the same protection to order-pay and retry screens. */ public function filter_order_needs_payment( $needs_payment, $order, $valid_order_statuses = array() ) { unset( $valid_order_statuses ); if ( ! $this->runtime_enabled() || ! $this->effective_bool( 'free_checkout_no_payment', $this->profile_for_order( $order ) ) ) { return $needs_payment; } return $this->order_is_safe_free_checkout( $order ) ? false : $needs_payment; } /** * Gateway UI fallback for extensions that expose payment choices even after * WooCommerce reports that the cart does not need payment. */ public function filter_free_checkout_gateways( $gateways ) { if ( ! $this->runtime_enabled() || ! is_array( $gateways ) || ! $this->effective_bool( 'free_checkout_no_payment', $this->active_profile() ) ) { return $gateways; } return $this->cart_is_safe_free_checkout() ? array() : $gateways; } /** * Optional Classic Checkout cleanup for free, shipping-free digital orders. */ public function filter_free_checkout_fields( $fields ) { $profile = $this->active_profile(); if ( ! $this->runtime_enabled() || ! $this->effective_bool( 'free_checkout_trim_billing', $profile ) || ! is_array( $fields ) || ! $this->cart_is_safe_free_checkout() || ! $this->cart_is_shipping_free() ) { return $fields; } $remove = array( 'billing_company', 'billing_country', 'billing_address_1', 'billing_address_2', 'billing_city', 'billing_state', 'billing_postcode', 'billing_phone', ); foreach ( $remove as $key ) { unset( $fields['billing'][ $key ] ); } return $fields; } private function cart_is_safe_free_checkout( $cart = null ) { if ( ! $this->runtime_enabled() ) { return false; } if ( ! is_object( $cart ) && function_exists( 'WC' ) && WC() ) { $cart = WC()->cart; } if ( ! is_object( $cart ) ) { return false; } $total = method_exists( $cart, 'get_total' ) ? (float) $cart->get_total( 'edit' ) : (float) ( $cart->total ?? 0 ); if ( $total > 0.00001 ) { return false; } $profile = $this->active_profile(); $has_recurring = $this->cart_has_recurring_item( $cart ); if ( $has_recurring ) { $mode = $this->effective_free_checkout_subscription_mode( $profile ); if ( 'require' === $mode ) { return false; } if ( 'skip' !== $mode ) { /* * Flexible Subscriptions can display a recurring total that is * different from the product's catalog price after recurring * coupons. Trust the calculated recurring cart/coupon result * before falling back to the product's original renewal price. */ if ( ! $this->recurring_cart_total_is_zero( $cart ) && $this->cart_has_future_paid_recurring_item( $cart ) ) { return false; } } } return (bool) apply_filters( 'divi_powers_safe_free_checkout_cart', true, $cart ); } private function order_is_safe_free_checkout( $order ) { if ( ! is_object( $order ) || ! method_exists( $order, 'get_total' ) ) { return false; } if ( (float) $order->get_total() > 0.00001 ) { return false; } $profile = $this->profile_for_order( $order ); $mode = $this->effective_free_checkout_subscription_mode( $profile ); if ( 'require' === $mode && $this->order_has_recurring_item( $order ) ) { return false; } if ( 'auto' === $mode && $this->order_has_recurring_item( $order ) && $this->order_has_future_paid_recurring_item( $order ) ) { return false; } return (bool) apply_filters( 'divi_powers_safe_free_checkout_order', true, $order ); } /** * Flexible Subscriptions can independently request a card for a free trial. * Keep that request only when the configured subscription behavior says the * cart has a future paid renewal. */ public function filter_flexible_subscription_payment_requirement( $require_payment ) { if ( ! $this->runtime_enabled() || ! $this->effective_bool( 'free_checkout_no_payment', $this->active_profile() ) ) { return $require_payment; } if ( $this->cart_is_safe_free_checkout() ) { return false; } return 'require' === $this->effective_free_checkout_subscription_mode( $this->active_profile() ) ? true : $require_payment; } private function cart_is_shipping_free() { if ( ! function_exists( 'WC' ) || ! WC() || ! WC()->cart ) { return false; } if ( method_exists( WC()->cart, 'needs_shipping' ) && WC()->cart->needs_shipping() ) { return false; } foreach ( WC()->cart->get_cart() as $item ) { $product = isset( $item['data'] ) && is_object( $item['data'] ) ? $item['data'] : null; if ( $product && method_exists( $product, 'is_virtual' ) && ! $product->is_virtual() ) { return false; } } return true; } private function cart_has_recurring_item( $cart ) { if ( ! is_object( $cart ) || ! method_exists( $cart, 'get_cart' ) ) { return false; } foreach ( $cart->get_cart() as $item ) { $product = isset( $item['data'] ) && is_object( $item['data'] ) ? $item['data'] : ( function_exists( 'wc_get_product' ) ? wc_get_product( absint( $item['variation_id'] ?? $item['product_id'] ?? 0 ) ) : null ); if ( $product && $this->product_is_recurring( $product, $item ) ) { return true; } } return false; } private function order_has_recurring_item( $order ) { if ( ! is_object( $order ) || ! method_exists( $order, 'get_items' ) ) { return false; } foreach ( $order->get_items() as $item ) { $product = is_object( $item ) && method_exists( $item, 'get_product' ) ? $item->get_product() : null; if ( $product && $this->product_is_recurring( $product, $item ) ) { return true; } } return false; } private function cart_has_future_paid_recurring_item( $cart ) { if ( ! is_object( $cart ) || ! method_exists( $cart, 'get_cart' ) ) { return false; } foreach ( $cart->get_cart() as $item ) { $product = isset( $item['data'] ) && is_object( $item['data'] ) ? $item['data'] : ( function_exists( 'wc_get_product' ) ? wc_get_product( absint( $item['variation_id'] ?? $item['product_id'] ?? 0 ) ) : null ); if ( $product && $this->product_has_future_paid_recurrence( $product, $item ) ) { return true; } } return false; } private function order_has_future_paid_recurring_item( $order ) { if ( ! is_object( $order ) || ! method_exists( $order, 'get_items' ) ) { return false; } foreach ( $order->get_items() as $item ) { $product = is_object( $item ) && method_exists( $item, 'get_product' ) ? $item->get_product() : null; if ( $product && $this->product_has_future_paid_recurrence( $product, $item ) ) { return true; } } return false; } /** * Read a calculated recurring total when a subscription plugin exposes one. * Flexible Subscriptions maintains WooCommerce-Subscriptions-compatible * structures in several checkout paths, while recurring coupons provide a * dependable fallback for a fully discounted renewal. */ private function recurring_cart_total_is_zero( $cart ) { $filtered = apply_filters( 'divi_powers_recurring_cart_total', null, $cart ); if ( null !== $filtered && is_numeric( $filtered ) ) { return (float) $filtered <= 0.00001; } $recurring_carts = array(); if ( method_exists( $cart, 'get_recurring_carts' ) ) { $recurring_carts = $cart->get_recurring_carts(); } elseif ( isset( $cart->recurring_carts ) ) { $recurring_carts = $cart->recurring_carts; } if ( is_array( $recurring_carts ) && $recurring_carts ) { $found = false; foreach ( $recurring_carts as $recurring_cart ) { if ( ! is_object( $recurring_cart ) ) { continue; } $found = true; $total = method_exists( $recurring_cart, 'get_total' ) ? (float) $recurring_cart->get_total( 'edit' ) : (float) ( $recurring_cart->total ?? 0 ); if ( $total > 0.00001 ) { return false; } } if ( $found ) { return true; } } if ( $this->recurring_coupon_zeroes_cart( $cart ) ) { return true; } /* * Some Flexible Subscriptions versions store calculated recurring totals * in WooCommerce session data. Inspect only keys explicitly describing * a recurring/renewal total and ignore unrelated numeric values. */ if ( function_exists( 'WC' ) && WC() && WC()->session && method_exists( WC()->session, 'get_session_data' ) ) { $values = array(); $this->collect_recurring_total_values( WC()->session->get_session_data(), $values ); if ( $values ) { foreach ( $values as $value ) { if ( (float) $value > 0.00001 ) { return false; } } return true; } } return false; } private function recurring_coupon_zeroes_cart( $cart ) { if ( ! method_exists( $cart, 'get_applied_coupons' ) || ! class_exists( 'WC_Coupon' ) ) { return false; } $recurring_base = 0.0; foreach ( $cart->get_cart() as $item ) { $product = isset( $item['data'] ) && is_object( $item['data'] ) ? $item['data'] : null; if ( ! $product || ! $this->product_is_recurring( $product, $item ) ) { continue; } $quantity = max( 1, absint( $item['quantity'] ?? 1 ) ); $price = method_exists( $product, 'get_regular_price' ) ? (float) $product->get_regular_price( 'edit' ) : (float) $product->get_price( 'edit' ); $recurring_base += max( 0, $price ) * $quantity; } $fixed_recurring_discount = 0.0; foreach ( $cart->get_applied_coupons() as $code ) { $coupon = new WC_Coupon( $code ); $type = strtolower( (string) $coupon->get_discount_type() ); $amount = (float) $coupon->get_amount(); $is_recurring = false !== strpos( $type, 'recurr' ) || false !== strpos( $type, 'renew' ); if ( ! $is_recurring ) { foreach ( array( '_fsub_recurring', 'fsub_recurring', '_subscription_recurring', '_wcs_apply_to_renewals' ) as $key ) { $value = strtolower( (string) $coupon->get_meta( $key, true ) ); if ( in_array( $value, array( 'yes', '1', 'true', 'on', 'recurring' ), true ) ) { $is_recurring = true; break; } } } if ( ! $is_recurring ) { continue; } if ( false !== strpos( $type, 'percent' ) && $amount >= 99.999 ) { return true; } if ( false !== strpos( $type, 'fixed' ) ) { $fixed_recurring_discount += $amount; } } return $recurring_base > 0 && $fixed_recurring_discount + 0.00001 >= $recurring_base; } private function collect_recurring_total_values( $value, &$values, $key = '' ) { if ( is_array( $value ) ) { foreach ( $value as $child_key => $child ) { $this->collect_recurring_total_values( $child, $values, (string) $child_key ); } return; } if ( is_numeric( $value ) && preg_match( '/(?:recurr|renew|subscription).*(?:total|amount)|(?:total|amount).*(?:recurr|renew|subscription)/i', $key ) ) { $values[] = (float) $value; } } private function product_is_recurring( $product, $context = null ) { if ( ! is_object( $product ) || ! method_exists( $product, 'get_id' ) ) { return false; } $product_id = absint( $product->get_id() ); $parent_id = method_exists( $product, 'get_parent_id' ) ? absint( $product->get_parent_id() ) : 0; $lookup_id = $parent_id ? $parent_id : $product_id; $is_recurring = false; if ( class_exists( 'WC_Subscriptions_Product' ) && is_callable( array( 'WC_Subscriptions_Product', 'is_subscription' ) ) ) { $is_recurring = (bool) WC_Subscriptions_Product::is_subscription( $product ); } if ( ! $is_recurring && function_exists( 'wcs_is_subscription_product' ) ) { $is_recurring = (bool) wcs_is_subscription_product( $product_id ); } if ( ! $is_recurring && function_exists( 'wps_sfw_is_product_subscription' ) ) { $is_recurring = (bool) wps_sfw_is_product_subscription( $lookup_id ); } if ( ! $is_recurring && method_exists( $product, 'get_type' ) ) { $type = strtolower( (string) $product->get_type() ); $is_recurring = false !== strpos( $type, 'subscription' ); } if ( ! $is_recurring && false !== strpos( get_class( $product ), 'FlexibleSubscriptions' ) ) { $is_recurring = true; } if ( ! $is_recurring ) { foreach ( array( '_subscription_period', '_subscription_period_interval', '_subscription_price', '_wps_sfw_product', 'wps_sfw_product', '_wps_sfw_subscription', 'wps_sfw_subscription', ) as $key ) { $value = get_post_meta( $lookup_id, $key, true ); if ( '' !== (string) $value && ! in_array( strtolower( (string) $value ), array( 'no', '0', 'false', 'off' ), true ) ) { $is_recurring = true; break; } } } return (bool) apply_filters( 'divi_powers_product_is_recurring', $is_recurring, $product, $context ); } private function product_has_future_paid_recurrence( $product, $context = null ) { if ( ! $this->product_is_recurring( $product, $context ) ) { return false; } $product_id = absint( $product->get_id() ); $parent_id = method_exists( $product, 'get_parent_id' ) ? absint( $product->get_parent_id() ) : 0; $lookup_id = $parent_id ? $parent_id : $product_id; $amounts = array(); foreach ( array( 'get_price', 'get_regular_price' ) as $method ) { if ( method_exists( $product, $method ) ) { $amounts[] = (float) $product->{$method}( 'edit' ); } } foreach ( array( '_price', '_regular_price', '_subscription_price', '_wps_sfw_subscription_price', 'wps_sfw_subscription_price', ) as $key ) { $amounts[] = (float) get_post_meta( $lookup_id, $key, true ); } $future_amount = max( array_merge( array( 0.0 ), $amounts ) ); return (float) apply_filters( 'divi_powers_recurring_product_future_amount', $future_amount, $product, $context ) > 0.00001; } private function effective_free_checkout_subscription_mode( $profile = null ) { if ( is_array( $profile ) && isset( $profile['definition'] ) && is_array( $profile['definition'] ) ) { $value = sanitize_key( (string) ( $profile['definition']['commerce_free_checkout_subscription_mode'] ?? 'inherit' ) ); if ( in_array( $value, array( 'auto', 'skip', 'require' ), true ) ) { return $value; } } $value = sanitize_key( (string) ( $this->settings()['free_checkout_subscription_mode'] ?? 'auto' ) ); return in_array( $value, array( 'auto', 'skip', 'require' ), true ) ? $value : 'auto'; } private function effective_free_checkout_button_text( $profile = null ) { if ( is_array( $profile ) && isset( $profile['definition'] ) && is_array( $profile['definition'] ) ) { $value = substr( sanitize_text_field( $profile['definition']['commerce_free_checkout_button_text'] ?? '' ), 0, 80 ); if ( $value ) { return $value; } } return substr( sanitize_text_field( $this->settings()['free_checkout_button_text'] ?? 'Complete order' ), 0, 80 ); } public function capture_campaign_coupon() { if ( ! $this->runtime_enabled() || empty( $_GET['dp_coupon'] ) || ! function_exists( 'WC' ) || ! WC() || ! WC()->session ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended $profile = $this->active_profile(); if ( ! $profile && function_exists( 'get_query_var' ) ) { $route = get_query_var( 'dp_checkout_journey' ); if ( $route ) { $profile = apply_filters( 'divi_powers_woo_profile_by_slug', null, $route ); } } if ( ! $this->effective_bool( 'coupon_links', is_array( $profile ) ? $profile : null ) ) { return; } $posted_code = sanitize_text_field( wp_unslash( $_GET['dp_coupon'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended $code = function_exists( 'wc_format_coupon_code' ) ? wc_format_coupon_code( $posted_code ) : $posted_code; if ( $code ) { WC()->session->set( self::PENDING_COUPON_SESSION, $code ); } } public function apply_pending_coupon() { if ( ! $this->runtime_enabled() || ! function_exists( 'WC' ) || ! WC() || ! WC()->session || ! WC()->cart ) { return; } $code = WC()->session->get( self::PENDING_COUPON_SESSION ); if ( ! $code ) { return; } if ( ! $this->effective_bool( 'coupon_links', $this->active_profile() ) ) { WC()->session->__unset( self::PENDING_COUPON_SESSION ); return; } if ( WC()->cart->is_empty() ) { return; } if ( ! WC()->cart->has_discount( $code ) ) { WC()->cart->apply_coupon( $code ); } WC()->session->__unset( self::PENDING_COUPON_SESSION ); } public function filter_coupons_enabled( $enabled ) { if ( ! $enabled || ! $this->runtime_enabled() || ! $this->effective_bool( 'hide_coupon_after_apply', $this->active_profile() ) || ! function_exists( 'WC' ) || ! WC() || ! WC()->cart ) { return $enabled; } return WC()->cart->get_applied_coupons() ? false : $enabled; } private function effective_account_behavior() { if ( ! $this->runtime_enabled() ) { return 'default'; } $profile = $this->active_profile(); if ( is_array( $profile ) ) { $value = sanitize_key( (string) ( $profile['definition']['commerce_account_behavior'] ?? 'inherit' ) ); if ( 'inherit' !== $value && in_array( $value, array( 'default', 'guest', 'optional', 'required', 'auto' ), true ) ) { return $value; } } return $this->settings()['account_behavior']; } private function effective_bool( $key, $profile = null ) { $settings = $this->settings(); if ( is_array( $profile ) && isset( $profile['definition'] ) && is_array( $profile['definition'] ) ) { $override_key = 'commerce_' . $key; $value = isset( $profile['definition'][ $override_key ] ) ? sanitize_key( (string) $profile['definition'][ $override_key ] ) : 'inherit'; if ( 'enabled' === $value ) { return true; } if ( 'disabled' === $value ) { return false; } } return ! empty( $settings[ $key ] ); } private function active_profile() { $profile = apply_filters( 'divi_powers_woo_active_journey_profile', null ); return is_array( $profile ) ? $profile : null; } private function profile_for_order( $order ) { if ( ! is_object( $order ) || ! method_exists( $order, 'get_meta' ) ) { return null; } $id = absint( $order->get_meta( '_divi_powers_woo_profile_id', true ) ); $profile = $id ? apply_filters( 'divi_powers_woo_profile_by_id', null, $id ) : null; return is_array( $profile ) ? $profile : null; } private function product_completion_setting( $product ) { $id = $this->product_parent_aware_id( $product ); $value = sanitize_key( (string) get_post_meta( $id, self::PRODUCT_COMPLETE_META, true ) ); return in_array( $value, array( 'include', 'exclude' ), true ) ? $value : 'inherit'; } private function product_force_one( $product ) { return $this->product_meta_bool( $this->product_parent_aware_id( $product ), self::PRODUCT_FORCE_ONE_META ); } private function product_parent_aware_id( $product ) { if ( ! is_object( $product ) || ! method_exists( $product, 'get_id' ) ) { return 0; } $parent = method_exists( $product, 'get_parent_id' ) ? absint( $product->get_parent_id() ) : 0; return $parent ? $parent : absint( $product->get_id() ); } private function product_meta_bool( $product_id, $key ) { $product_id = absint( $product_id ); if ( ! $product_id ) { return false; } if ( get_post_meta( $product_id, $key, true ) ) { return true; } $parent = function_exists( 'wp_get_post_parent_id' ) ? absint( wp_get_post_parent_id( $product_id ) ) : 0; return $parent ? (bool) get_post_meta( $parent, $key, true ) : false; } private function request_buy_now_product_id( $include_session = true ) { $product_id = 0; if ( isset( $_REQUEST['dp_buy_now'] ) && is_numeric( $_REQUEST['dp_buy_now'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended $product_id = absint( $_REQUEST['dp_buy_now'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( 1 === $product_id && isset( $_REQUEST['add-to-cart'] ) ) { $product_id = absint( $_REQUEST['add-to-cart'] ); } // phpcs:ignore WordPress.Security.NonceVerification.Recommended } if ( ! $product_id && $include_session && function_exists( 'WC' ) && WC() && WC()->session ) { $product_id = absint( WC()->session->get( self::BUY_NOW_PRODUCT_SESSION ) ); } return $product_id; } private function request_buy_now_quantity( $include_session = true ) { $quantity = isset( $_REQUEST['quantity'] ) ? absint( $_REQUEST['quantity'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( ! $quantity && $include_session && function_exists( 'WC' ) && WC() && WC()->session ) { $quantity = absint( WC()->session->get( self::BUY_NOW_QUANTITY_SESSION ) ); } return max( 1, min( 99, $quantity ? $quantity : 1 ) ); } private function product_id_from_add_request( $adding_to_cart ) { if ( is_object( $adding_to_cart ) && method_exists( $adding_to_cart, 'get_id' ) ) { return absint( $adding_to_cart->get_id() ); } if ( is_numeric( $adding_to_cart ) ) { return absint( $adding_to_cart ); } if ( isset( $_REQUEST['add-to-cart'] ) ) { return absint( $_REQUEST['add-to-cart'] ); } // phpcs:ignore WordPress.Security.NonceVerification.Recommended return $this->request_buy_now_product_id(); } private function get_buy_now_products() { if ( ! function_exists( 'wc_get_product' ) ) { return array(); } $ids = get_posts( array( 'post_type' => array( 'product', 'product_variation' ), 'post_status' => 'publish', 'posts_per_page' => 250, 'fields' => 'ids', 'orderby' => 'title', 'order' => 'ASC' ) ); $items = array(); foreach ( $ids as $id ) { $product = wc_get_product( $id ); if ( ! $product || ! $product->is_purchasable() || ( method_exists( $product, 'is_type' ) && $product->is_type( 'variable' ) ) ) { continue; } $name = $product->get_name(); $price = method_exists( $product, 'get_price_html' ) ? wp_strip_all_tags( $product->get_price_html() ) : ''; $items[] = array( 'id' => absint( $id ), 'label' => $name . ( $price ? ' — ' . $price : '' ) . ' (#' . absint( $id ) . ')' ); } return $items; } private function runtime_enabled() { return $this->plugin->is_power_enabled( 'commerce_utilities' ) && $this->woo_available(); } private function woo_available() { return class_exists( 'WooCommerce' ) || function_exists( 'WC' ); } }