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 ); } }