JezK
Edit File: class-cartflows-elementor-compatibility.php
<?php /** * Elementor page builder compatibility * * @package CartFlows */ namespace Elementor\Modules\PageTemplates; use Elementor\Core\Base\Document; use Elementor\Plugin; use Cartflows_Helper; use WP_REST_Request; use WP_REST_Response; // Exit if accessed directly. if ( ! defined( 'ABSPATH' ) ) { exit; } /** * Class for elementor page builder compatibility */ class Cartflows_Elementor_Compatibility { /** * 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() { add_filter( 'cartflows_page_template', array( $this, 'get_page_template' ) ); if ( wcf()->is_woo_active ) { // On Editor - Register WooCommerce frontend hooks before the Editor init. // Priority = 5, in order to allow plugins remove/add their wc hooks on init. if ( ! empty( $_REQUEST['action'] ) && 'elementor' === $_REQUEST['action'] && is_admin() ) {//phpcs:ignore WordPress.Security.NonceVerification.Recommended add_action( 'init', array( $this, 'register_wc_hooks' ), 5 ); } add_action( 'elementor/editor/before_enqueue_scripts', array( $this, 'maybe_init_cart' ) ); } add_action( 'wp_enqueue_scripts', array( $this, 'generate_gcp_elem_css_style' ), 10000 ); add_filter( 'rest_post_dispatch', array( $this, 'elementor_add_cartflows_colors' ), 1000, 3 ); add_filter( 'rest_request_after_callbacks', array( $this, 'display_cartflows_global_colors_front_end' ), 1000, 3 ); // Bypass Elementor's kit trash confirmation when trashing CartFlows posts. add_action( 'wp_trash_post', array( $this, 'skip_elementor_kit_trash_confirmation' ), 1 ); add_action( 'before_delete_post', array( $this, 'skip_elementor_kit_trash_confirmation' ), 1 ); } /** * Enqueue styles for elementor preview mode * * @since 2.1.8 * @return void */ public function generate_gcp_elem_css_style() { $gcp_vars = ''; $flow_id = wcf()->utils->get_flow_id(); if ( empty( $flow_id ) ) { return; } $gcp_vars_array = array_keys( Cartflows_Helper::get_gcp_vars() ); foreach ( $gcp_vars_array as $slug ) { // Gather value of global color VAR. $color_value = wcf()->options->get_flow_meta_value( $flow_id, $slug, '' ); if ( empty( $color_value ) ) { continue; } // Convert it into the CSS var. $slug = str_replace( '-', '', $slug ); $gcp_vars .= '--e-global-color-' . $slug . ': ' . $color_value . '; '; } $output = ':root { ' . $gcp_vars . ' }'; wp_add_inline_style( 'wcf-frontend-global', $output ); } /** * Get page template filter callback for elementor preview mode * * @param string $template page template. * @return string */ public function get_page_template( $template ) { if ( is_singular() ) { $document = Plugin::$instance->documents->get_doc_for_frontend( get_the_ID() ); if ( $document ) { $template = $document->get_meta( '_wp_page_template' ); } } return $template; } /** * Rgister wc hookes for elementor preview mode */ public function register_wc_hooks() { wc()->frontend_includes(); } /** * Init cart in elementor preview mode */ public function maybe_init_cart() { $has_cart = is_a( WC()->cart, 'WC_Cart' ); if ( ! $has_cart ) { $session_class = apply_filters( 'woocommerce_session_handler', 'WC_Session_Handler' ); WC()->session = new $session_class(); WC()->session->init(); WC()->cart = new \WC_Cart(); WC()->customer = new \WC_Customer( get_current_user_id(), true ); } } /** * Display theme global colors to Elementor Global colors. * * @since 2.1.8 * @param WP_REST_Response $response REST request response. * @param array{0: object, 1: string}|callable $handler Route handler used for the request. * @param WP_REST_Request<array<string, mixed>> $request Request used to generate the response. * @return WP_REST_Response */ public function elementor_add_cartflows_colors( $response, $handler, $request ) { $route = $request->get_route(); if ( '/elementor/v1/globals' !== $route ) { return $response; } // Ensure $data is an array. $data = $response->get_data(); if ( ! is_array( $data ) ) { return $response; // Ensure `$data` is an array before accessing keys. } $referer_url = isset( $_SERVER['HTTP_REFERER'] ) ? esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) : ''; $parsed_url = parse_url( $referer_url ); //phpcs:ignore WordPress.WP.AlternativeFunctions.parse_url_parse_url $post_id = 0; // Check if the query part exists in the URL. if ( isset( $parsed_url['query'] ) ) { parse_str( $parsed_url['query'], $query_params ); $post_id = isset( $query_params['post'] ) ? (int) $query_params['post'] : 0; } // Get flow ID from CartFlows. $flow_id = wcf()->utils->get_flow_id_from_step_id( $post_id ); // Generate global palette. $global_palette = Cartflows_Helper::generate_css_var_array( $flow_id, 'elementor' ); // Ensure 'colors' exists in the array before merging. if ( ! isset( $data['colors'] ) || ! is_array( $data['colors'] ) ) { $data['colors'] = array(); } $data['colors'] = array_merge( $data['colors'], $global_palette ); $response->set_data( $data ); return $response; } /** * Display theme global colors to Elementor Global colors. * * @since 2.1.8 * @param WP_REST_Response $response REST request response. * @param array{0: object, 1: string}|callable $handler Route handler used for the request. * @param WP_REST_Request<array<string, mixed>> $request Request used to generate the response. * @return WP_REST_Response */ public function display_cartflows_global_colors_front_end( $response, $handler, $request ) { $route = $request->get_route(); if ( 0 !== strpos( $route, '/elementor/v1/globals' ) ) { return $response; } $post_id = get_the_ID(); // Get flow ID from CartFlows. $flow_id = wcf()->utils->get_flow_id_from_step_id( $post_id ); // Generate global palette. $slug_map = array(); $global_palette = Cartflows_Helper::generate_css_var_array( $flow_id, 'elementor' ); $css_vars = Cartflows_Helper::get_gcp_vars(); foreach ( $css_vars as $key => $slug ) { // Remove hyphens as hyphens do not work with Elementor global styles. $no_hyphens = str_replace( '-', '', $key ); $slug_map[ $no_hyphens ] = $no_hyphens; } $rest_id = substr( $route, strrpos( $route, '/' ) + 1 ); if ( ! in_array( $rest_id, array_keys( $slug_map ), true ) ) { return $response; } $response = rest_ensure_response( array( 'id' => esc_attr( $rest_id ), 'title' => $slug_map[ $rest_id ], 'value' => $global_palette[ $slug_map[ $rest_id ] ], ) ); return $response; } /** * Bypass Elementor's kit trash confirmation when a CartFlows post is being trashed or deleted. * * Elementor's Manager::before_delete_kit() hooks into `wp_trash_post` and `before_delete_post` * at priority 10 and calls wp_die() with a confirmation popup when it detects an active kit * is being trashed. It already provides a bypass via the `force_delete_kit` GET flag. * Setting it here at priority 1 — before Elementor's hook fires — ensures the check is * skipped for CartFlows flow and step post types without affecting any other post types. * A valid nonce for any CartFlows trash or delete action is required before bypassing. * * @since 2.2.2 * @param int $post_id Post ID being trashed or deleted. * @return void */ public function skip_elementor_kit_trash_confirmation( $post_id ) { // Get the post type of the post being trashed or deleted. $post_type = get_post_type( $post_id ); // Only proceed if the post type is a CartFlows Flow or Step. if ( ! in_array( $post_type, array( CARTFLOWS_FLOW_POST_TYPE, CARTFLOWS_STEP_POST_TYPE ), true ) ) { return; } // Retrieve and sanitize the AJAX action and security nonce if present. // These are set by CartFlows when performing trash/delete via AJAX or bulk actions. $ajax_action = isset( $_POST['action'] ) ? sanitize_text_field( wp_unslash( $_POST['action'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing $nonce = isset( $_POST['security'] ) ? sanitize_text_field( wp_unslash( $_POST['security'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing // List of CartFlows AJAX actions that trigger trash or delete operations. $cartflows_delete_actions = array( 'cartflows_trash_flows_in_bulk', // Bulk trash flows. 'cartflows_trash_flow', // Trash a single flow. 'cartflows_delete_flow', // Permanently delete a flow. 'cartflows_delete_flows_permanently', // Bulk permanently delete flows. 'cartflows_delete_step', // Delete a step. ); // Only set the `force_delete_kit` flag if a recognized CartFlows delete action is being run and the nonce is valid. if ( in_array( $ajax_action, $cartflows_delete_actions, true ) && wp_verify_nonce( $nonce, $ajax_action ) ) { // Set the `force_delete_kit` GET parameter so Elementor skips its trash confirmation for kits. // This prevents interruption for CartFlows flows and steps during delete/trash. $_GET['force_delete_kit'] = '1'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended } } } /** * Kicking this off by calling 'get_instance()' method */ Cartflows_Elementor_Compatibility::get_instance();