allowed_fields;
}
public function __construct() {
add_action( 'init', array( $this, 'register_product_meta' ) );
add_action( 'add_meta_boxes_product', array( $this, 'add_product_meta_box' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) );
add_action( 'rest_api_init', array( $this, 'register_routes' ) );
}
public function register_product_meta() {
register_post_meta(
'product',
self::META_KEY,
array(
'type' => 'string',
'single' => true,
'show_in_rest' => true,
'auth_callback' => function() {
return current_user_can( 'edit_products' ) || current_user_can( 'edit_posts' );
},
)
);
register_post_meta(
'product',
self::META_GENERATED_AT,
array(
'type' => 'string',
'single' => true,
'show_in_rest' => true,
'sanitize_callback' => 'sanitize_text_field',
'auth_callback' => function() {
return current_user_can( 'edit_products' ) || current_user_can( 'edit_posts' );
},
)
);
}
public function add_product_meta_box() {
add_meta_box(
'wpthemoon-product-generator',
__( 'AI Product Generator', 'wpthemoon' ),
array( $this, 'render_product_meta_box' ),
'product',
'side',
'high'
);
}
public function render_product_meta_box( $post ) {
echo '
';
}
public function enqueue_admin_assets( $hook ) {
if ( ! in_array( $hook, array( 'post.php', 'post-new.php' ), true ) ) {
return;
}
$screen = get_current_screen();
if ( ! $screen || 'product' !== $screen->post_type ) {
return;
}
$asset_path = WPTHEMOON_PATH . 'build/product-generator.asset.php';
$asset_file = file_exists( $asset_path )
? include $asset_path
: array(
'dependencies' => array( 'wp-element', 'wp-components', 'wp-api-fetch', 'wp-i18n' ),
'version' => WPTHEMOON_VERSION,
);
wp_enqueue_script(
'wpthemoon-product-generator',
WPTHEMOON_URL . 'build/product-generator.js',
$asset_file['dependencies'],
$asset_file['version'],
true
);
wp_enqueue_style(
'wpthemoon-product-generator-style',
WPTHEMOON_URL . 'build/product-generator.css',
array(),
$asset_file['version']
);
$product_id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : 0;
wp_localize_script(
'wpthemoon-product-generator',
'wpthemoonProductGenerator',
array(
'root' => esc_url_raw( rest_url() ),
'nonce' => wp_create_nonce( 'wp_rest' ),
'productId' => $product_id,
'defaultAudience' => get_option( 'wpthemoon_target_audience' ),
'defaultLanguage' => substr( get_locale(), 0, 2 ),
)
);
wp_set_script_translations( 'wpthemoon-product-generator', 'wpthemoon' );
}
public function register_routes() {
$namespace = function_exists( 'wpthemoon_rest_namespace' ) ? wpthemoon_rest_namespace() : apply_filters( 'wpthemoon_rest_namespace', 'wpthemoon/v1' );
register_rest_route(
$namespace,
'/woocommerce/product/(?P\d+)',
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_product_context' ),
'permission_callback' => array( $this, 'check_product_permission' ),
)
);
register_rest_route(
$namespace,
'/woocommerce/product/generate',
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array( $this, 'generate_product_content' ),
'permission_callback' => array( $this, 'check_product_permission' ),
)
);
register_rest_route(
$namespace,
'/woocommerce/product/apply',
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array( $this, 'apply_product_content' ),
'permission_callback' => array( $this, 'check_product_permission' ),
)
);
}
public function check_product_permission( WP_REST_Request $request ) {
$product_id = absint( $request->get_param( 'id' ) );
if ( ! $product_id ) {
$product_id = absint( $request->get_param( 'productId' ) );
}
$product_payload = $request->get_param( 'product' );
if ( ! $product_id && is_array( $product_payload ) && isset( $product_payload['id'] ) ) {
$product_id = absint( $product_payload['id'] );
}
if ( $product_id ) {
return current_user_can( 'edit_post', $product_id );
}
return current_user_can( 'edit_products' );
}
public function get_product_context( WP_REST_Request $request ) {
$product = $this->get_product_or_error( absint( $request->get_param( 'id' ) ) );
if ( is_wp_error( $product ) ) {
return $product;
}
return rest_ensure_response( $this->build_product_context( $product ) );
}
public function generate_product_content( WP_REST_Request $request ) {
$product_id = absint( $request->get_param( 'productId' ) );
$product = $this->get_product_or_error( $product_id );
if ( is_wp_error( $product ) ) {
return $product;
}
$options = $this->sanitize_generation_options( $request->get_param( 'options' ) );
$client = new WPTheMoon_API_Client();
$result = $client->post(
'/woocommerce/product',
array(
'product' => $this->build_product_context( $product ),
'options' => $options,
)
);
if ( is_wp_error( $result ) ) {
return $this->ensure_api_result( $result );
}
delete_transient( 'wpthemoon_credits_cache' );
return rest_ensure_response( $result );
}
public function apply_product_content( WP_REST_Request $request ) {
$product_id = absint( $request->get_param( 'productId' ) );
$generated = $request->get_param( 'generated' );
if ( ! is_array( $generated ) ) {
return new WP_Error( 'invalid_generated_content', __( 'Generated content is required.', 'wpthemoon' ), array( 'status' => 400 ) );
}
$fields = $this->sanitize_fields( $request->get_param( 'fields' ) );
if ( empty( $fields ) ) {
return new WP_Error( 'invalid_fields', __( 'Select at least one field to apply.', 'wpthemoon' ), array( 'status' => 400 ) );
}
$result = $this->apply_generated_content( $product_id, $generated, $fields );
if ( is_wp_error( $result ) ) {
return $result;
}
return rest_ensure_response( $result );
}
public function apply_generated_content( $product_id, $generated, $fields, $source = 'single', $extra_metadata = array() ) {
$product = $this->get_product_or_error( absint( $product_id ) );
if ( is_wp_error( $product ) ) {
return $product;
}
if ( ! is_array( $generated ) ) {
return new WP_Error( 'invalid_generated_content', __( 'Generated content is required.', 'wpthemoon' ), array( 'status' => 400 ) );
}
$fields = $this->sanitize_fields( $fields );
if ( empty( $fields ) ) {
return new WP_Error( 'invalid_fields', __( 'Select at least one field to apply.', 'wpthemoon' ), array( 'status' => 400 ) );
}
$post_updates = array( 'ID' => $product_id );
$applied = array();
if ( in_array( 'title', $fields, true ) && ! empty( $generated['title'] ) ) {
$post_updates['post_title'] = sanitize_text_field( $generated['title'] );
$applied[] = 'title';
}
if ( in_array( 'shortDescription', $fields, true ) && ! empty( $generated['shortDescription'] ) ) {
$post_updates['post_excerpt'] = wp_kses_post( $generated['shortDescription'] );
$applied[] = 'shortDescription';
}
$next_description = null;
if ( in_array( 'description', $fields, true ) && ! empty( $generated['description'] ) ) {
$next_description = wp_kses_post( $generated['description'] );
$applied[] = 'description';
}
if ( in_array( 'faq', $fields, true ) && ! empty( $generated['faq'] ) && is_array( $generated['faq'] ) ) {
$base_description = null !== $next_description ? $next_description : get_post_field( 'post_content', $product_id );
$faq_html = $this->build_faq_html( $generated['faq'] );
if ( $faq_html ) {
$next_description = trim( $base_description . "\n\n" . $faq_html );
$applied[] = 'faq';
}
}
if ( null !== $next_description ) {
$post_updates['post_content'] = wp_kses_post( $next_description );
}
if ( count( $post_updates ) > 1 ) {
$result = wp_update_post( wp_slash( $post_updates ), true );
if ( is_wp_error( $result ) ) {
return new WP_Error( 'product_update_failed', $result->get_error_message(), array( 'status' => 500 ) );
}
}
if ( in_array( 'tags', $fields, true ) && ! empty( $generated['tags'] ) && is_array( $generated['tags'] ) ) {
$tags = array_values( array_filter( array_map( 'sanitize_text_field', $generated['tags'] ) ) );
if ( ! empty( $tags ) ) {
$tag_result = wp_set_object_terms( $product_id, $tags, 'product_tag', false );
if ( is_wp_error( $tag_result ) ) {
return new WP_Error( 'product_tags_update_failed', $tag_result->get_error_message(), array( 'status' => 500 ) );
}
$applied[] = 'tags';
}
}
if ( in_array( 'seo', $fields, true ) && ! empty( $generated['seo'] ) && is_array( $generated['seo'] ) ) {
$applied[] = 'seo';
}
$applied = array_values( array_unique( $applied ) );
if ( empty( $applied ) ) {
return new WP_Error( 'no_fields_applied', __( 'No generated fields were available to apply.', 'wpthemoon' ), array( 'status' => 400 ) );
}
$metadata = array(
'generated' => $generated,
'applied_fields' => $applied,
'source' => sanitize_text_field( $source ),
'generated_at' => current_time( 'mysql' ),
);
if ( is_array( $extra_metadata ) && ! empty( $extra_metadata ) ) {
$metadata = array_merge( $metadata, $extra_metadata );
}
update_post_meta( $product_id, self::META_KEY, wp_json_encode( $metadata ) );
update_post_meta( $product_id, self::META_GENERATED_AT, $metadata['generated_at'] );
$updated_product = wc_get_product( $product_id );
return rest_ensure_response(
array(
'productId' => $product_id,
'applied' => $applied,
'product' => $updated_product ? $this->build_product_context( $updated_product ) : null,
)
);
}
private function get_product_or_error( $product_id ) {
if ( ! class_exists( 'WooCommerce' ) || ! function_exists( 'wc_get_product' ) ) {
return new WP_Error( 'woocommerce_inactive', __( 'WooCommerce is not active.', 'wpthemoon' ), array( 'status' => 400 ) );
}
$product = wc_get_product( $product_id );
if ( ! $product || 'product' !== get_post_type( $product_id ) ) {
return new WP_Error( 'invalid_product', __( 'A valid WooCommerce product is required.', 'wpthemoon' ), array( 'status' => 404 ) );
}
if ( ! current_user_can( 'edit_post', $product_id ) ) {
return new WP_Error( 'forbidden', __( 'You do not have permission to edit this product.', 'wpthemoon' ), array( 'status' => 403 ) );
}
return $product;
}
public function build_product_context( WC_Product $product ) {
$product_id = $product->get_id();
return array(
'id' => $product_id,
'name' => $product->get_name(),
'type' => $product->get_type(),
'status' => get_post_status( $product_id ),
'sku' => $product->get_sku(),
'regularPrice' => $product->get_regular_price(),
'salePrice' => $product->get_sale_price(),
'price' => $product->get_price(),
'categories' => $this->get_term_names( $product_id, 'product_cat' ),
'tags' => $this->get_term_names( $product_id, 'product_tag' ),
'attributes' => $this->get_product_attributes( $product ),
'shortDescription' => $product->get_short_description(),
'description' => $product->get_description(),
'permalink' => get_permalink( $product_id ),
);
}
private function get_term_names( $product_id, $taxonomy ) {
$terms = wp_get_post_terms( $product_id, $taxonomy, array( 'fields' => 'names' ) );
return is_wp_error( $terms ) ? array() : array_values( $terms );
}
private function get_product_attributes( WC_Product $product ) {
$attributes = array();
foreach ( $product->get_attributes() as $attribute ) {
if ( ! $attribute instanceof WC_Product_Attribute ) {
continue;
}
$name = $attribute->is_taxonomy()
? wc_attribute_label( $attribute->get_name() )
: $attribute->get_name();
$options = $attribute->is_taxonomy()
? wc_get_product_terms( $product->get_id(), $attribute->get_name(), array( 'fields' => 'names' ) )
: $attribute->get_options();
$attributes[] = array(
'name' => $name,
'options' => is_wp_error( $options ) ? array() : array_values( array_map( 'strval', $options ) ),
);
}
return $attributes;
}
public function sanitize_generation_options( $options ) {
$options = is_array( $options ) ? $options : array();
return array(
'language' => sanitize_text_field( isset( $options['language'] ) ? $options['language'] : 'auto' ),
'tone' => sanitize_text_field( isset( $options['tone'] ) ? $options['tone'] : 'professional' ),
'audience' => sanitize_text_field( isset( $options['audience'] ) ? $options['audience'] : get_option( 'wpthemoon_target_audience', 'online shoppers' ) ),
'fields' => $this->sanitize_fields( isset( $options['fields'] ) ? $options['fields'] : $this->allowed_fields ),
);
}
public function sanitize_fields( $fields ) {
if ( ! is_array( $fields ) ) {
return array();
}
return array_values(
array_intersect(
$this->allowed_fields,
array_map( 'sanitize_text_field', $fields )
)
);
}
private function build_faq_html( $faq_items ) {
$items = array();
foreach ( $faq_items as $item ) {
if ( ! is_array( $item ) ) {
continue;
}
$question = isset( $item['question'] ) ? sanitize_text_field( $item['question'] ) : '';
$answer = isset( $item['answer'] ) ? wp_kses_post( $item['answer'] ) : '';
if ( ! $question || ! $answer ) {
continue;
}
$items[] = '' . esc_html( $question ) . '
' . wp_kses_post( $answer ) . '
';
}
if ( empty( $items ) ) {
return '';
}
return '' . esc_html__( 'FAQ', 'wpthemoon' ) . '
' . implode( '', $items ) . '';
}
private function ensure_api_result( $result ) {
if ( is_wp_error( $result ) ) {
$code = $result->get_error_code();
$data = $result->get_error_data( $code );
$status = ( is_array( $data ) && isset( $data['status'] ) ) ? $data['status'] : 500;
return new WP_Error( $code, $result->get_error_message(), array( 'status' => $status ) );
}
return rest_ensure_response( $result );
}
}