PluginProbe
Image Alt Text / trunk
Image Alt Text vtrunk
4.1.1 4.1.0 trunk 1.0.0 2.0.0 3.0.0 4.0.0 4.0.1 4.0.2
image-alt-text / image-alt-text.php

image-alt-text.php in Image Alt Text trunk, at image-alt-text.php

274 lines 9.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Plugin Name: Image Alt Text
5 * Plugin URI: https://wordpress.org/plugins/image-alt-text/
6 * Description: Easily manage, add, and update image alt text to enhance website accessibility and improve SEO performance.
7 * Version: 4.1.1
8 * Author: RS WebStudios
9 * Author URI: https://rswebstudios.com
10 * License: GPLv2 or later
11 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
12 * Text Domain: image-alt-text
13 * Domain Path: /languages
14 * Requires at least: 5.0
15 * Requires PHP: 7.4
16 *
17 * @package ImageAltText
18 */
19
20 // Exit if accessed directly
21 if (! defined('ABSPATH')) {
22 exit;
23 }
24
25 // Record install date on activation (used by review notice)
26 register_activation_hook(__FILE__, 'iat_on_activation');
27 function iat_on_activation()
28 {
29 if (!get_option('iat_install_date')) {
30 update_option('iat_install_date', time(), false);
31 }
32 }
33
34 register_deactivation_hook(__FILE__, 'iat_on_deactivation');
35 function iat_on_deactivation()
36 {
37 $timestamp = wp_next_scheduled('iat_refresh_reviews_cache');
38 if ($timestamp) {
39 wp_unschedule_event($timestamp, 'iat_refresh_reviews_cache');
40 }
41 }
42
43 // Define plugin constants
44 define('IMAGE_ALT_TEXT', 'image-alt-text');
45 define('IAT_PLUGIN_VERSION', get_file_data(__FILE__, ['Version' => 'Version'])['Version']);
46 define('IAT_FILE_VERSION', IAT_PLUGIN_VERSION);
47 define('IAT_PREFIX', 'iat_');
48 define('IAT_PLUGIN_DIR', plugin_dir_path(__FILE__));
49 define('IAT_PLUGIN_URL', plugin_dir_url(__FILE__));
50
51 if (!function_exists('iat_smart_ucfirst')) {
52 /**
53 * Capitalize the first character for natural sentence-case reading, while
54 * preserving intentional brand / camelCase like "iPhone" or "eBay"
55 * (skips when the first letter is lowercase but the second is uppercase).
56 *
57 * @param string $text Input text.
58 * @return string Text with a safely capitalized first letter.
59 */
60 function iat_smart_ucfirst($text)
61 {
62 if ($text === '') {
63 return $text;
64 }
65
66 $has_mb = function_exists('mb_substr');
67 $first = $has_mb ? mb_substr($text, 0, 1) : substr($text, 0, 1);
68 $second = $has_mb ? mb_substr($text, 1, 1) : substr($text, 1, 1);
69 $upper_1 = function_exists('mb_strtoupper') ? mb_strtoupper($first) : strtoupper($first);
70
71 // Already uppercase or not a lowercase letter — leave it.
72 if ($first === $upper_1) {
73 return $text;
74 }
75
76 // Preserve camelCase / brand casing: lowercase first + uppercase-LETTER second.
77 if ($second !== '' && preg_match('/[A-Za-z]/', $second)) {
78 $upper_2 = function_exists('mb_strtoupper') ? mb_strtoupper($second) : strtoupper($second);
79 if ($second === $upper_2) {
80 return $text;
81 }
82 }
83
84 return $upper_1 . ($has_mb ? mb_substr($text, 1) : substr($text, 1));
85 }
86 }
87
88 if (!function_exists('iat_clean_filename_for_alt')) {
89 /**
90 * Convert a raw image filename (without extension) into human-readable alt text.
91 *
92 * Strips WordPress auto-generated size / scaled / edit suffixes and turns common
93 * separators into spaces, so "IMG_0421-1024x768-scaled" becomes "IMG 0421".
94 * Always falls back to the original string so it can never return empty alt text.
95 *
96 * @param string $filename Filename WITHOUT extension.
97 * @return string Cleaned, human-readable text.
98 */
99 function iat_clean_filename_for_alt($filename)
100 {
101 $original = (string) $filename;
102 $text = $original;
103
104 // Repeatedly strip trailing WordPress-generated suffixes — they can stack
105 // (e.g. "-1024x768-scaled"), so loop until nothing more is removed.
106 do {
107 $before = $text;
108 $text = preg_replace('/-\d+x\d+$/', '', $text); // dimensions e.g. -1024x768
109 $text = preg_replace('/-(scaled|rotated)$/i', '', $text); // -scaled / -rotated
110 $text = preg_replace('/-e\d{10,}$/i', '', $text); // edit hash e.g. -e1693400000000
111 } while ($text !== $before);
112
113 // Turn common separators into spaces.
114 $text = str_replace(array('-', '_', '.', '+'), ' ', $text);
115
116 // Collapse repeated whitespace and trim.
117 $text = trim(preg_replace('/\s+/', ' ', $text));
118
119 // Fallback: if cleanup emptied the string, humanize the original minimally,
120 // and as a last resort keep the original so we never return empty alt text.
121 if ($text === '') {
122 $text = trim(preg_replace('/\s+/', ' ', str_replace(array('-', '_', '.', '+'), ' ', $original)));
123 }
124 if ($text === '') {
125 $text = $original;
126 }
127
128 // Capitalize the first character (camelCase / brand-safe).
129 $text = iat_smart_ucfirst($text);
130
131 return $text;
132 }
133 }
134
135 if (!function_exists('iat_clean_title_for_alt')) {
136 /**
137 * Lightly clean an attachment TITLE into readable alt text.
138 *
139 * Titles are often human-edited, so this is gentler than the filename cleaner:
140 * it only turns "-", "_" and "+" into spaces and fixes capitalization. It does
141 * NOT strip dimension/-scaled/edit suffixes (those are filename artifacts) and
142 * keeps "." so real punctuation like "U.S.A." survives. Never returns empty.
143 *
144 * @param string $title Attachment post title.
145 * @return string Cleaned, human-readable text.
146 */
147 function iat_clean_title_for_alt($title)
148 {
149 $original = (string) $title;
150
151 // Turn filename-style separators into spaces (keep "." for punctuation).
152 $text = str_replace(array('-', '_', '+'), ' ', $original);
153
154 // Collapse repeated whitespace and trim.
155 $text = trim(preg_replace('/\s+/', ' ', $text));
156
157 // Never return empty — fall back to the original title.
158 if ($text === '') {
159 $text = trim($original);
160 }
161 if ($text === '') {
162 $text = $original;
163 }
164
165 // Capitalize the first character (camelCase / brand-safe).
166 $text = iat_smart_ucfirst($text);
167
168 return $text;
169 }
170 }
171
172 if (!function_exists('iat_get_alt_coverage_stats')) {
173 /**
174 * Get alt-text coverage stats for the media library (images only).
175 *
176 * Uses WP_Query found_posts (SQL count) rather than loading every attachment,
177 * so it stays cheap on large libraries.
178 *
179 * @return array{total:int,with:int,without:int,percent:int}
180 */
181 function iat_get_alt_coverage_stats()
182 {
183 $base_args = array(
184 'post_type' => 'attachment',
185 'post_mime_type' => 'image',
186 'post_status' => 'inherit',
187 'posts_per_page' => 1,
188 'fields' => 'ids',
189 'no_found_rows' => false,
190 'update_post_meta_cache' => false,
191 'update_post_term_cache' => false,
192 );
193
194 $total_query = new WP_Query($base_args);
195 $total = (int) $total_query->found_posts;
196
197 $with_args = $base_args;
198 $with_args['meta_query'] = array(
199 array(
200 'key' => '_wp_attachment_image_alt',
201 'value' => '',
202 'compare' => '!=',
203 ),
204 );
205 $with_query = new WP_Query($with_args);
206 $with = (int) $with_query->found_posts;
207
208 if ($with > $total) {
209 $with = $total;
210 }
211 $without = $total - $with;
212 $percent = $total > 0 ? (int) round(($with / $total) * 100) : 0;
213
214 return array(
215 'total' => $total,
216 'with' => $with,
217 'without' => $without,
218 'percent' => $percent,
219 );
220 }
221 }
222
223 class IMAGE_ALT_TEXT
224 {
225 public function __construct()
226 {
227 // Plugin load dependencies
228 add_action('plugins_loaded', array($this, 'iat_load_dependencies'));
229 // Load text domain for translations
230 add_action('init', array($this, 'iat_load_textdomain'));
231 // Plugin row meta
232 add_filter('plugin_row_meta', array($this, 'iat_plugin_row_meta'), 10, 2);
233 }
234
235 public function iat_load_dependencies()
236 {
237 $admin_files = array(
238 'class-iat-review-notice.php',
239 'class-iat-admin-menu.php',
240 'class-iat-list.php',
241 'class-iat-row-action.php',
242 'class-iat-bulk-action.php',
243 );
244
245 foreach ($admin_files as $file) {
246 require_once IAT_PLUGIN_DIR . 'includes/admin/' . $file;
247 }
248 }
249
250 public function iat_load_textdomain()
251 {
252 load_plugin_textdomain('image-alt-text', false, dirname(plugin_basename(__FILE__)) . '/languages/');
253 }
254
255 public function iat_plugin_row_meta($links, $file)
256 {
257 if (plugin_basename(__FILE__) === $file) {
258 $row_meta = array(
259 'rate' => '<a href="https://wordpress.org/support/plugin/image-alt-text/reviews/#new-post" target="_blank">' . __('Rate this plugin �
260
261
262
263
264 ', 'image-alt-text') . '</a>',
265 'upgrade' => '<a href="https://imagealttext.in/pricing/?utm_source=image_alt_text_plugin&utm_medium=plugin&utm_campaign=get_pro&utm_content=pricing_page" target="_blank" style="color:#CC5500 ;font-weight:bold;">' . __('Upgrade to Pro ', 'image-alt-text') . '</a>'
266 );
267 return array_merge($links, $row_meta);
268 }
269 return $links;
270 }
271 }
272
273 new IMAGE_ALT_TEXT();
274