PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.7.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.7.0
2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.10.0 All 48 releases
thinkrank / includes / abilities / media / class-update-image-alt-text.php

class-update-image-alt-text.php in ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO 2.7.0, at includes/abilities/media/class-update-image-alt-text.php

166 lines 5.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Update image alt text ability.
4 *
5 * @package ThinkRank\Abilities\Media
6 */
7
8 declare(strict_types=1);
9
10 namespace ThinkRank\Abilities\Media;
11
12 if ( ! defined( 'ABSPATH' ) ) {
13 exit; // Exit if accessed directly.
14 }
15
16 /**
17 * Writes one image's alt text.
18 *
19 * Two modes, because the two useful answers to "what should this say?" come
20 * from different places: `alt_text` writes the words the caller chose, and
21 * `generate` asks Image SEO to build one from the site's alt_format template,
22 * which is the same value the front-end filter would have injected.
23 *
24 * Non-destructive by default: an image that already has hand-written alt text
25 * is left alone unless `overwrite` is set, so an agent sweeping the library
26 * cannot quietly replace an author's words with a template.
27 */
28 class Update_Image_Alt_Text extends Image_Alt_Ability_Base {
29
30 /**
31 * Constructor.
32 */
33 public function __construct() {
34 $this->id = 'thinkrank/update-image-alt-text';
35 $this->label = __( 'Update ThinkRank Image Alt Text', 'thinkrank' );
36 $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.', 'thinkrank' );
37 }
38
39 /**
40 * {@inheritDoc}
41 *
42 * @return array<string, bool|float|string>
43 */
44 public function get_annotations() {
45 return [
46 'readonly' => false,
47 'destructive' => false,
48 'idempotent' => true,
49 'priority' => 0.8,
50 'openWorldHint' => false,
51 ];
52 }
53
54 /**
55 * {@inheritDoc}
56 *
57 * @return array<string, mixed>
58 */
59 public function get_input_schema() {
60 return [
61 'type' => 'object',
62 'additionalProperties' => false,
63 'required' => [ 'attachment_id' ],
64 'properties' => [
65 'attachment_id' => [
66 'type' => 'integer',
67 'description' => __( 'Attachment ID from list-images.', 'thinkrank' ),
68 ],
69 'alt_text' => [
70 'type' => 'string',
71 'description' => __( 'The alt text to store. Describe the image; do not repeat the file name. Omit when using generate.', 'thinkrank' ),
72 ],
73 'generate' => [
74 'type' => 'boolean',
75 'description' => __( 'Build the alt text from the site\'s alt_format template instead of supplying it. Ignored when alt_text is given.', 'thinkrank' ),
76 ],
77 'overwrite' => [
78 'type' => 'boolean',
79 'description' => __( 'Replace alt text this image already has. Default false, which skips such images and reports skipped: true.', 'thinkrank' ),
80 ],
81 ],
82 ];
83 }
84
85 /**
86 * {@inheritDoc}
87 *
88 * @return array<string, mixed>
89 */
90 public function get_output_schema() {
91 return [
92 'type' => 'object',
93 'properties' => array_merge(
94 [
95 'success' => [ 'type' => 'boolean' ],
96 'skipped' => [
97 'type' => 'boolean',
98 'description' => __( 'True when the image already had alt text and overwrite was not set. Nothing was changed.', 'thinkrank' ),
99 ],
100 ],
101 $this->image_properties()
102 ),
103 ];
104 }
105
106 /**
107 * Execute ability.
108 *
109 * @param array<string, mixed> $input Ability input payload.
110 * @return array<string, mixed>|\WP_Error
111 */
112 public function execute( $input ) {
113 $input = (array) $input;
114 $attachment_id = (int) ( $input['attachment_id'] ?? 0 );
115
116 $rejection = $this->reject_if_not_an_image( $attachment_id );
117 if ( null !== $rejection ) {
118 return $rejection;
119 }
120
121 $has_text = array_key_exists( 'alt_text', $input );
122 $generate = ! empty( $input['generate'] );
123
124 if ( ! $has_text && ! $generate ) {
125 return new \WP_Error(
126 'thinkrank_no_alt_text_provided',
127 __( 'Provide alt_text, or set generate to true to build it from the site template.', 'thinkrank' ),
128 [ 'status' => 400 ]
129 );
130 }
131
132 $overwrite = ! empty( $input['overwrite'] );
133
134 if ( ! $overwrite && '' !== trim( $this->stored_alt( $attachment_id ) ) ) {
135 return array_merge(
136 [
137 'success' => true,
138 'skipped' => true,
139 ],
140 $this->describe_image( $attachment_id )
141 );
142 }
143
144 if ( $has_text ) {
145 // sanitize_text_field(), matching what the Media Library itself
146 // stores: alt text is a plain-text attribute, and markup in it is
147 // escaped into visible noise rather than rendered.
148 update_post_meta( $attachment_id, self::ALT_META_KEY, sanitize_text_field( (string) $input['alt_text'] ) );
149 } elseif ( ! $this->manager()->fill_attachment_alt( $attachment_id, true ) ) {
150 return new \WP_Error(
151 'thinkrank_alt_generation_failed',
152 __( 'The alt text template produced no value for this image. Set alt_text explicitly, or review the template under Essential SEO → Image SEO.', 'thinkrank' ),
153 [ 'status' => 422 ]
154 );
155 }
156
157 return array_merge(
158 [
159 'success' => true,
160 'skipped' => false,
161 ],
162 $this->describe_image( $attachment_id )
163 );
164 }
165 }
166