File manager - Edit - /home/verseaumee/middletvnew26/wp-content/plugins/shopbuild/includes/class-storebuild-offer-manager.php
Back
<?php /** * The file that manages offer settings and logic. * * @since 1.0.0 * @package Storebuild * @subpackage Storebuild/includes */ if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } class StoreBuild_Offer_Manager { /** * Offer Key in Post Meta */ const CONFIG_KEY = '_sb_offer_config'; /** * Initialize the class and set its properties. * * @since 1.0.0 */ public function __construct() { // Enqueue Assets add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); // AJAX Hooks add_action( 'wp_ajax_storebuild_get_offers', array( $this, 'ajax_get_offers' ) ); add_action( 'wp_ajax_storebuild_create_offer', array( $this, 'ajax_create_offer' ) ); add_action( 'wp_ajax_storebuild_update_offer', array( $this, 'ajax_update_offer' ) ); add_action( 'wp_ajax_storebuild_delete_offer', array( $this, 'ajax_delete_offer' ) ); // Runtime Hook add_action( 'wp_footer', array( $this, 'render_runtime_behavior' ) ); } /** * Get Offers API */ public function ajax_get_offers() { if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( __( 'Permission denied', 'shopbuild' ) ); } $args = array( 'post_type' => 'shopbuild_offer', 'posts_per_page' => -1, 'post_status' => array('publish', 'draft'), ); $query = new WP_Query( $args ); $offers = array(); if ( $query->have_posts() ) { while ( $query->have_posts() ) { $query->the_post(); $post_id = get_the_ID(); $config = get_post_meta( $post_id, self::CONFIG_KEY, true ); // Main config // Fallback for empty config if ( ! is_array( $config ) ) { $config = $this->get_default_config( 'popup' ); // Default } $offers[] = array( 'id' => $post_id, 'title' => get_the_title(), 'status' => get_post_status(), 'config' => $config, 'edit_url' => \StoreBuild\Admin\StoreBuild_Admin::get_edit_with_elementor_link( $post_id ), ); } wp_reset_postdata(); } wp_send_json_success( $offers ); } /** * Create Offer API */ public function ajax_create_offer() { if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( __( 'Permission denied', 'shopbuild' ) ); } $title = isset($_POST['title']) ? sanitize_text_field( $_POST['title'] ) : 'New Offer'; $type = isset($_POST['type']) ? sanitize_text_field( $_POST['type'] ) : 'popup'; // Targeting Params $display_on = isset($_POST['display_on']) ? sanitize_text_field( $_POST['display_on'] ) : 'entire_site'; $target_id = isset($_POST['target_id']) ? intval( $_POST['target_id'] ) : 0; // 1. Create Post $post_id = wp_insert_post( array( 'post_title' => $title, 'post_type' => 'shopbuild_offer', 'post_status' => 'publish', ) ); if ( is_wp_error( $post_id ) ) { wp_send_json_error( $post_id->get_error_message() ); } // 2. Create Config with Targeting $config = $this->get_default_config( $type ); // Apply Targeting logic $config['targeting'] = array( 'include' => [], 'exclude' => [] ); switch ( $display_on ) { case 'entire_site': $config['targeting']['include'] = ['all']; break; case 'pages': $config['targeting']['include'] = ['pages']; break; case 'archive': $config['targeting']['include'] = ['archive']; break; case 'specific_page': if ( $target_id > 0 ) { $config['targeting']['include'] = ["id:$target_id"]; } else { $config['targeting']['include'] = ['all']; // Fallback } break; default: $config['targeting']['include'] = ['all']; } update_post_meta( $post_id, self::CONFIG_KEY, $config ); // 3. Setup Elementor specific meta update_post_meta( $post_id, '_elementor_edit_mode', 'builder' ); update_post_meta( $post_id, '_elementor_template_type', 'wp-post' ); // Or specific type if registered update_post_meta( $post_id, '_wp_page_template', 'elementor_canvas' ); // 4. Return Data wp_send_json_success( array( 'id' => $post_id, 'config' => $config, 'edit_url' => \StoreBuild\Admin\StoreBuild_Admin::get_edit_with_elementor_link( $post_id ), ) ); } /** * Update Offer Config API */ public function ajax_update_offer() { if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( __( 'Permission denied', 'shopbuild' ) ); } $post_id = intval( $_POST['id'] ); if ( ! $post_id ) { wp_send_json_error( __( 'Invalid ID', 'shopbuild' ) ); } // Decode JSON Payload (Assuming React sends 'config' as JSON string) $config_raw = isset( $_POST['config'] ) ? stripslashes( $_POST['config'] ) : ''; // Assuming JSON string $params = json_decode( $config_raw, true ); // Handle direct partial updates if not sending full JSON string if ( ! $params && isset( $_POST['settings'] ) ) { // Future enhancement: merge settings into config if separate fields implemented wp_send_json_error( __( 'Invalid Config Format', 'shopbuild' ) ); } if ( ! is_array( $params ) ) { wp_send_json_error( __( 'Invalid Config Data', 'shopbuild' ) ); } // Validate Config Schema (Basic) $config = $this->validate_config( $params ); // Save update_post_meta( $post_id, self::CONFIG_KEY, $config ); // Handle Status Change separately if needed (draft vs publish) if ( isset( $_POST['status'] ) ) { $status = sanitize_text_field( $_POST['status'] ); if ( in_array( $status, array( 'publish', 'draft' ) ) ) { wp_update_post( array( 'ID' => $post_id, 'post_status' => $status, ) ); } } wp_send_json_success( array( 'message' => __( 'Offer updated', 'shopbuild' ), 'config' => $config, ) ); } /** * Delete Offer API */ public function ajax_delete_offer() { if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( __( 'Permission denied', 'shopbuild' ) ); } $post_id = intval( $_POST['id'] ); if ( ! $post_id ) { wp_send_json_error( __( 'Invalid ID', 'shopbuild' ) ); } wp_delete_post( $post_id, true ); wp_send_json_success( __( 'Offer deleted', 'shopbuild' ) ); } private function is_elementor_editor() { // Elementor not loaded if ( ! did_action( 'elementor/loaded' ) ) { return false; } // Backend editor if ( isset( $_GET['action'] ) && $_GET['action'] === 'elementor' ) { return true; } // Frontend preview iframe if ( isset( $_GET['elementor-preview'] ) ) { return true; } // AJAX inside editor if ( wp_doing_ajax() && isset( $_POST['action'] ) && $_POST['action'] === 'elementor_ajax' ) { return true; } return false; } /** * Runtime Behavior: Inject JS Logic & Render HTML */ public function render_runtime_behavior() { // Stop inside Elementor editor / preview if ( $this->is_elementor_editor() ) { return; } // Fetch eligible offers $active_offers = $this->get_eligible_offers_payload(); if ( empty( $active_offers ) ) { return; } // Prepare Config Payload (for JS triggers) $configs = array(); // Render Markup Loop foreach ( $active_offers as $offer_id ) { $config = get_post_meta( $offer_id, self::CONFIG_KEY, true ); $type = isset( $config['type'] ) ? $config['type'] : 'popup'; $configs[$offer_id] = array( 'id' => $offer_id, 'config' => $config, ); // Render Elementor Content $content = ''; if ( did_action( 'elementor/loaded' ) ) { $content = \Elementor\Plugin::instance()->frontend->get_builder_content_for_display( $offer_id ); } if ( empty( $content ) ) { $content = get_post_field( 'post_content', $offer_id ); } // Ensure empty content still renders the popup shell if ( empty( trim( $content ) ) ) { $content = '<div class="storebuild-no-content">No contents set yet</div>'; } // Extract Size for inline styles $width = isset($config['size']['width']) ? $config['size']['width'] : '800px'; $height = isset($config['size']['height']) ? $config['size']['height'] : 'auto'; $radius = isset($config['size']['border_radius']) ? $config['size']['border_radius'] : '0px'; // Default radius // Ensure unit is present if just a number if (is_numeric($radius)) { $radius .= 'px'; } $wrapper_id = 'storebuild-offer-' . esc_attr($offer_id); // 1. Popup Markup if ($type === 'popup') { ?> <div id="<?php echo $wrapper_id; ?>" class="subscribe-popup d-flex justify-content-center align-items-center" style="display:none !important;"> <div class="aq-card-modal-style-4-overlay"> <div class="aq-card-modal-style-4-container" style="width: <?php echo esc_attr($width); ?>; height: <?php echo esc_attr($height); ?>; border-radius: <?php echo esc_attr($radius); ?> !important; overflow: hidden;"> <button class="aq-card-modal-style-4-close close"> <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" fill="none"> <path d="M13 1L1 13" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path> <path d="M1 1L13 13" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path> </svg> </button> <div class="aq-card-modal-style-4-wrapper"> <?php echo $content; ?> </div> </div> </div> </div> <?php } // 2. Banner/Other Markup (Fallback) else { $wrapper_class = "storebuild-offer-wrapper storebuild-type-$type"; $style = "width: 100%; height: " . esc_attr($height) . "; border-radius: " . esc_attr($radius) . "; background: #fff; z-index: 99999; position: relative; box-shadow: 0 4px 6px rgba(0,0,0,0.05);"; // Only hide if NOT banner if ( $type !== 'banner' ) { $style .= " display:none;"; } // Banners typically full width, distinct from popups ?> <div id="<?php echo $wrapper_id; ?>" class="<?php echo esc_attr($wrapper_class); ?>" style="<?php echo esc_attr($style); ?>"> <div class="storebuild-offer-content-box"> <!-- Close Button Positioned Absolute Right --> <button class="storebuild-offer-close" aria-label="Close" style="position: absolute; right: 15px; top: 50%; transform: translateY(-50%); z-index: 10; background: transparent; border: none; cursor: pointer;"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"></path></svg> </button> <div class="storebuild-offer-body"> <?php echo $content; ?> </div> </div> </div> <?php } } // Output to DOM for JS to pick up ?> <script> console.log('StoreBuild Payload:', <?php echo count($configs); ?> + ' offer(s)'); window.ShopBuildActiveOffers = <?php echo json_encode( $configs ); ?>; </script> <?php } /** * Helper: Get Eligible Offers based on Server-Side Rules (Pages, Roles, etc.) */ private function get_eligible_offers_payload() { // Query Published Offers $args = array( 'post_type' => 'shopbuild_offer', 'posts_per_page' => -1, 'post_status' => 'publish', 'fields' => 'ids', ); $candidates = get_posts( $args ); $eligible = array(); foreach ( $candidates as $id ) { $config = get_post_meta( $id, self::CONFIG_KEY, true ); // 1. Basic Eligibility Check (e.g. Include/Exclude URLs) if ( ! $this->check_targeting_rules( $config ) ) { continue; } $eligible[] = $id; } return $eligible; } /** * Check Targeting Rules */ private function check_targeting_rules( $config ) { if ( ! isset( $config['targeting'] ) ) return true; // Default match $rules = $config['targeting']; // Includes $include = isset($rules['include']) ? $rules['include'] : []; if( empty($include) ) $include = ['all']; // Excludes $exclude = isset($rules['exclude']) ? $rules['exclude'] : []; // Exclude Logic (Simple URL check for now, can be expanded) $current_url = $_SERVER['REQUEST_URI']; foreach($exclude as $rule) { if ( strpos( $current_url, $rule ) !== false ) return false; } // Include Logic $matched = false; // 1. Entire Site if ( in_array( 'all', $include ) ) { $matched = true; } // 2. Archives (category, tag, date, etc.) if ( ! $matched && in_array( 'archive', $include ) && is_archive() ) { $matched = true; } // 3. Pages (Post Type 'page') if ( ! $matched && in_array( 'pages', $include ) && is_page() ) { $matched = true; } // 4. Specific IDs or URL paths if ( ! $matched ) { foreach ( $include as $rule ) { // Formatting: "id:123" if ( strpos( $rule, 'id:' ) === 0 ) { $id = intval( substr( $rule, 3 ) ); // Check if current ID matches (general coverage) if ( (int) get_queried_object_id() === $id ) { $matched = true; break; } // WC Shop Page Special Check (since is_page often fails on Shop Archive) if ( function_exists('is_shop') && is_shop() && function_exists('wc_get_page_id') ) { if ( (int) wc_get_page_id('shop') === $id ) { $matched = true; break; } } // Standard Check if ( is_page( $id ) || is_single( $id ) ) { $matched = true; break; } } // Formatting: "/some-path" elseif ( strpos( $current_url, $rule ) !== false && $rule !== 'all' && $rule !== 'archive' && $rule !== 'pages' ) { $matched = true; break; } } } return $matched; } /** * Defaults */ private function get_default_config( $type ) { return array( 'version' => 1, 'type' => $type, // popup, banner 'placement' => 'center', // or header_top 'size' => array( 'width' => '500px', 'height' => 'auto', 'border_radius' => '8px' ), // Added border_radius 'trigger' => array( 'mode' => 'delay', 'delay_sec' => 2, 'duration_sec' => 0 ), // 0 = implementation dependent (forever until close) 'frequency' => array( 'limit' => 'session', 'days' => 0 ), 'targeting' => array( 'include' => ['all'], 'exclude' => [] ), 'priority' => 10, ); } /** * Validate Config */ private function validate_config( $params ) { // Ensure defaults for missing keys $defaults = $this->get_default_config( isset($params['type']) ? $params['type'] : 'popup' ); // Recursive merge not ideal for array replacements (like targeting), so merge top-level carefully $config = array_merge( $defaults, $params ); // Sanitize specific fields $config['size']['width'] = sanitize_text_field( $config['size']['width'] ); $config['size']['height'] = sanitize_text_field( $config['size']['height'] ); // ... more validation logic return $config; } public function enqueue_scripts() { // Enqueue frontend scripts (e.g. for popup behavior) // Only depend on elementor-frontend if it's actually registered, or rely on jQuery $deps = array( 'jquery' ); if ( wp_script_is( 'elementor-frontend', 'registered' ) ) { $deps[] = 'elementor-frontend'; } wp_enqueue_script( 'storebuild-offer-js', STOREBUILD_URL . 'public/js/storebuild-offer.js', $deps, '1.0.0', true ); wp_localize_script( 'storebuild-offer-js', 'storebuild_offer_params', array( 'ajax_url' => admin_url( 'admin-ajax.php' ) ) ); wp_enqueue_style( 'storebuild-offer-css', STOREBUILD_URL . 'public/css/pure-wc-shopbuild.css', array(), '1.0.0' ); } }
| ver. 1.4 |
Github
|
.
| PHP 8.5.7 | Generation time: 0 |
proxy
|
phpinfo
|
Settings