Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

jsarnowski / jsarnowski/cartflows-pro   php

Repository URL to install this package:

Version: 1.6.10 

/ class-cartflows-pro-frontend.php

<?php
/**
 * Cartflows Frontend.
 *
 * @package cartflows
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Class Cartflows_Pro_Frontend.
 */
class Cartflows_Pro_Frontend {

	/**
	 * Member Variable
	 *
	 * @var instance
	 */
	private static $instance;

	/**
	 *  Initiator
	 */
	public static function get_instance() {
		if ( ! isset( self::$instance ) ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Constructor
	 */
	public function __construct() {

		/* Set / Destroy Flow Sessions. Set data */
		add_action( 'wp', array( $this, 'init_actions' ), 1 );

		/* Enqueue global required scripts */
		add_action( 'wp', array( $this, 'wp_actions' ), 55 );

		/* Setup Upsell for for payment gatways. Only if we are in out flow */
		add_action( 'woocommerce_pre_payment_complete', array( $this, 'maybe_setup_upsell' ), 99, 1 );
		/* Setup upsell for other gatways which are not covered in "woocommerce_pre_payment_complete" hook */
		add_action( 'woocommerce_checkout_order_processed', array( $this, 'maybe_setup_upsell_ignore_gateways' ), 100, 3 );

		add_action( 'cartflows_order_status_change_to_main_order', array( $this, 'register_cron_for_order_success' ), 10, 3 );
		add_action( 'cartflows_order_status_change_to_main_order', array( $this, 'update_main_order_data_in_transient' ), 10, 3 );

		/* Redirect to next step in flow if next step is other than thank you */
		add_action( 'cartflows_order_started', array( $this, 'set_next_step_url' ) );

		$facebook_settings = Cartflows_Helper::get_facebook_settings();
		if ( 'enable' === $facebook_settings['facebook_pixel_tracking'] ) {
			/* Facebook Pixel */
			add_action( 'wcf_order_bump_item_added', array( $this, 'trigger_fb_event' ) );
			add_action( 'wcf_after_quantity_update', array( $this, 'trigger_fb_event' ) );
			add_action( 'wcf_after_force_all_selection', array( $this, 'trigger_fb_event' ) );
			add_action( 'wcf_after_multiple_selection', array( $this, 'trigger_fb_event' ) );
			add_action( 'wcf_after_single_selection', array( $this, 'trigger_fb_event' ) );
		}

		$google_analytics_settings = Cartflows_Helper::get_google_analytics_settings();
		if ( 'enable' === $google_analytics_settings['enable_google_analytics'] ) {
			/* Google analyics add to cart */
			add_action( 'wcf_order_bump_item_added', array( $this, 'trigger_ga_event' ) );
			add_action( 'wcf_after_quantity_update', array( $this, 'trigger_ga_event' ) );
			add_action( 'wcf_after_force_all_selection', array( $this, 'trigger_ga_event' ) );
			add_action( 'wcf_after_multiple_selection', array( $this, 'trigger_ga_event' ) );
			add_action( 'wcf_after_single_selection', array( $this, 'trigger_ga_event' ) );

			/* Google analyics remove from cart */
			add_action( 'wcf_order_bump_item_removed', array( $this, 'trigger_ga_remove_cart_event' ) );
		}

	}


	/**
	 * Add updated cart and product in Ajax response.
	 *
	 * @param integer $product_id product id.
	 */
	public function trigger_fb_event( $product_id ) {

		add_filter(
			'woocommerce_update_order_review_fragments',
			function( $data ) use ( $product_id ) {
				$data['added_to_cart_data'] = wcf_pro()->utils->prepare_fb_response( $product_id );
				return $data;
			}
		);

	}

	/**
	 * Add updated cart and product in Ajax response.
	 *
	 * @param integer $product_id product id.
	 */
	public function trigger_ga_event( $product_id ) {

		add_filter(
			'woocommerce_update_order_review_fragments',
			function( $data ) use ( $product_id ) {
				$data['ga_added_to_cart_data'] = wcf_pro()->utils->prepare_ga_response( $product_id );
				return $data;
			}
		);
	}

	/**
	 * Add updated cart and product in Ajax response.
	 *
	 * @param integer $product_id product id.
	 */
	public function trigger_ga_remove_cart_event( $product_id ) {

		add_filter(
			'woocommerce_update_order_review_fragments',
			function( $data ) use ( $product_id ) {
				$data['ga_remove_to_cart_data'] = wcf_pro()->utils->prepare_ga_response( $product_id );
				return $data;
			}
		);
	}

	/**
	 * Set next step url.
	 *
	 * @param object $order order object.
	 * @since 1.0.0
	 */
	public function set_next_step_url( $order ) {

		/* Modify the checkout order received url to go thank you page in our flow */
		remove_filter( 'woocommerce_get_checkout_order_received_url', array( Cartflows_Frontend::get_instance(), 'redirect_to_thankyou_page' ), 10, 2 );

		add_filter( 'woocommerce_get_checkout_order_received_url', array( $this, 'show_offer_step' ), 10, 2 );
	}

	/**
	 * Show offer step.
	 *
	 * @param string $order_recieve_url recieve url.
	 * @param object $order order object.
	 * @since 1.0.0
	 */
	public function show_offer_step( $order_recieve_url, $order ) {

		wcf()->logger->log( 'Start-' . __CLASS__ . '::' . __FUNCTION__ );

		if ( $order->get_status() !== 'failed' ) {

			if ( _is_wcf_doing_checkout_ajax() ) {

				$checkout_id = wcf()->utils->get_checkout_id_from_post_data();

				if ( ! $checkout_id ) {
					$checkout_id = wcf()->utils->get_checkout_id_from_order( $order->get_id() );
				}
			} else {
				$checkout_id = wcf()->utils->get_checkout_id_from_order( $order->get_id() );
			}

			wcf()->logger->log( $checkout_id );

			if ( $checkout_id ) {

				$wcf_step_obj = wcf_pro_get_step( $checkout_id );
				$next_step_id = $wcf_step_obj->get_next_step_id();
				$flow_id      = $wcf_step_obj->get_flow_id();

				// If order bump is enabled then check the redirection condition and return next step id.
				$order_bump = get_post_meta( $checkout_id, 'wcf-order-bump', true );
				if ( 'yes' === $order_bump ) {
					$next_step_id = $this->order_bump_conditional_redirection( $checkout_id, $order, $wcf_step_obj );
				}

				if ( $next_step_id ) {

					$order_recieve_url = get_permalink( $next_step_id );

					$session_key = wcf_pro()->session->get_session_key( $flow_id );

					$query_args = array(
						'wcf-order' => $order->get_id(),
						'wcf-key'   => $order->get_order_key(),
					);

					if ( $session_key ) {
						$query_args['wcf-sk'] = $session_key;
					}

					$order_recieve_url = add_query_arg( $query_args, $order_recieve_url );
				}
			}
		}

		Cartflows_Helper::send_fb_response_if_enabled( $order->get_id() );

		Cartflows_Tracking::send_ga_data_if_enabled( $order->get_id() );

		wcf()->logger->log( 'End-' . __CLASS__ . '::' . __FUNCTION__ );

		return $order_recieve_url;
	}

	/**
	 * Order bump redirection.
	 *
	 * @param string $checkout_id id.
	 * @param array  $order order data.
	 * @param object $wcf_step_obj step data.
	 */
	public function order_bump_conditional_redirection( $checkout_id, $order, $wcf_step_obj ) {

		$ob_yes_next_step_id = get_post_meta( $checkout_id, 'wcf-ob-yes-next-step', true );

		$ob_instance     = Cartflows_Pro_Order_Bump_Product::get_instance();
		$order_bump_data = $ob_instance->get_order_bump_data( $checkout_id );
		$product_id      = intval( $order_bump_data['product_id'] );

		$order_items = $order->get_items();
		foreach ( $order_items as $order_item => $items ) {
			$item_id      = $items->get_product_id();
			$variation_id = $items->get_variation_id();

			if ( $item_id === $product_id || $variation_id === $product_id ) {
				$next_step_id = $ob_yes_next_step_id;
			}
		}

		if ( empty( $next_step_id ) ) {
			$next_step_id = $wcf_step_obj->get_next_step_id();
		}

		return $next_step_id;
	}
	/**
	 * Register Cron.
	 *
	 * @param string $new_status new status.
	 * @param string $normal_status normal status.
	 * @param object $order order object.
	 * @since 1.0.0
	 */
	public function register_cron_for_order_success( $new_status, $normal_status, $order ) {

		if ( false === is_a( $order, 'WC_Order' ) ) {

			/* Not Valid Order */
			wcf()->logger->log( 'Not a valid order' );

			return;
		}

		wcf()->logger->log( 'register_cron_for_order_success' );
		wcf()->logger->log( 'new-status - ' . $new_status );

		if ( wcf_pro()->order->get_order_status_slug() !== $new_status ) {

			/* Not Valid Order Status */
			wcf()->logger->log( 'Not a valid order status' );

			return;
		}

		$args = array(
			'order_id'      => $order->get_id(),
			'before_normal' => $order->get_status(), // Pending.
			'normal_status' => $normal_status, // On Hold/Processing etc.
		);

		if ( false === wp_next_scheduled( 'carflows_schedule_normalize_order_status', $args ) ) {

			/* Filter to change the cron time */

			$cron_time = apply_filters( 'cartflows_order_status_cron_time', 30 );

			/* Setup Schedule */

			wp_schedule_single_event( time() + ( $cron_time * MINUTE_IN_SECONDS ), 'carflows_schedule_normalize_order_status', $args );

			wcf()->logger->log( 'Order-' . $order->get_id() . ' Cron Scheduled for Normalize Order Status' );
		}
	}

	/**
	 * Update main order data in transient.
	 *
	 * @param string $new_status new status.
	 * @param string $normal_status normal status.
	 * @param object $order order object.
	 * @since 1.0.0
	 */
	public function update_main_order_data_in_transient( $new_status, $normal_status, $order ) {

		if ( false === is_a( $order, 'WC_Order' ) ) {

			/* Not Valid Order */
			wcf()->logger->log( 'Not a valid order' );

			return;
		}

		wcf()->logger->log( 'new-status - ' . $new_status );

		if ( wcf_pro()->order->get_order_status_slug() !== $new_status ) {

			/* Not Valid Order Status */
			wcf()->logger->log( 'Not a valid order status' );

			return;
		}

		$flow_id = get_post_meta( $order->get_id(), '_wcf_flow_id', true );

		$data = array(
			'order_id'      => $order->get_id(),
			'before_normal' => $order->get_status(), // Pending.
			'normal_status' => $normal_status, // On Hold/Processing etc.
		);

		wcf()->logger->log( 'Gateway status change - Flow-' . $flow_id . ' Order-' . $order->get_id() . wp_json_encode( $data ) );

		wcf_pro()->session->update_data( $flow_id, $data );

		/* Add status change key order */
		$order->update_meta_data( '_cartflows_order_status_change', $data );
		$order->save();
	}

	/**
	 * Init Actions.
	 *
	 * @since 1.0.0
	 */
	public function init_actions() {

		$this->set_flow_session();
	}

	/**
	 * Set flow session.
	 *
	 * @since 1.0.0
	 */
	public function set_flow_session() {

		if ( wcf()->utils->is_step_post_type() ) {

			wcf()->utils->do_not_cache();

			$flow_id = wcf()->utils->get_flow_id();

			if ( ! $flow_id ) {
				return;
			}

			if ( _is_wcf_thankyou_type() ) {

				// Destroy Session On Thank You Page.
				wcf_pro()->session->destroy_session( $flow_id );

			} elseif ( _is_wcf_landing_type() || _is_wcf_checkout_type() ) {

				$data = array(
					'flow_id' => $flow_id,
					'steps'   => get_post_meta( $flow_id, 'wcf-steps', true ),
				);

				wcf_pro()->session->set_session( $flow_id, $data );

			} elseif ( _is_wcf_upsell_type() || _is_wcf_downsell_type() ) {

				if ( wcf()->flow->is_flow_testmode( $flow_id ) ) {
					return;
				}

				if ( ! ( is_user_logged_in() && current_user_can( 'manage_options' ) ) ) {

					if ( ! wcf_pro()->session->is_active_session( $flow_id ) ) {
						wp_die( esc_html__( 'Your session is expired', 'cartflows-pro' ) );
					}
				}
			}
		}

	}

	/**
	 * Setup upsell common.
	 *
	 * @param int $order_id Order id.
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function setup_upsell( $order_id = '' ) {

		wcf()->logger->log( 'Force setup upsell' );

		if ( '' == $order_id ) {
			return;
		}

		$order = wc_get_order( $order_id );

		$this->start_the_upsell_flow( $order );
	}

	/**
	 * Maybe setup upsell.
	 *
	 * @param int $order_id Order id.
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function maybe_setup_upsell( $order_id = '' ) {

		wcf()->logger->log( ' woocommerce_pre_payment_complete ' );

		if ( '' == $order_id ) {
			return;
		}

		$order = wc_get_order( $order_id );

		$this->start_the_upsell_flow( $order );

	}

	/**
	 * Ignore Gateways checkout processed.
	 *
	 * @param int    $order_id order id.
	 * @param array  $posted_data post data.
	 * @param object $order order object.
	 * @since 1.0.0
	 */
	public function maybe_setup_upsell_ignore_gateways( $order_id, $posted_data, $order ) {
		wcf()->logger->log( ' woocommerce_checkout_order_processed ' );

		if ( '' == $order_id ) {
			return;
		}

		// Added here again to solve the issue: Some times checkout was redirecting to default thank you page instead of upsell/downsell.
		$order_gateway = $order->get_payment_method();

		$gateways = array( 'bacs', 'stripe', 'ppec_paypal' );
		$gateways = apply_filters( 'cartflows_offer_supported_payment_gateway_slugs', $gateways );

		if ( in_array( $order_gateway, $gateways, true ) ) {

			$this->start_the_upsell_flow( $order );
		}
		// Added here again to solve the issue: Some times checkout was redirecting to default thank you page instead of upsell/downsell.
	}

	/**
	 * Start the upsell flow.
	 *
	 * @param object $order Order object.
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function start_the_upsell_flow( $order ) {

		if ( ! is_object( $order ) ) {

			wcf()->logger->log( 'Not valid order' );
		}

		if ( ! wcf_pro()->flow->is_upsell_exists( $order ) ) {

			wcf()->logger->log( 'Order-' . $order->get_id() . ' Upsell not exists' );

			return;
		}

		$order_gateway = $order->get_payment_method();

		$gateway_obj = wcf_pro()->gateways->load_gateway( $order_gateway );

		if ( $gateway_obj ) {

			wcf()->logger->log( 'Order-' . $order->get_id() . ' Flow Started' );

			/* Checkout recive url filter */
			do_action( 'cartflows_order_started', $order );

		} else {
			wcf()->logger->log( 'Order-' . $order->get_id() . ' Gateway object not found' );
		}
	}

