n array */ public function get_all_email_template_meta( $email_template_id ) { global $wpdb; $results = $wpdb->get_results( $wpdb->prepare( "SELECT meta_key, meta_value FROM {$wpdb->prefix}cartflows_ca_email_templates_meta WHERE email_template_id = %d", $email_template_id ), ARRAY_A ); $meta = []; foreach ( $results as $row ) { $meta[ $row['meta_key'] ] = maybe_unserialize( $row['meta_value'] ); } // Fill missing keys with defaults. $defaults = function_exists( 'wcf_ca' ) ? wcf_ca()->options->get_email_template_meta_defaults() : []; return array_merge( $defaults, $meta ); } /** * AJAX: Search WooCommerce products for ProductSearchField. * Handles both search by term and get products by comma-separated IDs. */ public function search_products() { if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_send_json_error( [ 'message' => $this->get_error_msg( 'permission' ) ] ); } $nonce = isset( $_POST['security'] ) ? sanitize_text_field( $_POST['security'] ) : ''; if ( ! wp_verify_nonce( $nonce, 'wcar_search_products' ) ) { wp_send_json_error( [ 'message' => $this->get_error_msg( 'nonce' ) ] ); } $term = isset( $_POST['term'] ) ? sanitize_text_field( wp_unslash( $_POST['term'] ) ) : ''; $product_ids = isset( $_POST['product_ids'] ) ? sanitize_text_field( wp_unslash( $_POST['product_ids'] ) ) : ''; $posts_per_page = isset( $_POST['posts_per_page'] ) ? absint( wp_unslash( $_POST['posts_per_page'] ) ) : 20; $products = []; if ( ! empty( $term ) && strlen( $term ) >= 3 ) { // Use WooCommerce's search method to search the products. $data = \WC_Data_Store::load( 'product' ); $ids = $data->search_products( $term, '', true, false, $posts_per_page ); // Get all product objects including variations. $product_objects = array_filter( array_map( 'wc_get_product', $ids ), 'wc_products_array_filter_readable' ); foreach ( $product_objects as $product_object ) { if ( ! $product_object ) { continue; } $products[] = $this->format_product_data( $product_object ); } } elseif ( ! empty( $product_ids ) ) { // Get products by IDs. $ids_array = array_filter( array_map( 'intval', explode( ',', $product_ids ) ) ); if ( ! empty( $ids_array ) ) { foreach ( $ids_array as $product_id ) { $product_object = wc_get_product( $product_id ); if ( $product_object ) { $products[] = $this->format_product_data( $product_object ); } } } } wp_send_json_success( $products ); } /** * Format product data for response. * * @param WC_Product $product_object Product object. * @return array Formatted product data. */ private function format_product_data( $product_object ) { $product_id = $product_object->get_id(); $image_id = get_post_thumbnail_id( $product_id ); $image_url = $image_id ? wp_get_attachment_image_url( $image_id, 'thumbnail' ) : ''; $managing_stock = $product_object->managing_stock(); $is_in_stock = $product_object->is_in_stock(); $product_type = $product_object->get_type(); $availibility_text = $this->get_stock_availability_text( $product_object ); $product_price_range = $this->get_formatted_product_price_range( $product_object ); // Format name for variations. $formatted_name = $product_object->get_name(); // phpcs:disable /* if ( $product_type === 'variation' ) { $formatted_name = $formatted_name . ' - ' . __( 'Variation', 'woo-cart-abandonment-recovery' ); } */ // phpcs:enable return [ 'value' => (string) $product_id, 'label' => $formatted_name, 'image' => $image_url, 'original_price' => $product_object->get_price(), 'product_name' => $product_object->get_name(), 'product_type' => $product_type, 'stock_status' => $availibility_text, 'in_stock' => $is_in_stock, 'price_range' => $product_price_range, ]; } /** * Get stock availability text for product. * * @param WC_Product $product_object Product object. * @return string Stock availability text. */ private function get_stock_availability_text( $product_object ) { if ( ! $product_object->is_in_stock() ) { return __( 'Out of stock', 'woo-cart-abandonment-recovery' ); } if ( $product_object->managing_stock() ) { $stock_quantity = $product_object->get_stock_quantity(); if ( $stock_quantity <= 0 ) { return __( 'Out of stock', 'woo-cart-abandonment-recovery' ); } return sprintf( // translators: %d: Number of items in stock. __( '%d in stock', 'woo-cart-abandonment-recovery' ), $stock_quantity ); } return __( 'In stock', 'woo-cart-abandonment-recovery' ); } /** * Get formatted product price range. * * @param WC_Product $product_object Product object. * @return string Formatted price range. */ private function get_formatted_product_price_range( $product_object ) { if ( $product_object->is_type( 'variable' ) ) { $prices = $product_object->get_variation_prices( true ); if ( ! empty( $prices['price'] ) ) { $min_price = current( $prices['price'] ); $max_price = end( $prices['price'] ); if ( $min_price !== $max_price ) { return wc_format_price_range( $min_price, $max_price ); } } } return $product_object->get_price_html(); } /** * Restore default email templates via AJAX. * * @since 2.0.0 * @return void */ public function restore_email_templates(): void { $response_data = [ 'message' => $this->get_error_msg( 'permission' ) ]; if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_send_json_error( $response_data ); } if ( ! check_ajax_referer( 'wcar_restore_email_templates', 'security', false ) ) { $response_data = [ 'message' => $this->get_error_msg( 'nonce' ) ]; wp_send_json_error( $response_data ); } include_once CARTFLOWS_CA_DIR . 'modules/cart-abandonment/classes/class-cartflows-ca-database.php'; $db = \Cartflows_Ca_Database::get_instance(); $db->template_table_seeder( true ); global $wpdb; $templates = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}cartflows_ca_email_templates ORDER BY id ASC" ); $email_templates = \Cartflows_Ca_Email_Templates::get_instance(); $formatted_templates = []; foreach ( $templates as $template ) { $meta = $email_templates->get_all_email_template_meta( $template->id ); $formatted_templates[] = array_merge( [ 'id' => $template->id, 'template_name' => $template->template_name, 'email_subject' => $template->email_subject, 'email_body' => $template->email_body, 'is_activated' => boolval( $template->is_activated ) ? 'on' : '', 'email_frequency' => $template->frequency, 'email_frequency_unit' => $template->frequency_unit, ], $meta ); } wp_send_json_success( [ 'message' => __( 'Default email templates restored successfully.', 'woo-cart-abandonment-recovery' ), 'templates' => $formatted_templates, ] ); } /** * Send preview email. * * @since 2.0.0 * @return void */ public function send_preview_email(): void { $response_data = [ 'message' => $this->get_error_msg( 'permission' ) ]; if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_send_json_error( $response_data ); } if ( empty( $_POST ) ) { $response_data = [ 'message' => $this->get_error_msg( 'missing-data' ) ]; wp_send_json_error( $response_data ); } /** * Nonce verification */ if ( ! check_ajax_referer( 'wcar_send_preview_email', 'security', false ) ) { $response_data = [ 'message' => $this->get_error_msg( 'nonce' ) ]; wp_send_json_error( $response_data ); } $email_schedule = \Cartflows_Ca_Email_Schedule::get_instance(); $mail_result = $email_schedule->send_email_templates( null, true ); if ( $mail_result ) { wp_send_json_success( [ 'message' => __( 'Test email sent successfully!', 'woo-cart-abandonment-recovery' ) ] ); } else { wp_send_json_error( [ 'message' => __( 'Failed to send test email', 'woo-cart-abandonment-recovery' ) ] ); } } }