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-image-alt-ability-base.php

class-image-alt-ability-base.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-image-alt-ability-base.php

147 lines 4.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Shared base for the image alt-text abilities.
4 *
5 * @package ThinkRank\Abilities\Media
6 */
7
8 declare(strict_types=1);
9
10 namespace ThinkRank\Abilities\Media;
11
12 use ThinkRank\Abilities\Ability_Base;
13 use ThinkRank\SEO\Image_SEO_Manager;
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit; // Exit if accessed directly.
17 }
18
19 /**
20 * Common manager access and schema fragments for the alt-text abilities.
21 *
22 * Before these, the only image abilities were get/update-image-seo-settings,
23 * which operate on the site-wide alt_format template. An agent could change the
24 * rule and never see or set a single image's alt text — so "find the images
25 * missing alt text and write sensible ones", the most obvious image workflow
26 * there is, had no path through the connector at all (#676).
27 */
28 abstract class Image_Alt_Ability_Base extends Ability_Base {
29
30 /**
31 * The meta key WordPress stores alt text under.
32 */
33 protected const ALT_META_KEY = '_wp_attachment_image_alt';
34
35 /**
36 * Image SEO manager, built once per ability instance.
37 *
38 * @var Image_SEO_Manager|null
39 */
40 private ?Image_SEO_Manager $manager = null;
41
42 /**
43 * The settings manager for the image SEO feature.
44 *
45 * @return Image_SEO_Manager
46 */
47 protected function manager(): Image_SEO_Manager {
48 if ( null === $this->manager ) {
49 $this->manager = new Image_SEO_Manager();
50 }
51
52 return $this->manager;
53 }
54
55 /**
56 * Read one attachment's stored alt text.
57 *
58 * @param int $attachment_id Attachment ID.
59 * @return string Alt text, or '' when none is stored.
60 */
61 protected function stored_alt( int $attachment_id ): string {
62 return (string) get_post_meta( $attachment_id, self::ALT_META_KEY, true );
63 }
64
65 /**
66 * Reject anything that is not an image attachment.
67 *
68 * Returned rather than thrown so each ability can hand the caller the same
69 * message, and so a deleted or non-image id is a 404-shaped answer instead
70 * of a silent no-op that reads like success.
71 *
72 * @param int $attachment_id Attachment ID.
73 * @return \WP_Error|null Error when unusable, null when fine.
74 */
75 protected function reject_if_not_an_image( int $attachment_id ): ?\WP_Error {
76 if ( $attachment_id <= 0 || ! wp_attachment_is_image( $attachment_id ) ) {
77 return new \WP_Error(
78 'thinkrank_not_an_image',
79 sprintf(
80 /* translators: %d: attachment ID that was sent. */
81 __( '%d is not an image attachment on this site. Use list-images to find valid ids.', 'thinkrank' ),
82 $attachment_id
83 ),
84 [ 'status' => 404 ]
85 );
86 }
87
88 return null;
89 }
90
91 /**
92 * The shape one image is reported in, shared by every ability here.
93 *
94 * @return array<string, mixed>
95 */
96 protected function image_properties(): array {
97 return [
98 'id' => [ 'type' => 'integer' ],
99 'title' => [ 'type' => 'string' ],
100 'filename' => [ 'type' => 'string' ],
101 'url' => [ 'type' => 'string' ],
102 'edit_url' => [ 'type' => 'string' ],
103 'alt_text' => [ 'type' => 'string' ],
104 'has_alt' => [
105 'type' => 'boolean',
106 'description' => __( 'False when the alt text is missing or whitespace only.', 'thinkrank' ),
107 ],
108 'mime_type' => [ 'type' => 'string' ],
109 ];
110 }
111
112 /**
113 * Describe one attachment in the shape image_properties() declares.
114 *
115 * @param int $attachment_id Attachment ID.
116 * @return array<string, mixed>
117 */
118 protected function describe_image( int $attachment_id ): array {
119 $alt = $this->stored_alt( $attachment_id );
120 $file = (string) get_post_meta( $attachment_id, '_wp_attached_file', true );
121
122 return [
123 'id' => $attachment_id,
124 'title' => (string) get_the_title( $attachment_id ),
125 'filename' => '' !== $file ? basename( $file ) : '',
126 'url' => (string) wp_get_attachment_url( $attachment_id ),
127 'edit_url' => (string) get_edit_post_link( $attachment_id, 'raw' ),
128 'alt_text' => $alt,
129 'has_alt' => '' !== trim( $alt ),
130 'mime_type' => (string) get_post_mime_type( $attachment_id ),
131 ];
132 }
133
134 /**
135 * Post statuses an image can carry.
136 *
137 * Mirrors Image_SEO_Manager's own list: 'inherit' alone misses the private
138 * attachments media-protection and membership plugins create, which is the
139 * same gap #322 fixed in the bulk filler.
140 *
141 * @return string[]
142 */
143 protected function attachment_statuses(): array {
144 return [ 'inherit', 'private', 'publish', 'draft', 'pending', 'future' ];
145 }
146 }
147