ic function listPostsMissingSeo( $input ) { return ( new PostSeoService() )->listMissingSeo( is_array( $input ) ? $input : [] ); } /** * Delegate to PostSeoService::listByTruseoScore. * * @since 4.9.8 * * @param array $input The input data. * @return array|\WP_Error */ public function listPostsByTruseoScore( $input ) { return ( new PostSeoService() )->listByTruseoScore( is_array( $input ) ? $input : [] ); } /** * Delegate to SettingsService::get. * * @since 4.9.8 * * @return array|\WP_Error */ public function getSettings() { return ( new SettingsService() )->get(); } /** * Delegate to NotificationsService::listActive. * * @since 4.9.8 * * @param array $input The input data. * @return array|\WP_Error */ public function listNotifications( $input ) { return ( new NotificationsService() )->listActive( is_array( $input ) ? $input : [] ); } /** * Delegate to RobotsService::getOutput. * * @since 4.9.8 * * @return array|\WP_Error */ public function getRobotsOutput() { return ( new RobotsService() )->getOutput(); } /** * Delegate to RobotsService::listRules. * * @since 4.9.8 * * @return array|\WP_Error */ public function listRobotsRules() { return ( new RobotsService() )->listRules(); } /** * Delegate to RobotsService::addRule. * * @since 4.9.8 * * @param array $input The input data. * @return array|\WP_Error */ public function addRobotsRule( $input ) { return ( new RobotsService() )->addRule( is_array( $input ) ? $input : [] ); } /** * Delegate to RobotsService::updateRule. * * @since 4.9.8 * * @param array $input The input data. * @return array|\WP_Error */ public function updateRobotsRule( $input ) { $input = is_array( $input ) ? $input : []; $id = isset( $input['id'] ) ? (string) $input['id'] : ''; $rule = $input; unset( $rule['id'] ); return ( new RobotsService() )->updateRule( $id, $rule ); } /** * Delegate to RobotsService::deleteRule. * * @since 4.9.8 * * @param array $input The input data. * @return array|\WP_Error */ public function deleteRobotsRule( $input ) { $input = is_array( $input ) ? $input : []; return ( new RobotsService() )->deleteRule( isset( $input['id'] ) ? (string) $input['id'] : '' ); } /** * Delegate to AuditService::getHomepage. * * @since 4.9.8 * * @return array|\WP_Error */ public function getHomepageAudit() { return ( new AuditService() )->getHomepage(); } /** * Delegate to AuditService::getSite. * * @since 4.9.8 * * @return array|\WP_Error */ public function getSiteAudit() { return ( new AuditService() )->getSite(); } // ========================================================================= // Schema helpers (reused across multiple ability registrations). // ========================================================================= /** * Shared meta block for read-only abilities. * * @since 4.9.8 * * @return array */ protected function readonlyMeta() { return [ 'annotations' => [ 'readonly' => true ], 'show_in_rest' => true, 'mcp' => [ 'public' => true ] ]; } /** * Input schema for abilities that take no input. * * Without an input schema the Abilities API rejects any provided input with * `ability_missing_input_schema` — including the empty array MCP clients and * WP-CLI pass for a no-argument call. A permissive empty-object schema with a * `default` lets both `null` and `[]` validate. * * @since 4.9.8 * * @return array */ protected function noInputSchema() { // No `properties` key: an empty PHP array serializes to JSON `[]` (an array, not an // object `{}`), which stricter Abilities API validators reject as a malformed schema — // they then treat the input schema as absent and fail with a missing-schema error. return [ 'type' => 'object', 'additionalProperties' => false, 'default' => [] ]; } /** * Input schema for the robots flags object on post SEO updates. * * @since 4.9.8 * * @return array */ protected function robotsInputSchema() { return [ 'type' => 'object', 'properties' => [ 'use_default' => [ 'type' => 'boolean' ], 'noindex' => [ 'type' => 'boolean' ], 'nofollow' => [ 'type' => 'boolean' ], 'noarchive' => [ 'type' => 'boolean' ], 'nosnippet' => [ 'type' => 'boolean' ], 'noimageindex' => [ 'type' => 'boolean' ], 'notranslate' => [ 'type' => 'boolean' ], 'noodp' => [ 'type' => 'boolean' ] ], 'additionalProperties' => false ]; } /** * Input schema for the social meta object on post/term SEO updates. * * @since 4.9.8 * * @return array */ protected function socialInputSchema() { return [ 'type' => 'object', 'properties' => [ 'og_title' => [ 'type' => [ 'string', 'null' ] ], 'og_description' => [ 'type' => [ 'string', 'null' ] ], 'twitter_title' => [ 'type' => [ 'string', 'null' ] ], 'twitter_description' => [ 'type' => [ 'string', 'null' ] ] ], 'additionalProperties' => false ]; } /** * Output schema for a single post SEO snapshot. * * @since 4.9.8 * * @param bool $includeAnalysis Whether to declare the optional `analysis` field on the schema. * @return array */ protected function postSnapshotOutputSchema( $includeAnalysis ) { $properties = [ 'title' => [ 'type' => [ 'string', 'null' ] ], 'description' => [ 'type' => [ 'string', 'null' ] ], 'canonical_url' => [ 'type' => [ 'string', 'null' ] ], 'focus_keyphrase' => [ 'type' => [ 'string', 'null' ] ], 'additional_keyphrases' => [ 'type' => 'array', 'items' => [ 'type' => 'string' ] ], 'seo_score' => [ 'type' => 'integer' ], 'pillar_content' => [ 'type' => 'boolean' ], 'robots' => [ 'type' => 'object', 'properties' => [ 'use_default' => [ 'type' => 'boolean' ], 'noindex' => [ 'type' => 'boolean' ], 'nofollow' => [ 'type' => 'boolean' ], 'noarchive' => [ 'type' => 'boolean' ], 'nosnippet' => [ 'type' => 'boolean' ], 'noimageindex' => [ 'type' => 'boolean' ], 'notranslate' => [ 'type' => 'boolean' ], 'noodp' => [ 'type' => 'boolean' ] ] ], 'social' => [ 'type' => 'object', 'properties' => [ 'og_title' => [ 'type' => [ 'string', 'null' ] ], 'og_description' => [ 'type' => [ 'string', 'null' ] ], 'twitter_title' => [ 'type' => [ 'string', 'null' ] ], 'twitter_description' => [ 'type' => [ 'string', 'null' ] ] ] ], 'schema_type' => [ 'type' => [ 'string', 'null' ] ] ]; if ( $includeAnalysis ) { $properties['analysis'] = [ 'type' => 'object', 'description' => __( 'Full TruSEO analysis breakdown. Only present when "analysis" is in include.', 'all-in-one-seo-pack' ) ]; } return [ 'type' => 'object', 'properties' => $properties ]; } /** * Output schema for a list of posts. * * @since 4.9.8 * * @param bool $includeMissingFields Whether to include the `missing_fields` array per post. * @return array */ protected function postListOutputSchema( $includeMissingFields ) { $itemProperties = [ 'id' => [ 'type' => 'integer' ], 'post_title' => [ 'type' => 'string' ], 'post_type' => [ 'type' => 'string' ], 'status' => [ 'type' => 'string' ], 'permalink' => [ 'type' => [ 'string', 'null' ] ], 'seo_score' => [ 'type' => 'integer' ] ]; if ( $includeMissingFields ) { $itemProperties['missing_fields'] = [ 'type' => 'array', 'items' => [ 'type' => 'string', 'enum' => [ 'title', 'description', 'focus_keyphrase' ] ] ]; } return [ 'type' => 'object', 'properties' => [ 'posts' => [ 'type' => 'array', 'items' => [ 'type' => 'object', 'properties' => $itemProperties ] ], 'total' => [ 'type' => 'integer' ] ] ]; } /** * Output schema for a single robots.txt rule. * * @since 4.9.8 * * @return array */ protected function robotsRuleSchema() { return [ 'type' => 'object', 'properties' => [ 'id' => [ 'type' => 'string' ], 'user_agent' => [ 'type' => 'string' ], 'directive' => [ 'type' => 'string', 'enum' => [ 'allow', 'disallow' ] ], 'field_value' => [ 'type' => 'string' ] ] ]; } }