	/**
	 * Set upsell and return new status based on condition.
	 *
	 * @since 1.5.5
	 * @param string $order_status order status.
	 * @param array  $order order data.
	 * @return string
	 */
	public function set_upsell_return_new_order_status( $order_status, $order ) {

		if ( false === is_a( $order, 'WC_Order' ) ) {

			// Create Log.
			wcf()->logger->log( 'Not a valid order' );

			return $order_status;
		}

		$flow_id = get_post_meta( $order->get_id(), '_wcf_flow_id', true );

		if ( ! wcf_pro()->flow->is_upsell_exists( $order ) ) {

			wcf()->logger->log( 'Flow-' . $flow_id . ' Order-' . $order->get_id() . ' Upsell not exists' );

			return $order_status;
		}

		do_action( 'cartflows_order_started', $order );

		/* If offer order is separate then don't change main order status */
		if ( ! wcf_pro()->utils->is_separate_offer_order() ) {

			$new_status = wcf_pro()->order->get_order_status_slug();

			$data = array(
				'flow_id'  => $flow_id,
				'order_id' => $order->get_id(),
			);

			wcf_pro()->session->set_session( $flow_id, $data );

			/**
			 * $new_status = our new status
			 * $order_status = default status change
			 */
			do_action( 'cartflows_order_status_change_to_main_order', $new_status, $order_status, $order );

			wcf()->logger->log( 'Flow-' . $flow_id . ' Order-' . $order->get_id() . ' Status changed to Main Order' );

			return $new_status;
		} else {
			wcf()->logger->log( 'Flow-' . $flow_id . ' Order-' . $order->get_id() . ' No need to change Status. Separate order option is set' );
		}

		return $order_status;
	}
	/**
	 * WP Actions.
	 *
	 * @since 1.0.0
	 */
	public function wp_actions() {

		if ( wcf()->utils->is_step_post_type() ) {

			add_action( 'wp_enqueue_scripts', array( $this, 'global_flow_scripts' ), 20 );

			/* Add pro version class to body frontend */
			add_filter( 'body_class', array( $this, 'pro_body_class' ) );
		}
	}

	/**
	 * Global flow scripts.
	 *
	 * @since 1.0.0
	 */
	public function global_flow_scripts() {

		if ( wcf()->utils->is_step_post_type() ) {

			wp_enqueue_style(
				'wcf-pro-frontend-global',
				wcf_pro()->utils->get_css_url( 'frontend' ),
				array(),
				CARTFLOWS_PRO_VER
			);

			wp_enqueue_script(
				'wcf-pro-frontend-global',
				wcf_pro()->utils->get_js_url( 'frontend' ),
				array( 'jquery' ),
				CARTFLOWS_PRO_VER,
				false
			);
		}
	}

	/**
	 * Add pro version class to body in frontend.
	 *
	 * @since 1.1.5
	 * @param array $classes classes.
	 * @return array $classes classes.
	 */
	public function pro_body_class( $classes ) {

		$classes[] = ' cartflows-pro-' . CARTFLOWS_PRO_VER;

		return $classes;
	}

}

/**
 *  Prepare if class 'Cartflows_Pro_Frontend' exist.
 *  Kicking this off by calling 'get_instance()' method
 */
Cartflows_Pro_Frontend::get_instance();