*/ class Wpfnl_Public { use SingletonTrait; public $offer_metas; public $step_id; /** * Initialize the class and set its properties. * * @param string $plugin_name The name of the plugin. * @param string $version The version of this plugin. * * @since 1.0.0 */ public function __construct() { /** * This will trigger when funnel first start */ add_action('wp', array($this, 'init_funnel'), 1); add_action('wp', array($this, 'init_wp_actions'), 55); add_action('wp_enqueue_scripts', [$this, 'enqueue_styles']); add_action('wp_enqueue_scripts', [$this, 'enqueue_scripts']); add_action('init', array($this, 'init_function')); add_filter('wpfnl_offer_meta', array($this, 'wpfnl_offer_meta'), 10, 1); /** * Modify the checkout order received url to next step */ add_filter('woocommerce_get_checkout_order_received_url', [$this, 'redirect_to_funnel_thankyou_page'], 1000, 2); /* * This hook will be triggered once the elementor data is saved. * Will be placed this hook on another class in future. */ add_action('elementor/editor/after_save', array($this, 'elementor_data_after_save_action'), 10, 2); add_filter('woocommerce_hidden_order_itemmeta', array($this, 'wpfnl_woocommerce_hidden_order_itemmeta'), 10, 1); add_filter('woocommerce_order_item_display_meta_key', array($this, 'wpfnl_beautify_item_meta_on_order'), 10, 3); add_filter('woocommerce_order_item_display_meta_value', array($this, 'wpfnl_update_order_item_display_meta_value'), 9999, 3); /** * Trigger ajax if any user abandoned a funnel */ add_action('wp_ajax_nopriv_maybe_abandoned_funnel', array($this, 'maybe_abandoned_funnel'), 10); add_action('wp_ajax_maybe_abandoned_funnel', array($this, 'maybe_abandoned_funnel'), 10); add_action('wpfunnels/funnel_journey_end', array($this, 'end_journey'), 10, 2); add_filter('woocommerce_add_cart_item_data', [$this, 'namespace_force_individual_cart_items'], 10, 2); add_filter('woocommerce_cart_item_name', [$this, 'wpfnl_order_bump_cart_item_name'], 10, 3); add_filter('woocommerce_checkout_cart_item_quantity', [$this, 'wpfnl_order_bump_cart_item_quantity'], 10, 3); add_action('woocommerce_checkout_create_order_line_item', [$this, 'wpfnl_order_bump_order_line_item'], 10, 4); add_filter('wpfunnels/funnel_order_placed', [$this, 'add_order_details_to_logger'], 10, 3); add_action('wpfunnels/after_optin_submit', array($this, 'add_optin_data_to_logger'), 10, 4); add_action('wpfunnels/after_optin_submit', array($this, 'get_optin_data'), 10, 4); add_action('wpfunnels/after_optin_submit', [$this, 'register_optin_user'], 10, 5); add_action('wpfunnels/optin_submission_response', [$this, 'regenerate_redirect_url'], 10, 5); add_filter('posts_where', array($this, 'posts_where'), 99); /** * Add edit funnel menu at admin bar */ add_action('admin_bar_menu', array($this, 'add_edit_funnel_menu'), 999); /** * Add custom script */ add_action('wp_head', array($this, 'add_custom_script')); add_action('wp_head', array($this, 'add_custom_css')); // remove woodmart hook for funnel checkout add_action('wp', [$this, 'remove_woodmart_hook'], 150); add_action( 'wp_footer', array( $this, 'load_variable_product_modal' ) ); } /** * Removes Woodmart theme related hooks from the checkout page. * * This function is called when the checkout page is loaded in the admin or frontend. * It checks if the Woodmart theme is active and if the current page is a checkout page. * If both conditions are met, it removes the 'woodmart_enqueue_base_styles' action * with a priority of 10000 and the 'woodmart_sticky_toolbar_template' action with a priority of 10000. * * @since 3.4.9 */ public function remove_woodmart_hook() { global $post; // Check if we are in the admin or if the post object is not set if (is_admin() || !$post) { return; } $checkout_id = ''; // Check if we are doing an AJAX request if (wp_doing_ajax()) { // Get the checkout ID from the POST data $checkout_id = Wpfnl_functions::get_checkout_id_from_post($_POST); } // If the checkout ID is not set, use the current post ID $checkout_id = !$checkout_id ? $post->ID : $checkout_id; $funnel_id = Wpfnl_functions::get_funnel_id_from_step( $checkout_id ); // Check if the Woodmart theme is active and if the current page is a checkout page if (Wpfnl_functions::maybe_woodmart_theme() && $checkout_id && $funnel_id) { // Remove the 'woodmart_enqueue_base_styles' action with a priority of 10000 remove_action('wp_enqueue_scripts', 'woodmart_enqueue_base_styles', 10000); // Remove the 'woodmart_sticky_toolbar_template' action with a priority of 10000 remove_action('wp_footer', 'woodmart_sticky_toolbar_template', 10000); } } /** * Modify the SQL JOIN clause to exclude language-based filtering when querying posts. * * This function is a filter callback used in WordPress to customize the SQL query's JOIN clause * when retrieving posts. It is specifically designed to work with the Polylang plugin to exclude * language-based filtering from the JOIN clause. * * @param string $join The original SQL JOIN clause. * * @return string The modified SQL JOIN clause with language-based filtering excluded. * * @see https://polylang.pro/ * @since 2.8.6 */ public function posts_join($join) { if (class_exists('PLL_Model') && class_exists('PLL_Translated_Post')) { $options = new \WP_Syntex\Polylang\Options\Options(); $model = new PLL_Model($options); $post = new PLL_Translated_Post($model); $custom_join = $post->join_clause(); return str_replace($custom_join, '', $join); } return $join; } /** * Modify the SQL WHERE clause for post queries, optionally removing language filtering. * * This function is a filter callback used in WordPress to customize the SQL query's WHERE clause * when retrieving posts. If the query appears to target 'wpfunnel_steps', it adjusts the WHERE * clause, possibly removing language-based filtering. It also adds a filter to customize the SQL * JOIN clause for further modifications. * * @param string $where The original SQL WHERE clause. * * @return string The modified SQL WHERE clause, possibly without language-based filtering. * * @see https://wordpress.org/ * @since 2.8.6 */ public function posts_where($where) { if (preg_match('/wpfunnel_steps/', $where)) { add_filter('posts_join', array($this, 'posts_join'), 99); $lang = $this->get_pll_term_id(); return str_replace("AND pll_tr.term_taxonomy_id = {$lang}", '', $where); } return $where; } /** * Get the term ID associated with a language slug from Polylang plugin options. * * This function retrieves the term ID for a specific language slug by querying the WordPress * database terms table. It uses the provided language slug and the Polylang plugin options. * * @return int|null The term ID of the language, or null if not found. * * @global object $wpdb WordPress database object. * * @see https://polylang.pro/ * @since 2.8.6 */ private function get_pll_term_id() { global $wpdb; $pll_options = get_option('polylang', []); return $wpdb->get_var($wpdb->prepare("SELECT `term_id` FROM %i WHERE `slug`=%s", [$wpdb->terms, $pll_options['default_lang'] ?? ''])); } /** * Add individual items in cart forcefully. It's implemented for indentifying orderbump items * * @param Array * @param String * * @return Array */ public function namespace_force_individual_cart_items($cart_item_data, $product_id) { $post_action = filter_input_array(INPUT_POST, FILTER_SANITIZE_SPECIAL_CHARS); if (isset($post_action['action']) && ('wpfnl_order_bump_ajax' === $post_action['action'] || 'wpfnl_order_bump_ajax' === $post_action['action'])) { $unique_cart_item_key = md5(microtime() . rand()); $cart_item_data['unique_key'] = $unique_cart_item_key; } return $cart_item_data; } /** * Display custom product title for order bump items in cart * * @param string $product_name * @param array $cart_item * @param string $cart_item_key * @return string */ public function wpfnl_order_bump_cart_item_name($product_name, $cart_item, $cart_item_key) { if (isset($cart_item['wpfnl_order_bump']) && $cart_item['wpfnl_order_bump'] && isset($cart_item['wpfnl_ob_title']) && !empty($cart_item['wpfnl_ob_title'])) { return $cart_item['wpfnl_ob_title']; } return $product_name; } /** * Display custom quantity for order bump items in cart * * @param string $quantity * @param array $cart_item * @param string $cart_item_key * @return string */ public function wpfnl_order_bump_cart_item_quantity($quantity, $cart_item, $cart_item_key) { if (isset($cart_item['wpfnl_order_bump']) && $cart_item['wpfnl_order_bump'] && isset($cart_item['wpfnl_ob_quantity'])) { // When the checkout step has quantity selection enabled, let the bump // item go through the same editable quantity control as every other // cart item instead of forcing a fixed, non-editable badge. $step_id = wp_doing_ajax() ? Wpfnl_functions::is_funnel_checkout_page()['id'] : get_the_ID(); if ( 'yes' === get_post_meta( $step_id, '_wpfnl_quantity_support', true ) ) { return $quantity; } return ' ' . $cart_item['quantity'] . ''; } return $quantity; } /** * Add custom product title to order line item meta * * @param \WC_Order_Item_Product $item * @param string $cart_item_key * @param array $values * @param \WC_Order $order */ public function wpfnl_order_bump_order_line_item($item, $cart_item_key, $values, $order) { if (isset($values['wpfnl_order_bump']) && $values['wpfnl_order_bump'] && isset($values['wpfnl_ob_title']) && !empty($values['wpfnl_ob_title'])) { $item->set_name($values['wpfnl_ob_title']); } } /** * Initialize function */ public function init_function() { $offer_meta = ['_wpfunnels_order_bump']; return apply_filters('wpfnl_offer_meta', $offer_meta); } /** * Init actions for a funnel * * @since 2.0.2 */ public function init_funnel() { if (Wpfnl_functions::is_funnel_step_page()) { global $post; do_action('wpfunnels/wp', $post->ID); $this->start_funnel_journey(); } } /** * Start session data when funnel starts * * @since 2.0.3 */ private function start_funnel_journey() { Wpfnl_functions::start_journey(); if (Wpfnl_functions::check_if_this_is_step_type('checkout')) { Wpfnl_functions::do_not_cache(); } } public function init_wp_actions() { $is_ajax = wp_doing_ajax() || isset( $_GET['wc-ajax'] ); if ( Wpfnl_functions::is_funnel_step_page('') || $is_ajax ) { /** * Enqueue public styles */ if ( ! $is_ajax ) { add_action('wp_enqueue_scripts', array($this, 'enqueue_styles')); add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts')); add_action('wp_enqueue_scripts', array($this, 'remove_theme_styles')); add_filter('woocommerce_enqueue_styles', array($this, 'enqueue_wc_styles'), 9999); } add_filter('woocommerce_locate_template', array($this, 'wpfunnels_woocommerce_locate_template'), 20, 3); } } /** * Register the stylesheets for the public-facing side of the site. * * @since 1.0.0 */ public function enqueue_styles() { /** * This function is provided for demonstration purposes only. * * An instance of this class should be passed to the run() function * defined in Wpfnl_Loader as all of the hooks are defined * in that particular class. * * The Wpfnl_Loader will then create the relationship * between the defined hooks and the functions defined in this * class. */ if (Wpfnl_functions::is_funnel_step_page()) { add_filter('astra_theme_woocommerce_dynamic_css', '__return_empty_string'); wp_enqueue_style('wpfnl-public', plugin_dir_url(__FILE__) . 'assets/css/wpfnl-public.css', [], WPFNL_VERSION, 'all'); //$this->load_googlefonts(); } } /** * Register the JavaScript for the public-facing side of the site. * * @since 1.0.0 */ public function enqueue_scripts() { /** * This function is provided for demonstration purposes only. * * An instance of this class should be passed to the run() function * defined in Wpfnl_Loader as all of the hooks are defined * in that particular class. * * The Wpfnl_Loader will then create the relationship * between the defined hooks and the functions defined in this * class. */ if (Wpfnl_functions::is_funnel_step_page()) { $compatibility = Wpfnl_Theme_Compatibility::getInstance(); wp_enqueue_script('wpfnl-public', plugin_dir_url(__FILE__) . 'assets/js/wpfnl-public.js', ['jquery'], WPFNL_VERSION, false); if ( Wpfnl_functions::is_funnel_step_page( 'checkout' ) ) { $checkout_id = get_the_ID(); $order_bump_settings = get_post_meta( $checkout_id, 'order-bump-settings', true ); if ( ! empty( $order_bump_settings ) ) { foreach ( $order_bump_settings as $key => $settings ) { if ( isset( $settings['product'] ) ) { $product = wc_get_product( $settings['product'] ); if ( $product && $product->is_type( 'variable' ) ) { wp_enqueue_script( 'wc-single-product' ); break; } } } } } wp_enqueue_script('wpfnl-order-bump-variable', plugin_dir_url(__FILE__) . 'assets/js/order-bump-variable.js', array( 'jquery', 'wc-add-to-cart-variation' ), WPFNL_VERSION, true); wp_localize_script( 'wpfnl-public', 'wpfnl_obj', [ 'ajaxurl' => admin_url('admin-ajax.php'), 'is_builder_preview' => $compatibility->is_builder_preview(), 'funnel_id' => get_post_meta(get_the_ID(), '_funnel_id', true), 'step_id' => get_the_ID(), 'ajax_nonce' => wp_create_nonce('wpfnl'), 'abandoned_ajax_nonce' => wp_create_nonce('abandoned_ajax_nonce'), 'optin_form_nonce' => wp_create_nonce('optin_form_nonce'), 'is_user_logged_in' => is_user_logged_in(), 'user_id' => get_current_user_id(), 'is_login_reminder' => get_option('woocommerce_enable_checkout_login_reminder'), 'express_checkout_enabled' => get_post_meta(get_the_ID(), '_wpfnl_express_checkout_enabled', true) ? get_post_meta(get_the_ID(), '_wpfnl_express_checkout_enabled', true) : 'no', 'field_validation_enabled' => get_post_meta(get_the_ID(), '_wpfnl_field_validation_enabled', true) ? get_post_meta(get_the_ID(), '_wpfnl_field_validation_enabled', true) : 'no', 'field_validation_message' => get_post_meta(get_the_ID(), '_wpfnl_field_validation_message', true) ? get_post_meta(get_the_ID(), '_wpfnl_field_validation_message', true) : '{field} is required', 'display_product_images' => get_post_meta(get_the_ID(), '_wpfnl_display_product_images', true) ? get_post_meta(get_the_ID(), '_wpfnl_display_product_images', true) : 'no', 'collapsible_order_summary_enabled' => get_post_meta(get_the_ID(), '_wpfnl_collapsible_order_summary_enabled', true) !== '' ? get_post_meta(get_the_ID(), '_wpfnl_collapsible_order_summary_enabled', true) : 'yes', 'enhanced_phone_field_enabled' => get_post_meta(get_the_ID(), '_wpfnl_enhanced_phone_field_enabled', true) ? get_post_meta(get_the_ID(), '_wpfnl_enhanced_phone_field_enabled', true) : 'no', 'validate_phone_number' => get_post_meta(get_the_ID(), '_wpfnl_validate_phone_number', true) ? get_post_meta(get_the_ID(), '_wpfnl_validate_phone_number', true) : 'no', 'save_phone_number_format' => get_post_meta(get_the_ID(), '_wpfnl_save_phone_number_format', true) ? get_post_meta(get_the_ID(), '_wpfnl_save_phone_number_format', true) : 'without_country_code', 'phone_help_text' => get_post_meta(get_the_ID(), '_wpfnl_phone_help_text', true) ? get_post_meta(get_the_ID(), '_wpfnl_phone_help_text', true) : '', 'phone_utils_url' => plugin_dir_url(__FILE__) . 'assets/phone/js/utils.js', ] ); if ('yes' === (get_post_meta(get_the_ID(), '_wpfnl_enhanced_phone_field_enabled', true) ? get_post_meta(get_the_ID(), '_wpfnl_enhanced_phone_field_enabled', true) : 'no')) { wp_enqueue_style('intl-tel-input-style', plugin_dir_url(__FILE__) . 'assets/phone/css/intlTelInput.css', array(), WPFNL_VERSION); wp_enqueue_script('intl-tel-input-script', plugin_dir_url(__FILE__) . 'assets/phone/js/intlTelInput.min.js', array('jquery'), WPFNL_VERSION, true); wp_enqueue_script('intl-tel-input-utils', plugin_dir_url(__FILE__) . 'assets/phone/js/utils.js', array('intl-tel-input-script'), WPFNL_VERSION, true); } $post_id = get_the_ID(); $custom_css = get_post_meta($post_id, 'rex_gutenberg_css', true); if ($custom_css) { wp_register_style('rex-gutenberg-css', false); wp_enqueue_style('rex-gutenberg-css'); wp_add_inline_style('rex-gutenberg-css', $custom_css); } } } /** * This functions are from qubely. */ private function load_googlefonts() { global $blocks; $contains_wpfnl_blocks = false; $block_fonts = []; $load_google_fonts = 'yes'; if ($load_google_fonts == 'yes') { $blocks = $this->parse_all_blocks(); $contains_wpfnl_blocks = $this->has_wpfnl_blocks($blocks); if ($contains_wpfnl_blocks) { $block_fonts = $this->gather_block_fonts($blocks, $block_fonts); $global_settings = get_option($this->option_keyword); $global_settings = $global_settings == false ? json_decode('{}') : json_decode($global_settings); $global_settings = json_decode(json_encode($global_settings), true); $gfonts = ''; $all_global_fonts = array(); if (isset($global_settings['presets']) && isset($global_settings['presets'][$global_settings['activePreset']]) && isset($global_settings['presets'][$global_settings['activePreset']]['typography'])) { $all_global_fonts = $this->colsFromArray(array_column($global_settings['presets'][$global_settings['activePreset']]['typography'], 'value'), ['family', 'weight']); } if (count($all_global_fonts) > 0) { $global_fonts = array_column($all_global_fonts, 'family'); $all_fonts = array_unique(array_merge($global_fonts, $block_fonts)); if (!empty($all_fonts)) { $system = array( 'Arial', 'Tahoma', 'Verdana', 'Helvetica', 'Times New Roman', 'Trebuchet MS', 'Georgia', ); $gfonts_attr = ':100,100italic,200,200italic,300,300italic,400,400italic,500,500italic,600,600italic,700,700italic,800,800italic,900,900italic'; foreach ($all_fonts as $font) { if (!in_array($font, $system, true) && !empty($font)) { $gfonts .= str_replace(' ', '+', trim($font)) . $gfonts_attr . '|'; } } } if (!empty($gfonts)) { $query_args = array( 'family' => $gfonts, ); wp_register_style( 'qubely-google-fonts', add_query_arg($query_args, '//fonts.googleapis.com/css'), array(), QUBELY_VERSION ); wp_enqueue_style('qubely-google-fonts'); } } } } } /** * Parse all blocks * * @return array[] */ public function parse_all_blocks() { $blocks; if (is_single() || is_page() || is_404()) { global $post; if (is_object($post) && property_exists($post, 'post_content')) { $blocks = parse_blocks($post->post_content); } } elseif (is_archive() || is_home() || is_search()) { global $wp_query; foreach ($wp_query as $post) { if (is_object($post) && property_exists($post, 'post_content')) { $blocks = parse_blocks($post->post_content); } } } return $blocks; } /** * Check whether the contents has wpfnl blocks or not * * @param $blocks * * @return bool */ public function has_wpfnl_blocks($blocks) { $is_wpfnl_block = false; foreach ($blocks as $key => $block) { if (strpos($block['blockName'], 'wpfnl') !== false) { $is_wpfnl_block = true; } if (isset($block['innerBlocks']) && gettype($block['innerBlocks']) == 'array' && count($block['innerBlocks']) > 0) { $is_wpfnl_block = $this->has_qubely_blocks($block['innerBlocks']); } } return $is_wpfnl_block; } /** * Gather block google fonts * * @param $blocks * @param $block_fonts * * @return array */ public function gather_block_fonts($blocks, $block_fonts) { $google_fonts = $block_fonts; foreach ($blocks as $key => $block) { if (strpos($block['blockName'], 'wpfnl') !== false) { foreach ($block['attrs'] as $key => $att) { if (gettype($att) == 'array' && isset($att['openTypography']) && isset($att['family'])) { if (isset($block['attrs'][$key]['activeSource'])) { if ($block['attrs'][$key]['activeSource'] == 'custom') { array_push($google_fonts, $block['attrs'][$key]['family']); } } else { array_push($google_fonts, $block['attrs'][$key]['family']); } } } } if (isset($block['innerBlocks']) && gettype($block['innerBlocks']) == 'array' && count($block['innerBlocks']) > 0) { $child_fonts = $this->gather_block_fonts($block['innerBlocks'], $google_fonts); if (count($child_fonts) > 0) { $google_fonts = array_merge($google_fonts, $child_fonts); } } } return array_unique($google_fonts); } /** * Columns from array * * @param array $array * @param $keys * * @return array */ public function colsFromArray(array $array, $keys) { if (!is_array($keys)) $keys = [$keys]; return array_map(function ($el) use ($keys) { $o = []; foreach ($keys as $key) { // if(isset($el[$key]))$o[$key] = $el[$key]; //you can do it this way if you don't want to set a default for missing keys. $o[$key] = isset($el[$key]) ? $el[$key] : false; } return $o; }, $array); } /** * Remove theme style * * @return bool */ public function remove_theme_styles() { if (Wpfnl_functions::is_funnel_step_page()) { if (Wpfnl_Theme_Compatibility::getInstance()->is_compatible_theme_enabled()) { return; } $general_settings = Wpfnl_functions::get_general_settings(); $wp_styles = wp_styles(); $themes_uri = get_theme_root_uri(); if ($general_settings['disable_theme_style'] == 'on') { $dequeue_theme_style = apply_filters('wpfunnels/dequeue_theme_style', true); } else { $dequeue_theme_style = apply_filters('wpfunnels/dequeue_theme_style', false); } if ($dequeue_theme_style) { foreach ($wp_styles->registered as $wp_style) { if (strpos($wp_style->src, $themes_uri) !== false) { wp_deregister_style($wp_style->handle); wp_dequeue_style($wp_style->handle); do_action('wpfunnels/enqueue_custom_scripts'); } } } } } /** * Enqueue woocommerce styles * * @return array */ public function enqueue_wc_styles() { $wc_styles = array( 'woocommerce-layout' => array( 'src' => plugins_url('assets/css/woocommerce-layout.css', WC_PLUGIN_FILE), 'deps' => '', 'version' => WC_VERSION, 'media' => 'all', 'has_rtl' => true, ), 'woocommerce-smallscreen' => array( 'src' => plugins_url('assets/css/woocommerce-smallscreen.css', WC_PLUGIN_FILE), 'deps' => 'woocommerce-layout', 'version' => WC_VERSION, 'media' => 'only screen and (max-width: ' . apply_filters('woocommerce_style_smallscreen_breakpoint', '768px') . ')', 'has_rtl' => true, ), 'woocommerce-general' => array( 'src' => plugins_url('assets/css/woocommerce.css', WC_PLUGIN_FILE), 'deps' => '', 'version' => WC_VERSION, 'media' => 'all', 'has_rtl' => true, ), ); //unset( $wc_styles['woocommerce-layout'] ); //unset( $wc_styles['woocommerce-general'] ); return $wc_styles; } /** * Save custom meta fields for order bump while saving * checkout widget * * @param $post_id * @param $editor_data * * @since 2.0.0 */ public function elementor_data_after_save_action($post_id, $editor_data) { if (Wpfnl_functions::check_if_this_is_step_type_by_id($post_id, 'checkout')) { foreach ($editor_data as $key => $inner_element) { $checkout_widget = Wpfnl_functions::recursive_multidimensional_ob_array_search_by_value('wpfnl-checkout', $inner_element['elements']); $lms_checkout_widget = Wpfnl_functions::recursive_multidimensional_ob_array_search_by_value('wpfnl-lms-checkout', $inner_element['elements']); if ($checkout_widget || $lms_checkout_widget) { $widget_settings = isset($checkout_widget['settings']) ? $checkout_widget['settings'] : $lms_checkout_widget['settings']; $layout = isset($widget_settings['checkout_layout']) ? $widget_settings['checkout_layout'] : 'wpfnl-col-2'; update_post_meta($post_id, '_wpfnl_checkout_layout', $layout); $order_bump_status = get_post_meta($post_id, 'order-bump', true); $order_bump_settings = get_post_meta($post_id, 'order-bump-settings', true); $order_bump_enabled = 'no'; $default_order_bump_data = []; if ($order_bump_status) { $order_bump_enabled = $order_bump_status; } if ($order_bump_settings) { $order_bump_data = $order_bump_settings; } else { $order_bump_data = $default_order_bump_data; } if (count($order_bump_data) > 0) { foreach ($order_bump_data as $key => $ob) { $order_bump_data[$key]['isEnabled'] = $this->set_order_bump_data('isEnabled', 'order_bump_' . $key, $widget_settings, $order_bump_data[$key], 'yes'); $order_bump_data[$key]['selectedStyle'] = $this->set_order_bump_data('selectedStyle', 'order_bump_layout_' . $key, $widget_settings, $order_bump_data[$key], 'style1'); $order_bump_data[$key]['position'] = $this->set_order_bump_data('position', 'order_bump_position_' . $key, $widget_settings, $order_bump_data[$key], 'after-order'); $order_bump_data[$key]['checkBoxLabel'] = $this->set_order_bump_data('checkBoxLabel', 'order_bump_checkbox_label_' . $key, $widget_settings, $order_bump_data[$key], 'Grab this offer with one click!'); $order_bump_data[$key]['highLightText'] = $this->set_order_bump_data('highLightText', 'order_bump_product_detail_header_' . $key, $widget_settings, $order_bump_data[$key], 'Special one time offer'); $order_bump_data[$key]['productDescriptionText'] = $this->set_order_bump_data('productDescriptionText', 'order_bump_product_detail_' . $key, $widget_settings, $order_bump_data[$key], 'Get this scratch proof 6D Tempered Glass Screen Protector for your iPhone. Keep your phone safe and sound just like a new one.'); $ob_image = $this->set_order_bump_data('productImage', 'order_bump_image_' . $key, $widget_settings, $order_bump_data[$key]); $order_bump_data[$key]['productImage'] = array( 'id' => isset($ob_image['id']) ? $ob_image['id'] : 0, 'url' => isset($ob_image['url']) ? $ob_image['url'] : '', ); } } update_post_meta($post_id, 'order-bump-settings', $order_bump_data); // update_post_meta($post_id, 'order-bump', $order_bump_data['isEnabled']); } } } } /** * Redirect to next step * * @param $order_received_url * @param $order * * @return string * * @since 1.0.0 */ public function redirect_to_funnel_thankyou_page($order_received_url, $order) { if ($order && Wpfnl_functions::check_if_funnel_order($order)) { $order_key = $order->get_order_key(); $order_id = $order->get_id(); $link = '#'; $funnel_id = Wpfnl_functions::get_funnel_id_from_order($order); if (!$funnel_id) { return $link; } $current_page_id = apply_filters('wpfunnels/current_step_id', $order->get_meta('_wpfunnels_checkout_id'), $order); $parent_step_id = get_post_meta($current_page_id, '_parent_step_id', true); $current_page_id = $parent_step_id ? $parent_step_id : $current_page_id; $next_node = Wpfnl_functions::get_next_conditional_step($funnel_id, $current_page_id, $order); $next_node = apply_filters('wpfunnels/next_step_data', $next_node); $next_node = apply_filters('wpfunnels/modify_next_step_based_on_order', $next_node, $order); if (isset($next_node['step_type']) && 'thankyou' === $next_node['step_type']) { if( Wpfnl_functions::is_global_thank_you_page_enabled() ){ return home_url() . '/checkout/order-received/' . $order_id . '/?key=' . $order_key; } $custom_url = Wpfnl_functions::custom_url_for_thankyou_page($next_node['step_id']); if ($custom_url) { return $custom_url; } } $query_args = array( 'wpfnl-order' => $order_id, 'wpfnl-key' => $order_key, ); $query_args = apply_filters('wpfunnels/order_meta', $query_args, $order); $next_step_url = $this->get_thankyou_step_url($funnel_id, $next_node['step_id'], $order); if ($next_step_url) { return add_query_arg($query_args, $next_step_url); } } return $order_received_url; } /** * Get thankyou page url * * @param $funnel_id * @param $step_id * @param $order_received_url * * @return string */ private function get_thankyou_step_url($funnel_id, $step_id, $order) { /** * If pro plugin isn't installed, there is no need to redirect user to the * offer page. So for the plugin we redirect the user to the funnel thankyou page * forcefully. We have also placed a hook for next step url, which will be modified * on the pro plugin */ $next_step_url = get_permalink($step_id); $step_type = get_post_meta($step_id, '_step_type', true); $thankyou_step_url = ''; if (Wpfnl_functions::check_if_this_is_step_type_by_id($step_id, 'thankyou')) { return apply_filters('wpfunnels/funnel_thankyou_page_url', $next_step_url, $order); } else { $thankyou_page_id = Wpfnl_functions::get_thankyou_page_id($funnel_id); if ($thankyou_page_id) { $thankyou_step_url = get_permalink($thankyou_page_id); } } if( ! defined( 'WPFNL_PRO_DIR' ) ){ $thankyou_step_url = $next_step_url; } return apply_filters('wpfunnels/next_step_url', $thankyou_step_url, $next_step_url, $order); } /** * Set order bump data * * @param $settings_key * @param $key * @param $widget_settings * @param string $default_value * * @return mixed|string * * @since 2.0.0 */ private function set_order_bump_data($settings_key, $key, $widget_settings, $order_bump_data, $default_value = '') { $value = $default_value; if (isset($widget_settings[$key])) { $value = $widget_settings[$key]; } else { $value = $order_bump_data[$settings_key]; } return $value; } /** * Locate wc templates from plugin folders * * @param $template * @param $template_name * @param $template_path * * @return string * * @since 2.0.3 */ public function wpfunnels_woocommerce_locate_template($template, $template_name, $template_path) { /*** * Fires when change the wc template * * @since 2.8.21 */ if (apply_filters('wpfunnels/maybe_locate_template', true)) { $_template = $template; $plugin_path = WPFNL_DIR . 'woocommerce/templates/'; if (file_exists($plugin_path . $template_name)) { $template = $plugin_path . $template_name; } if (!$template) { $template = $_template; } } return $template; } /** * Wpfnl offer meta * * Return offer meta */ public function wpfnl_offer_meta($offer_meta) { $this->offer_metas = $offer_meta; return $offer_meta; } /** * Hidden order item meta * * @param $meta * * @return $meta */ public function wpfnl_woocommerce_hidden_order_itemmeta($meta) { $meta = ['_wpfunnels_step_id', '_wpfnl_upsell', '_wpfnl_downsell', '_wpfnl_step_id', '_wpfunnels_offer_txn_id', '_reduced_stock', '_wpfunnels_offer_refunded']; return $meta; } /** * Beautify item meta on order * * @param $display_key, $meta, $item * * @return $display_key */ public function wpfnl_beautify_item_meta_on_order($display_key, $meta, $item) { $offer_meta = '_wpfunnels_order_bump'; if (is_admin() && $item->get_type() === 'line_item' && ($meta->key === $offer_meta)) { $display_key = __("Offer Type", "wpfnl"); } return $display_key; } /** * Display customize meta value * * @param $display_key, $meta, $item * * @return $meta */ public function wpfnl_update_order_item_display_meta_value($display_key, $meta, $item) { if (isset($item['order_id']) && $item['order_id']) { $order = wc_get_order($item['order_id']); if ($order && Wpfnl_functions::check_if_funnel_order($order)) { if (is_admin() && $item->get_type() === 'line_item' && ($meta->key === '_wpfunnels_order_bump')) { $meta = __("Order Bump", "wpfnl"); return $meta; } elseif (is_admin() && $item->get_type() === 'line_item' && ($meta->key === '_wpfunnels_upsell')) { $meta = __("Upsell", "wpfnl"); return $meta; } elseif (is_admin() && $item->get_type() === 'line_item' && ($meta->key === '_wpfunnels_downsell')) { $meta = __("Downsell", "wpfnl"); return $meta; } elseif (is_admin() && $item->get_type() === 'shipping' && $meta->key === 'Items') { $meta = $item->get_meta('Items'); return $meta; } else { $display_value = $meta->value; return $display_value; } } else { $display_value = $meta->value; return $display_value; } } else { $display_value = $meta->value; return $display_value; } } /** * May be user abandoned funnel */ public function maybe_abandoned_funnel() { check_ajax_referer('abandoned_ajax_nonce', 'security'); $step_id = $_POST['step_id']; $funnel_id = $_POST['funnel_id']; $cookie_name = 'wpfunnels_automation_data'; $cookie = isset($_COOKIE[$cookie_name]) ? json_decode(wp_unslash($_COOKIE[$cookie_name]), true) : array(); $cookie['funnel_status'] = 'abandoned'; if (PHP_SESSION_DISABLED == session_status()) { session_start(); if (isset($_SESSION) || $_SESSION) { if (isset($_SESSION['wpfnl_orders_' . get_current_user_id() . '_' . $funnel_id])) { unset($_SESSION['wpfnl_orders_' . get_current_user_id() . '_' . $funnel_id]); } } } $step_type = get_post_meta($step_id, '_step_type', true); if ('upsell' == $step_type || 'downsell' == $step_type || 'thankyou' == $step_type) { delete_option('wpfunnels_dynamic_offer_data'); } delete_option('optin_data'); do_action('wpfunnels/maybe_user_abandoned_funnel', $step_id, $funnel_id, $cookie); die(); } /** * Redirect Thankyou page * * @param mixed $step_id * @param mixed $funnel_id * * @return [type] */ public function end_journey($step_id, $funnel_id) { if (!Wpfnl_Theme_Compatibility::getInstance()->is_elementor_preview()) { Wpfnl_functions::custom_url_for_thankyou_page($step_id); } } /** * Redirects from LearnDash offer step templates if accessed * without completing previous steps or user is already enrolled * in the course. */ public function redirect_learndash_template() { global $post; $step_id = isset($post->ID) ? $post->ID : false; if ($step_id) { $funnel_id = Wpfnl_functions::get_funnel_id_from_step($step_id); $lms_funnel = get_post_meta($funnel_id, 'is_lms', true); if ($lms_funnel === 'yes') { $step_type = get_post_meta($step_id, '_step_type', true); $course = Wpfnl_lms_learndash_functions::get_course_details($step_id); $redirect_url = ''; if ($step_type === 'upsell' || $step_type === 'downsell') { if (!isset($_GET['wpfnl_ld_payment'])) { $redirect_url = Wpfnl_functions::get_funnel_link($funnel_id); Wpfnl_lms_learndash_functions::learndash_template_safe_redirect($redirect_url); } if ($course && !empty($course)) { $course_id = isset($course['id']) ? $course['id'] : ''; $course_type = isset($course['type']) ? $course['type'] : ''; $next_step = Wpfnl_functions::get_next_step($funnel_id, $step_id); $current_user_id = function_exists('get_current_user_id') ? get_current_user_id() : ''; if (isset($next_step['step_id'])) { if ($next_step['step_type'] == 'conditional') { $next_step = Wpfnl_functions::get_next_step($funnel_id, $next_step['step_id']); } $redirect_url = get_permalink($next_step['step_id']); } if ($course_type === 'open') { Wpfnl_lms_learndash_functions::learndash_template_safe_redirect($redirect_url, 'free'); } if ($course_id !== '' && $current_user_id && $current_user_id !== '') { $course_access = function_exists('sfwd_lms_has_access') ? sfwd_lms_has_access($course_id, $current_user_id) : false; if ($course_access) { Wpfnl_lms_learndash_functions::learndash_template_safe_redirect($redirect_url, 'free'); } } } } } } } private function create_lms_order($order_details = []) { global $wpdb; $result = $wpdb->get_results("SELECT * FROM wp_wpfnl_lms_order WHERE funnel_id = " . $order_details['funnel_id'] . " AND step_id = " . $order_details['funnel_id'] . " AND user_id = " . $order_details['user_id'] . " "); if (!empty($result)) { $wpdb->query($wpdb->prepare("UPDATE wp_wpfnl_lms_order SET meta='order' WHERE funnel_id = " . $order_details['funnel_id'] . " AND step_id = " . $order_details['funnel_id'] . " AND user_id = " . $order_details['user_id'] . "")); } $wpdb->insert('wp_wpfnl_lms_order', array( 'funnel_id' => $order_details['funnel_id'], 'step_id' => $order_details['step_id'], 'user_id' => $order_details['user_id'], 'meta' => 'new_order', 'order_data' => serialize($order_details['course_details']), 'date_created' => date("Y-m-d h:i:s") )); } /** * After main order placed * * @param String $order_id * @param Number $funnel_id * * @since 2.5.9 */ public function add_order_details_to_logger($order_id, $funnel_id, $step_id) { if (Wpfnl_functions::maybe_logger_enabled() && Wpfnl_functions::is_wc_active()) { if ($order_id && $funnel_id) { $order = wc_get_order($order_id); $order_items = $order->get_items(); $product_array = []; foreach ($order_items as $item_id => $item) { $product_array[] = [ 'name' => $item['name'], 'price' => number_format($item['total'] + $item['total_tax'], 2), ]; } ob_start(); print_r($product_array); $textual_representation = ob_get_contents(); ob_end_clean(); Wpfnl_Logger::modify_log_file('event', $textual_representation, 'Main Order Deatils'); } } } /** * Add optin data to logger * * @param Integrer $step_id * @param String $post_action * @param String $action_type * @param Object $record * * @return void * @since 2.5.9 */ public function add_optin_data_to_logger($step_id, $post_action, $action_type, $record) { if (Wpfnl_functions::maybe_logger_enabled()) { $optin_data = (isset($record->form_data['email']) && $record->form_data['email']) ? $record->form_data : []; ob_start(); print_r($optin_data); $textual_representation = ob_get_contents(); ob_end_clean(); Wpfnl_Logger::modify_log_file('event', $textual_representation, 'Optin Submission Data'); } } /** * Get optin data */ public function get_optin_data($step_id, $post_action, $action_type, $record) { $data = get_option('optin_data'); $data['optin_data'][] = [ 'step_id' => $step_id, 'step_type' => get_post_meta($step_id, '_step_type', true) ]; update_option('optin_data', $data); } /** * Registers an opt-in user and performs actions based on the submitted data. * * This function is used to register an opt-in user and perform additional actions * based on the provided data, such as creating a user account. * * @param int $step_id The ID of the opt-in step. * @param string $post_action The type of action to be performed after registration. * @param string $action_type The type of action being taken, such as 'redirect_to_url' or 'next_step'. * @param object $record An object containing the opt-in form data. * @param array $post_data An array of submitted post data. * * @return void * @since 2.8.2 */ public function register_optin_user($step_id, $post_action, $action_type, $record, $post_data) { // Extract user info from the record. $user_info = $record->form_data ?? []; // Extract user's email for potential login and registration. $login = $user_info['email'] ? strstr($user_info['email'], '@', true) : ''; // Kept as an early-out next to the request-supplied flag; the // authoritative check lives in create_user_optin_allow_registration(), // which every registration path routes through. $can_register = get_option('users_can_register'); // Check if opt-in registration is allowed and perform related actions. if ( $can_register && !empty($post_data['optin_allow_registration']) && $post_data['optin_allow_registration'] == 'yes') { // Create user account based on the extracted login and post data. Ajax_Handler::create_user_optin_allow_registration($login, $post_data); } } /** * Generates the redirect URL based on the provided data and conditions. * * This function generates a redirect URL based on the next step's data and additional * query parameters. If the next step's type is conditional, it matches the step * based on conditions and updates the redirect URL accordingly. * * @param array $response The response data containing the current redirect information. * @param int $funnel_id The ID of the funnel. * @param int $step_id The ID of the current step. * @param array $next_step The data of the next step. * @param array $query_params Additional query parameters to be added to the redirect URL. * * @return array The updated response data with the generated redirect URL. * @since 2.8.2 */ public function regenerate_redirect_url($response, $funnel_id, $step_id, $next_step, $query_params) { // Check if the next step type is conditional. if (!empty($next_step['step_type']) && 'conditional' === $next_step['step_type']) { // Match the conditional step and update the redirect URL. $next_step = Ajax_Handler::match_conditional_step($next_step, $funnel_id, $step_id); $response['redirect_url'] = add_query_arg($query_params, get_the_permalink($next_step['step_id'])); } return $response; } /** * Add Edit Funnel menu on admin bar for admin users * * @param \WP_Admin_Bar $admin_bar * @since 3.1.0 */ public function add_edit_funnel_menu(\WP_Admin_Bar $admin_bar) { if (Wpfnl_functions::is_funnel_step_page()) { $funnel_id = Wpfnl_functions::get_funnel_id(); if (!$funnel_id) { return; } $admin_bar->add_node( array( 'id' => 'edit-funnel', 'title' => '' . esc_html__('Edit Funnel', 'wpfnl'), 'href' => admin_url("admin.php?page=edit_funnel&id={$funnel_id}&step_id=0"), 'meta' => array( 'class' => 'wpf-admin-bar-edit-funnel-menu-item', ), ) ); } } /** * Add custom script to header of funnel step * * @return void * @since 3.1.0 */ public function add_custom_script() { global $post; if (!is_object($post) && !isset($post->ID)) { return; } $script = get_post_meta($post->ID, '_wpfnl_custom_script', true); if ('' !== $script) { if (false === strpos($script, htmlentities(''; } echo ''; echo html_entity_decode($script); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo ''; } } /** * Add custom css to header of funnel step * * @return void * @since 3.1.0 */ public function add_custom_css() { global $post; if (!is_object($post) && !isset($post->ID)) { return; } $css = get_post_meta($post->ID, '_wpfnl_custom_css', true); if ('' !== $css) { if (false === strpos($css, htmlentities(''; } echo "\n\n"; echo html_entity_decode($css); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo "\n\n"; } } /** * Load variable product modal * * @return void */ public function load_variable_product_modal() { if ( Wpfnl_functions::is_funnel_step_page( 'checkout' ) ) { $checkout_id = get_the_ID(); $order_bump_settings = get_post_meta( $checkout_id, 'order-bump-settings', true ); if ( ! empty( $order_bump_settings ) ) { foreach ( $order_bump_settings as $key => $settings ) { if ( isset( $settings['product'] ) ) { $product = wc_get_product( $settings['product'] ); if ( $product && $product->is_type( 'variable' ) ) { wc_get_template( 'variable-product-modal.php', array( 'product' => $product, 'key' => $key, ), '', WPFNL_DIR . 'public/modules/checkout/templates/' ); } } } } } } }