register_frontend(); } } public static function load_shared() { $dir = defined( 'AIEO_DIR' ) ? AIEO_DIR : trailingslashit( dirname( __FILE__, 4 ) ); $map = array( 'AIEO_Product_Request_Settings' => 'includes/dmm/product-requests/class-aieo-product-request-settings.php', 'AIEO_Product_Request_Store' => 'includes/dmm/product-requests/class-aieo-product-request-store.php', 'AIEO_Product_Request_Trail' => 'includes/dmm/product-requests/class-aieo-product-request-trail.php', 'AIEO_Product_Request_Form' => 'includes/dmm/product-requests/class-aieo-product-request-form.php', 'AIEO_Product_Request_Ajax' => 'includes/dmm/product-requests/class-aieo-product-request-ajax.php', 'AIEO_Product_Request_Mailer' => 'includes/dmm/product-requests/class-aieo-email-product-request.php', ); foreach ( $map as $class => $rel ) { if ( class_exists( $class ) ) { continue; } $path = $dir . $rel; if ( is_readable( $path ) ) { require_once $path; } } } public function register_notify_worker() { if ( ! class_exists( 'AIEO_Product_Request_Ajax' ) ) { return; } if ( defined( 'AIEO_RUNTIME_OWNS_PRODUCT_REQUESTS' ) ) { return; } AIEO_Product_Request_Ajax::register(); } public function register_frontend() { if ( ! class_exists( 'AIEO_Product_Request_Form' ) ) { return; } add_action( 'init', array( 'AIEO_Product_Request_Form', 'register_frontend' ), 9 ); } public function maybe_install() { $schema = defined( 'AIEO_DIR' ) ? AIEO_DIR . 'includes/dmm/product-requests/sql/product-requests-schema.php' : ''; if ( '' === $schema || ! is_readable( $schema ) ) { return; } require_once $schema; if ( function_exists( 'aieo_install_schema_once' ) ) { aieo_install_schema_once( self::SCHEMA_OPTION, self::SCHEMA_VERSION, 'aieo_product_requests_install_schema' ); return; } if ( get_option( self::SCHEMA_OPTION, '' ) !== self::SCHEMA_VERSION ) { aieo_product_requests_install_schema(); update_option( self::SCHEMA_OPTION, self::SCHEMA_VERSION, false ); } } public function register_email_class( $emails ) { if ( ! is_array( $emails ) ) { return $emails; } if ( ! class_exists( 'AIEO_Product_Request_Mailer' ) ) { self::load_shared(); } if ( ! class_exists( 'AIEO_Product_Request_Mailer' ) || ! AIEO_Product_Request_Mailer::load_wc_email() ) { return $emails; } if ( ! isset( $emails['aieo_product_request'] ) ) { $emails['aieo_product_request'] = new AIEO_Email_Product_Request(); } return $emails; } public function render_product_override() { global $post; if ( ! $post ) { return; } $val = (string) get_post_meta( (int) $post->ID, AIEO_Product_Request_Settings::OVERRIDE_META, true ); echo '
'; woocommerce_wp_select( array( 'id' => '_aieo_product_request_display', 'label' => __( 'Product request card', 'ai-eshop-optimizer' ), 'description' => __( 'Overrides the site-wide display rule for this product only.', 'ai-eshop-optimizer' ), 'desc_tip' => true, 'value' => $val, 'options' => array( '' => __( 'Follow the site rule', 'ai-eshop-optimizer' ), 'show' => __( 'Always show it here', 'ai-eshop-optimizer' ), 'hide' => __( 'Never show it here', 'ai-eshop-optimizer' ), ), ) ); echo '
'; } public function save_product_override( $post_id ) { $post_id = (int) $post_id; if ( $post_id <= 0 ) { return; } $v = isset( $_POST['_aieo_product_request_display'] ) ? sanitize_key( wp_unslash( (string) $_POST['_aieo_product_request_display'] ) ) : ''; if ( ! in_array( $v, array( '', 'show', 'hide' ), true ) ) { $v = ''; } if ( '' === $v ) { delete_post_meta( $post_id, AIEO_Product_Request_Settings::OVERRIDE_META ); } else { update_post_meta( $post_id, AIEO_Product_Request_Settings::OVERRIDE_META, $v ); } } public function get_settings( $raw = false ) { return AIEO_Product_Request_Settings::get( $raw ); } public function render_subtab() { self::load_shared(); $request_id = isset( $_GET['request_id'] ) ? (int) $_GET['request_id'] : 0; if ( $request_id > 0 ) { $request = AIEO_Product_Request_Store::get_request( $request_id ); if ( $request ) { $this->render_template( 'product-requests-detail.php', array( 'request' => $request, 'trail' => AIEO_Product_Request_Trail::for_request( $request ), 'attr' => AIEO_Product_Request_Store::attribution_of( $request ), 'settings' => $this->get_settings(), ) ); return; } } $per_page = 50; $paged = max( 1, isset( $_GET['pr_paged'] ) ? (int) $_GET['pr_paged'] : 1 ); $filters = array( 'status' => isset( $_GET['pr_status'] ) ? sanitize_key( wp_unslash( (string) $_GET['pr_status'] ) ) : '', 'type' => isset( $_GET['pr_type'] ) ? sanitize_key( wp_unslash( (string) $_GET['pr_type'] ) ) : '', 'campaign' => isset( $_GET['pr_campaign'] ) ? sanitize_text_field( wp_unslash( (string) $_GET['pr_campaign'] ) ) : '', 'from' => isset( $_GET['pr_from'] ) ? sanitize_text_field( wp_unslash( (string) $_GET['pr_from'] ) ) : '', 'to' => isset( $_GET['pr_to'] ) ? sanitize_text_field( wp_unslash( (string) $_GET['pr_to'] ) ) : '', 'search' => isset( $_GET['pr_s'] ) ? sanitize_text_field( wp_unslash( (string) $_GET['pr_s'] ) ) : '', 'per_page' => $per_page, 'paged' => $paged, ); $listing = AIEO_Product_Request_Store::list_requests( $filters ); $this->render_template( 'product-requests.php', array( 'settings' => $this->get_settings( true ), 'i18n_defaults' => $this->get_settings(), 'rows' => $listing['rows'], 'total' => $listing['total'], 'per_page' => $per_page, 'current_page' => $paged, 'filters' => $filters, 'status_counts' => AIEO_Product_Request_Store::status_counts(), 'recipients' => AIEO_Product_Request_Mailer::recipients( 'quote' ), ) ); } public function ajax_save_settings() { AIEO_DMM::verify_ajax_request(); self::load_shared(); $existing = get_option( AIEO_Product_Request_Settings::OPTION_KEY, array() ); if ( ! is_array( $existing ) ) { $existing = array(); } $data = array(); foreach ( array( 'enabled', 'frontend_enabled', 'ack_enabled', 'finish_enabled', 'field_name', 'field_phone', 'field_qty', 'field_message', 'field_consent', ) as $k ) { if ( isset( $_POST[ 'has_' . $k ] ) ) { $data[ $k ] = ! empty( $_POST[ $k ] ) ? 'yes' : 'no'; } } foreach ( array( 'form_title', 'form_intro', 'form_button_text', 'field_email_label', 'title_contact', 'title_callback', 'button_contact', 'button_callback', 'field_name_label', 'field_phone_label', 'field_qty_label', 'field_message_label', 'consent_text', 'consent_link_text', 'add_button_text', 'add_button_text_first', 'added_text', 'remove_button_text', 'review_text', 'basket_title', 'basket_empty_text', 'remove_text', 'finish_button_text', 'ack_subject', 'recipients', 'recipients_quote', 'recipients_contact', 'recipients_callback', ) as $k ) { if ( isset( $_POST[ $k ] ) ) { $data[ $k ] = sanitize_text_field( wp_unslash( (string) $_POST[ $k ] ) ); } } foreach ( array( 'form_success_message', 'ack_message', 'basket_empty_text' ) as $k ) { if ( isset( $_POST[ $k ] ) ) { $data[ $k ] = sanitize_textarea_field( wp_unslash( (string) $_POST[ $k ] ) ); } } if ( isset( $_POST['consent_url'] ) ) { $data['consent_url'] = esc_url_raw( wp_unslash( (string) $_POST['consent_url'] ) ); } if ( isset( $_POST['display_rule'] ) ) { $rule = sanitize_key( wp_unslash( (string) $_POST['display_rule'] ) ); $data['display_rule'] = in_array( $rule, array( 'always', 'not_purchasable', 'out_of_stock', 'taxonomy' ), true ) ? $rule : 'not_purchasable'; } if ( isset( $_POST['placement'] ) ) { $place = sanitize_key( wp_unslash( (string) $_POST['placement'] ) ); $data['placement'] = ( 'manual' === $place ) ? 'manual' : 'automatic'; } if ( isset( $_POST['basket_storage'] ) ) { $mode = sanitize_key( wp_unslash( (string) $_POST['basket_storage'] ) ); $data['basket_storage'] = ( 'local' === $mode ) ? 'local' : 'session'; } if ( isset( $_POST['request_types'] ) ) { $data['request_types'] = AIEO_Product_Request_Settings::clean_types( wp_unslash( (array) $_POST['request_types'] ) ); } if ( isset( $_POST['rule_terms'] ) ) { $data['rule_terms'] = array_values( array_filter( array_map( 'absint', (array) wp_unslash( $_POST['rule_terms'] ) ) ) ); } $clamps = array( 'notify_delay_minutes' => array( 0, 1440 ), 'trail_lookback_hours' => array( 1, 168 ), 'basket_ttl_hours' => array( 1, 720 ), 'basket_local_hours' => array( 1, 720 ), 'max_items' => array( 1, AIEO_Product_Request_Settings::MAX_ITEMS_CEILING ), 'title_size' => array( 8, 48 ), 'text_size' => array( 8, 32 ), 'border_width' => array( 0, 10 ), 'border_radius' => array( 0, 40 ), 'btn_border_width' => array( 0, 10 ), 'btn_border_radius' => array( 0, 40 ), ); foreach ( $clamps as $k => $range ) { if ( isset( $_POST[ $k ] ) ) { $data[ $k ] = max( $range[0], min( $range[1], (int) wp_unslash( $_POST[ $k ] ) ) ); } } foreach ( array( 'bg_color', 'border_color', 'title_color', 'text_color', 'btn_bg_color', 'btn_text_color', 'btn_border_color' ) as $k ) { if ( ! isset( $_POST[ $k ] ) ) { continue; } $raw = trim( (string) wp_unslash( $_POST[ $k ] ) ); if ( '' === $raw ) { $data[ $k ] = ''; continue; } $v = sanitize_hex_color( $raw ); if ( null !== $v && '' !== $v ) { $data[ $k ] = $v; } } update_option( AIEO_Product_Request_Settings::OPTION_KEY, array_merge( $existing, $data ), false ); $this->purge_page_caches(); wp_send_json_success( array( 'message' => __( 'Product request settings saved.', 'ai-eshop-optimizer' ) ) ); } private function purge_page_caches() { do_action( 'aieo_purge_page_cache' ); do_action( 'aieo_product_requests_settings_saved' ); } public function ajax_update_request() { AIEO_DMM::verify_ajax_request(); self::load_shared(); $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0; if ( $id <= 0 ) { wp_send_json_error( array( 'message' => __( 'Invalid request.', 'ai-eshop-optimizer' ) ) ); } $ok = AIEO_Product_Request_Store::update_admin_fields( $id, isset( $_POST['status'] ) ? sanitize_key( wp_unslash( (string) $_POST['status'] ) ) : 'new', isset( $_POST['admin_note'] ) ? wp_unslash( (string) $_POST['admin_note'] ) : '' ); if ( ! $ok ) { wp_send_json_error( array( 'message' => __( 'Could not save.', 'ai-eshop-optimizer' ) ) ); } wp_send_json_success( array( 'message' => __( 'Saved.', 'ai-eshop-optimizer' ) ) ); } public function ajax_delete_request() { AIEO_DMM::verify_ajax_request(); self::load_shared(); $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0; if ( $id <= 0 ) { wp_send_json_error( array( 'message' => __( 'Invalid request.', 'ai-eshop-optimizer' ) ) ); } AIEO_Product_Request_Store::delete_request( $id ); wp_send_json_success(); } public function export_csv() { if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_die( esc_html__( 'You do not have permission to export this.', 'ai-eshop-optimizer' ) ); } check_admin_referer( 'aieo_product_requests_export' ); self::load_shared(); $listing = AIEO_Product_Request_Store::list_requests( array( 'status' => isset( $_GET['pr_status'] ) ? sanitize_key( wp_unslash( (string) $_GET['pr_status'] ) ) : '', 'type' => isset( $_GET['pr_type'] ) ? sanitize_key( wp_unslash( (string) $_GET['pr_type'] ) ) : '', 'from' => isset( $_GET['pr_from'] ) ? sanitize_text_field( wp_unslash( (string) $_GET['pr_from'] ) ) : '', 'to' => isset( $_GET['pr_to'] ) ? sanitize_text_field( wp_unslash( (string) $_GET['pr_to'] ) ) : '', 'search' => isset( $_GET['pr_s'] ) ? sanitize_text_field( wp_unslash( (string) $_GET['pr_s'] ) ) : '', 'per_page' => 200, 'paged' => 1, ) ); nocache_headers(); header( 'Content-Type: text/csv; charset=utf-8' ); header( 'Content-Disposition: attachment; filename=product-requests-' . gmdate( 'Y-m-d' ) . '.csv' ); $out = fopen( 'php://output', 'w' ); fwrite( $out, "\xEF\xBB\xBF" ); fputcsv( $out, array( 'ID', 'Date (UTC)', 'Type', 'Status', 'Name', 'Email', 'Phone', 'Items', 'Viewed while open', 'Campaign', 'Source', 'Medium', 'Message' ) ); foreach ( $listing['rows'] as $r ) { $a = AIEO_Product_Request_Store::attribution_of( $r ); fputcsv( $out, array( (int) $r->id, (string) $r->created_at, (string) $r->request_type, (string) $r->status, (string) $r->name, (string) $r->email, (string) $r->phone, (int) $r->item_count, (int) $r->view_count, (string) ( $a['utm_campaign'] ?? '' ), (string) ( $a['utm_source'] ?? '' ), (string) ( $a['utm_medium'] ?? '' ), (string) $r->message, ) ); } fclose( $out ); exit; } public static function badge_count() { if ( ! class_exists( 'AIEO_Product_Request_Store' ) ) { return 0; } return AIEO_Product_Request_Store::count_new(); } }