PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.12.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.12.0
2.12.0 2.11.0 2.10.0 2.9.0 2.8.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 All 53 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.12.0, at includes/abilities/media/class-update-image-alt-text.php

172 lines 5.4 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. 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' );
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 [ 'cache' => $this->cache_property() ]
103 ),
104 ];
105 }
106
107 /**
108 * Execute ability.
109 *
110 * @param array<string, mixed> $input Ability input payload.
111 * @return array<string, mixed>|\WP_Error
112 */
113 public function execute( $input ) {
114 $input = (array) $input;
115 $attachment_id = (int) ( $input['attachment_id'] ?? 0 );
116
117 $rejection = $this->reject_if_not_an_image( $attachment_id );
118 if ( null !== $rejection ) {
119 return $rejection;
120 }
121
122 $has_text = array_key_exists( 'alt_text', $input );
123 $generate = ! empty( $input['generate'] );
124
125 if ( ! $has_text && ! $generate ) {
126 return new \WP_Error(
127 'thinkrank_no_alt_text_provided',
128 __( 'Provide alt_text, or set generate to true to build it from the site template.', 'thinkrank' ),
129 [ 'status' => 400 ]
130 );
131 }
132
133 $overwrite = ! empty( $input['overwrite'] );
134
135 if ( ! $overwrite && '' !== trim( $this->stored_alt( $attachment_id ) ) ) {
136 return array_merge(
137 [
138 'success' => true,
139 'skipped' => true,
140 ],
141 $this->describe_image( $attachment_id )
142 );
143 }
144
145 if ( $has_text ) {
146 // sanitize_text_field(), matching what the Media Library itself
147 // stores: alt text is a plain-text attribute, and markup in it is
148 // escaped into visible noise rather than rendered.
149 update_post_meta( $attachment_id, self::ALT_META_KEY, sanitize_text_field( (string) $input['alt_text'] ) );
150
151 // fill_attachment_alt() purges for itself; this branch writes the
152 // meta directly, so it has to ask for the same thing (#763).
153 $this->manager()->purge_alt_caches( [ $attachment_id ] );
154 } elseif ( ! $this->manager()->fill_attachment_alt( $attachment_id, true ) ) {
155 return new \WP_Error(
156 'thinkrank_alt_generation_failed',
157 __( 'The alt text template produced no value for this image. Set alt_text explicitly, or review the template under Essential SEO → Image SEO.', 'thinkrank' ),
158 [ 'status' => 422 ]
159 );
160 }
161
162 return array_merge(
163 [
164 'success' => true,
165 'skipped' => false,
166 ],
167 $this->describe_image( $attachment_id ),
168 [ 'cache' => $this->cache_outcome() ]
169 );
170 }
171 }
172