id = 'thinkrank/update-image-alt-text'; $this->label = __( 'Update ThinkRank Image Alt Text', 'thinkrank' ); $this->description = __( 'Set one image\'s alt text in the Media Library. Pass alt_text to write specific words, or generate: true to build one from the site\'s alt text template. Existing alt text is kept unless overwrite is true, so a sweep never replaces what an author wrote. Returns the stored value. Call get-image-alt-text to read the current value first, list-images to find ids, or fill-missing-alt-text to do the whole library at once. Caches holding the old alt text are cleared where possible; check cache.warnings and pass on anything the user still has to clear by hand.', 'thinkrank' ); } /** * {@inheritDoc} * * @return array */ public function get_annotations() { return [ 'readonly' => false, 'destructive' => false, 'idempotent' => true, 'priority' => 0.8, 'openWorldHint' => false, ]; } /** * {@inheritDoc} * * @return array */ public function get_input_schema() { return [ 'type' => 'object', 'additionalProperties' => false, 'required' => [ 'attachment_id' ], 'properties' => [ 'attachment_id' => [ 'type' => 'integer', 'description' => __( 'Attachment ID from list-images.', 'thinkrank' ), ], 'alt_text' => [ 'type' => 'string', 'description' => __( 'The alt text to store. Describe the image; do not repeat the file name. Omit when using generate.', 'thinkrank' ), ], 'generate' => [ 'type' => 'boolean', 'description' => __( 'Build the alt text from the site\'s alt_format template instead of supplying it. Ignored when alt_text is given.', 'thinkrank' ), ], 'overwrite' => [ 'type' => 'boolean', 'description' => __( 'Replace alt text this image already has. Default false, which skips such images and reports skipped: true.', 'thinkrank' ), ], ], ]; } /** * {@inheritDoc} * * @return array */ public function get_output_schema() { return [ 'type' => 'object', 'properties' => array_merge( [ 'success' => [ 'type' => 'boolean' ], 'skipped' => [ 'type' => 'boolean', 'description' => __( 'True when the image already had alt text and overwrite was not set. Nothing was changed.', 'thinkrank' ), ], ], $this->image_properties(), [ 'cache' => $this->cache_property() ] ), ]; } /** * Execute ability. * * @param array $input Ability input payload. * @return array|\WP_Error */ public function execute( $input ) { $input = (array) $input; $attachment_id = (int) ( $input['attachment_id'] ?? 0 ); $rejection = $this->reject_if_not_an_image( $attachment_id ); if ( null !== $rejection ) { return $rejection; } $has_text = array_key_exists( 'alt_text', $input ); $generate = ! empty( $input['generate'] ); if ( ! $has_text && ! $generate ) { return new \WP_Error( 'thinkrank_no_alt_text_provided', __( 'Provide alt_text, or set generate to true to build it from the site template.', 'thinkrank' ), [ 'status' => 400 ] ); } $overwrite = ! empty( $input['overwrite'] ); if ( ! $overwrite && '' !== trim( $this->stored_alt( $attachment_id ) ) ) { return array_merge( [ 'success' => true, 'skipped' => true, ], $this->describe_image( $attachment_id ) ); } if ( $has_text ) { // sanitize_text_field(), matching what the Media Library itself // stores: alt text is a plain-text attribute, and markup in it is // escaped into visible noise rather than rendered. update_post_meta( $attachment_id, self::ALT_META_KEY, sanitize_text_field( (string) $input['alt_text'] ) ); // fill_attachment_alt() purges for itself; this branch writes the // meta directly, so it has to ask for the same thing (#763). $this->manager()->purge_alt_caches( [ $attachment_id ] ); } elseif ( ! $this->manager()->fill_attachment_alt( $attachment_id, true ) ) { return new \WP_Error( 'thinkrank_alt_generation_failed', __( 'The alt text template produced no value for this image. Set alt_text explicitly, or review the template under Essential SEO → Image SEO.', 'thinkrank' ), [ 'status' => 422 ] ); } return array_merge( [ 'success' => true, 'skipped' => false, ], $this->describe_image( $attachment_id ), [ 'cache' => $this->cache_outcome() ] ); } }