class-restai-woocommerce.php
1 <?php 2 /** 3 * WooCommerce hooks: AI-generated product descriptions, FAQ generation, and a 4 * product-aware support shortcode. 5 * 6 * Loads only when WooCommerce is active. 7 * 8 * @package RESTai 9 */ 10 11 namespace RESTai; 12 13 if ( ! defined( 'ABSPATH' ) ) { 14 exit; 15 } 16 17 class WooCommerce_Integration { 18 19 /** @var Client */ 20 private $client; 21 22 public function __construct( Client $client ) { 23 $this->client = $client; 24 add_action( 'init', array( $this, 'maybe_register' ) ); 25 add_shortcode( 'restai_product_faq', array( $this, 'product_faq_shortcode' ) ); 26 } 27 28 public function maybe_register() { 29 if ( ! class_exists( 'WooCommerce' ) ) { 30 return; 31 } 32 add_action( 'add_meta_boxes', array( $this, 'metabox' ) ); 33 add_action( 'wp_ajax_restai_generate_product_desc', array( $this, 'ajax_generate_description' ) ); 34 } 35 36 public function metabox() { 37 add_meta_box( 38 'restai_woocommerce', 39 __( 'RESTai (AI)', 'restai' ), 40 array( $this, 'metabox_view' ), 41 'product', 42 'side', 43 'low' 44 ); 45 } 46 47 public function metabox_view( $post ) { 48 wp_nonce_field( 'restai_product', '_restai_product_nonce' ); 49 echo '<p><button type="button" class="button" id="restai-gen-desc" data-post-id="' . esc_attr( $post->ID ) . '">' . esc_html__( 'Generate description', 'restai' ) . '</button></p>'; 50 echo '<p class="description">' . esc_html__( 'Calls the WP Product Writer project with this product\'s attributes.', 'restai' ) . '</p>'; 51 // Inline JS — keep it tiny. 52 ?> 53 <script> 54 jQuery(document).on('click', '#restai-gen-desc', function () { 55 var $btn = jQuery(this); 56 $btn.prop('disabled', true).text(<?php echo wp_json_encode( __( 'Generating…', 'restai' ) ); ?>); 57 jQuery.post(ajaxurl, { 58 action: 'restai_generate_product_desc', 59 _wpnonce: jQuery('#_restai_product_nonce').val(), 60 post_id: $btn.data('post-id'), 61 }).done(function (res) { 62 if (res && res.success && res.data && res.data.description) { 63 var ed = document.getElementById('content'); 64 if (ed) ed.value = res.data.description; 65 if (window.tinymce && window.tinymce.activeEditor) { 66 window.tinymce.activeEditor.setContent(res.data.description); 67 } 68 } else { 69 alert(<?php echo wp_json_encode( __( 'Could not generate description.', 'restai' ) ); ?>); 70 } 71 }).always(function () { 72 $btn.prop('disabled', false).text(<?php echo wp_json_encode( __( 'Generate description', 'restai' ) ); ?>); 73 }); 74 }); 75 </script> 76 <?php 77 } 78 79 public function ajax_generate_description() { 80 check_ajax_referer( 'restai_product', '_wpnonce' ); 81 $post_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : 0; 82 if ( ! $post_id || ! current_user_can( 'edit_post', $post_id ) ) { 83 wp_send_json_error( 'forbidden', 403 ); 84 } 85 86 $product = function_exists( 'wc_get_product' ) ? wc_get_product( $post_id ) : null; 87 if ( ! $product ) { 88 wp_send_json_error( 'not_a_product', 400 ); 89 } 90 91 $plugin = Plugin::instance(); 92 $project_id = $plugin->provisioner->project_for( 'product_writer' ); 93 if ( $project_id <= 0 ) { 94 wp_send_json_error( 'no_project', 400 ); 95 } 96 97 $attrs = array( 98 'name' => $product->get_name(), 99 'price' => $product->get_price(), 100 'sku' => $product->get_sku(), 101 'short' => $product->get_short_description(), 102 ); 103 $prompt = "Product attributes: " . wp_json_encode( $attrs ); 104 $desc = $this->client->ask( $project_id, $prompt ); 105 if ( is_wp_error( $desc ) ) { 106 wp_send_json_error( $desc->get_error_message(), 502 ); 107 } 108 wp_send_json_success( array( 'description' => $desc ) ); 109 } 110 111 /** 112 * [restai_product_faq] — renders an AI-generated FAQ for the current 113 * product, cached in a transient. 114 */ 115 public function product_faq_shortcode() { 116 if ( ! is_product() ) { 117 return ''; 118 } 119 $pid = get_the_ID(); 120 $key = 'restai_faq_' . $pid; 121 $cached = get_transient( $key ); 122 if ( false !== $cached ) { 123 return $cached; 124 } 125 $plugin = Plugin::instance(); 126 $project_id = $plugin->provisioner->project_for( 'product_writer' ); 127 if ( $project_id <= 0 ) { 128 return ''; 129 } 130 $product = wc_get_product( $pid ); 131 if ( ! $product ) { 132 return ''; 133 } 134 $prompt = "Generate an HTML <h3>FAQ</h3> with 5 likely customer questions and answers for this product:\n" . 135 'Name: ' . $product->get_name() . "\n" . 136 'Description: ' . wp_strip_all_tags( $product->get_description() ); 137 $out = $this->client->ask( $project_id, $prompt ); 138 if ( is_wp_error( $out ) ) { 139 return ''; 140 } 141 set_transient( $key, $out, WEEK_IN_SECONDS ); 142 return $out; 143 } 144 }