The conditionals. */ public static function get_conditionals() { return [ AI_Conditional::class ]; } /** * Class constructor. * * @param Content_Outline_Command_Handler $command_handler The command handler instance. */ public function __construct( Content_Outline_Command_Handler $command_handler ) { $this->command_handler = $command_handler; } /** * Registers routes with WordPress. * * @return void */ public function register_routes() { \register_rest_route( self::ROUTE_NAMESPACE, self::ROUTE_PREFIX, [ 'methods' => 'POST', 'args' => [ 'post_type' => [ 'required' => true, 'type' => 'string', 'description' => 'The post type to get a content outline for.', ], 'language' => [ 'required' => true, 'type' => 'string', 'description' => 'The language the content is written in.', ], 'editor' => [ 'required' => true, 'type' => 'string', 'enum' => [ 'classic', 'elementor', 'gutenberg', ], 'description' => 'The current editor.', ], 'title' => [ 'required' => true, 'type' => 'string', 'description' => 'The title of the chosen content suggestion.', ], 'intent' => [ 'required' => true, 'type' => 'string', 'description' => 'The intent of the chosen content suggestion.', ], 'explanation' => [ 'required' => true, 'type' => 'string', 'description' => 'The explanation of the chosen content suggestion.', ], 'keyphrase' => [ 'required' => true, 'type' => 'string', 'description' => 'The keyphrase of the chosen content suggestion.', ], 'meta_description' => [ 'required' => true, 'type' => 'string', 'description' => 'The meta description of the chosen content suggestion.', ], 'category' => [ 'required' => true, 'type' => 'object', 'properties' => [ 'name' => [ 'type' => 'string', 'required' => true, ], 'id' => [ 'type' => 'integer', 'required' => true, ], ], 'description' => 'The category of the chosen content suggestion. Use name "" and id -1 to indicate no category.', ], 'recent_content' => [ 'required' => true, 'type' => 'array', 'maxItems' => 100, 'items' => [ 'type' => 'object', 'properties' => [ 'title' => [ 'type' => 'string', 'required' => true, 'maxLength' => 500, ], 'description' => [ 'type' => 'string', 'required' => true, 'maxLength' => 1000, ], ], 'additionalProperties' => false, ], 'description' => 'The recent content returned by the get_suggestions response.', ], ], 'callback' => [ $this, 'get_outline' ], 'permission_callback' => [ $this, 'check_permissions' ], ], ); } /** * Runs the callback to get an AI-generated content outline. * * @param WP_REST_Request $request The request object. * * @return WP_REST_Response The response of the get_outline action. */ public function get_outline( WP_REST_Request $request ): WP_REST_Response { try { $user = \wp_get_current_user(); $category_param = $request->get_param( 'category' ); $command = new Content_Outline_Command( $user, $request->get_param( 'post_type' ), $request->get_param( 'language' ), $request->get_param( 'editor' ), $request->get_param( 'title' ), $request->get_param( 'intent' ), $request->get_param( 'explanation' ), $request->get_param( 'keyphrase' ), $request->get_param( 'meta_description' ), $category_param['name'], (int) $category_param['id'], $request->get_param( 'recent_content' ), ); $data = $this->command_handler->handle( $command ); } catch ( Remote_Request_Exception $e ) { $message = [ 'message' => $e->getMessage(), 'errorIdentifier' => $e->get_error_identifier(), ]; if ( $e instanceof Payment_Required_Exception || $e instanceof Too_Many_Requests_Exception ) { $message['missingLicenses'] = $e->get_missing_licenses(); } return new WP_REST_Response( $message, $e->getCode(), ); } catch ( RuntimeException $e ) { return new WP_REST_Response( 'Failed to get content outline.', 500 ); } return new WP_REST_Response( $data->to_array() ); } /** * Checks if the user is logged in and can edit posts of the requested post type. * * The requested post_type is caller-controlled, so the permission must be evaluated * against that post type's own edit_posts meta-capability — not the generic * 'edit_posts' string, which would let a user with edit_posts but no edit_pages * (e.g. an Author) trigger outline generation for pages or arbitrary CPTs. * * @param WP_REST_Request $request The request object. * * @return bool Whether the user can edit posts of the requested post type. */ public function check_permissions( WP_REST_Request $request ): bool { $user = \wp_get_current_user(); if ( $user === null || $user->ID < 1 ) { return false; } $post_type = $request->get_param( 'post_type' ); $post_type_object = \get_post_type_object( $post_type ); if ( $post_type_object === null ) { return false; } return \user_can( $user, $post_type_object->cap->edit_posts ); } }