/ wordpress / restai / includes / class-restai-woocommerce.php
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  }