PluginProbe
Auto Alt Text / 2.5.2
Auto Alt Text v2.5.2
3.0.3 2.8.2 1.3.1 1.3.2 2.0.0 2.1.0 2.1.1 2.2.0 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.5.0 2.5.1 2.5.2 2.5.3 2.6.0 2.6.1 2.7.0 2.8.0 2.8.1 All 28 releases
← All changes | src/App/Admin/PluginOptions.php +168 -6 2.3.12.5.2 View file →
@@ -30,25 +30,135 @@
30 30 }
31 31
32 32 self::$assetsManager = AssetsManager::make();
33 33
34 + add_action('admin_notices', [self::$instance, 'showEncryptionConstantsNotice']);
35 +
34 36 add_action('admin_enqueue_scripts', [self::$instance, 'enqueueAdminScripts'], 1);
35 37 add_action('admin_menu', [self::$instance, 'addOptionsPageToTheMenu']);
36 - add_action('admin_init', [self::$instance, 'setupPluginOptions']);
38 + add_action('admin_init', [self::$instance, 'setupPluginOptions'], 10);
39 + add_action('admin_init', [self::$instance, 'migrateEncryptionKeys'], 9);
37 40
38 41 // Encrypt API Keys on update
39 42 add_action('pre_update_option_' . Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_COMPUTER_VISION, [self::$instance, 'encryptDataOnUpdate'], 10, 3);
40 43 add_action('pre_update_option_' . Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_TRANSLATE_INSTANCE, [self::$instance, 'encryptDataOnUpdate'], 10, 3);
41 44 add_action('pre_update_option_' . Constants::AATXT_OPTION_FIELD_API_KEY_OPENAI, [self::$instance, 'encryptDataOnUpdate'], 10, 3);
45 + add_action('pre_update_option_' . Constants::AATXT_OPTION_FIELD_API_KEY_ANTHROPIC, [self::$instance, 'encryptDataOnUpdate'], 10, 3);
46 +
47 + add_action('admin_notices', [self::$instance, 'encryptionErrorNotice']);
42 48 }
43 49
44 50 /**
51 + * Show an error notice if the encryption of the API Key fails
52 + * @return void
53 + */
54 + public function encryptionErrorNotice(): void
55 + {
56 + $raw = get_option(Constants::AATXT_OPTION_FIELD_API_KEY_OPENAI);
57 + $decrypted = Encryption::make()->decrypt((string) $raw);
58 +
59 + if ($raw && $decrypted === '') {
60 + $screen = get_current_screen();
61 + $settingsScreenId = 'toplevel_page_' . Constants::AATXT_PLUGIN_OPTIONS_PAGE_SLUG;
62 + $showLink = ! ( $screen && $screen->id === $settingsScreenId );
63 +
64 + $settingsUrl = esc_url( menu_page_url(Constants::AATXT_PLUGIN_OPTIONS_PAGE_SLUG, false) );
65 +
66 + echo '<div class="notice notice-error is-dismissible">';
67 + echo '<p>';
68 +
69 + echo '<strong>' . esc_html__('Auto Alt Text', 'auto-alt-text') . ':</strong> ';
70 +
71 + echo esc_html__(
72 + 'There was a problem with the encryption of your API Key for alt text generation. Please re-enter the key and save.',
73 + 'auto-alt-text'
74 + );
75 +
76 + if ($showLink) {
77 + echo ' <a href="' . $settingsUrl . '">'
78 + . esc_html__('Go to settings page', 'auto-alt-text')
79 + . '</a>.';
80 + }
81 + echo '</p>';
82 + echo '</div>';
83 + }
84 + }
85 +
86 + /**
87 + * Print an admin notice containing the defines to be added
88 + * to the wp-config.php.
89 + */
90 + public function showEncryptionConstantsNotice(): void
91 + {
92 + if ( ! is_admin() || ! current_user_can('manage_options') ) {
93 + return;
94 + }
95 +
96 + $screen = function_exists('get_current_screen') ? get_current_screen() : null;
97 + if ( ! $screen || $screen->id !== 'toplevel_page_' . Constants::AATXT_PLUGIN_OPTIONS_PAGE_SLUG ) {
98 + return;
99 + }
100 +
101 + if ( defined('AATXT_ENCRYPTION_KEY') && defined('AATXT_ENCRYPTION_SALT') ) {
102 + return;
103 + }
104 +
105 + $suggestedKey = bin2hex(random_bytes(32));
106 + $suggestedSalt = bin2hex(random_bytes(32));
107 + ?>
108 + <div class="notice notice-info is-dismissible">
109 + <p>
110 + <?php
111 + printf(
112 + /* translators: “Optional” label */
113 + __( '%s: you can add two lines to your wp-config.php to make your API key more resilient if WordPress salts ever change.', 'auto-alt-text' ),
114 + '<strong>' . esc_html__( 'Optional', 'auto-alt-text' ) . '</strong>'
115 + );
116 + ?>
117 + </p>
118 +<pre style="background:#f5f5f5; padding:10px; border-radius:4px;">
119 +define('AATXT_ENCRYPTION_KEY', '<?php echo esc_html( $suggestedKey ); ?>');
120 +define('AATXT_ENCRYPTION_SALT', '<?php echo esc_html( $suggestedSalt ); ?>');
121 +</pre>
122 + <p>
123 + <?php esc_html_e(
124 + 'Just paste them before the line “/* That\'s all, stop editing! Happy publishing. */” in wp-config.php.',
125 + 'auto-alt-text'
126 + ); ?>
127 + </p>
128 + <p>
129 + <?php esc_html_e(
130 + "If you don't have the ability to edit the wp-config.php, don't worry because the plugin will still work without this change. If your site's salting keys change in the future, you will simply need to resave your API Key in the options below.",
131 + "auto-alt-text"
132 + ); ?>
133 + </p>
134 + </div>
135 + <?php
136 + }
137 +
138 + public function migrateEncryptionKeys(): void
139 + {
140 + if ( ! defined('AATXT_ENCRYPTION_KEY') || ! defined('AATXT_ENCRYPTION_SALT') ) {
141 + return;
142 + }
143 +
144 + update_option(Constants::AATXT_LEGACY_ENCRYPTION_MIGRATION_DONE, '0');
145 +
146 + if ( get_option(Constants::AATXT_LEGACY_ENCRYPTION_MIGRATION_DONE) ) {
147 + return;
148 + }
149 +
150 + Encryption::make()->migrateLegacyApiKeys();
151 +
152 + update_option(Constants::AATXT_LEGACY_ENCRYPTION_MIGRATION_DONE, '1');
153 + }
154 +
155 + /**
45 156 * Encrypt data
46 157 * @param ?string $newValue
47 - * @param ?string $oldValue
48 158 * @return ?string
49 159 */
50 - public function encryptDataOnUpdate(?string $newValue, ?string $oldValue): ?string
160 + public function encryptDataOnUpdate(?string $newValue): ?string
51 161 {
52 162 if (!empty($newValue)) {
53 163 $newValue = (Encryption::make())->encrypt($newValue);
54 164 }
@@ -167,8 +277,9 @@
167 277 <select name="<?php echo esc_attr(Constants::AATXT_OPTION_FIELD_TYPOLOGY); ?>"
168 278 id="<?php echo esc_attr(Constants::AATXT_OPTION_FIELD_TYPOLOGY); ?>">
169 279 <option value="<?php echo esc_attr(Constants::AATXT_OPTION_TYPOLOGY_DEACTIVATED); ?>"<?php echo esc_attr(self::selected($typology, Constants::AATXT_OPTION_TYPOLOGY_DEACTIVATED)); ?>><?php esc_html_e("Deactivated", 'auto-alt-text'); ?></option>
170 280 <option value="<?php echo esc_attr(Constants::AATXT_OPTION_TYPOLOGY_CHOICE_OPENAI); ?>"<?php echo esc_attr(self::selected($typology, Constants::AATXT_OPTION_TYPOLOGY_CHOICE_OPENAI)); ?>><?php esc_html_e("OpenAI's APIs", 'auto-alt-text'); ?></option>
281 + <option value="<?php echo esc_attr(Constants::AATXT_OPTION_TYPOLOGY_CHOICE_ANTHROPIC); ?>"<?php echo esc_attr(self::selected($typology, Constants::AATXT_OPTION_TYPOLOGY_CHOICE_ANTHROPIC)); ?>><?php esc_html_e("Antrhopic's APIs", 'auto-alt-text'); ?></option>
171 282 <option value="<?php echo esc_attr(Constants::AATXT_OPTION_TYPOLOGY_CHOICE_AZURE); ?>"<?php echo esc_attr(self::selected($typology, Constants::AATXT_OPTION_TYPOLOGY_CHOICE_AZURE)); ?>><?php esc_html_e("Azure's APIs", 'auto-alt-text'); ?></option>
172 283 <option value="<?php echo esc_attr(Constants::AATXT_OPTION_TYPOLOGY_CHOICE_ARTICLE_TITLE); ?>"<?php echo esc_attr(self::selected($typology, Constants::AATXT_OPTION_TYPOLOGY_CHOICE_ARTICLE_TITLE)); ?>><?php esc_html_e("Title of the article (not AI)", 'auto-alt-text'); ?></option>
173 284 <option value="<?php echo esc_attr(Constants::AATXT_OPTION_TYPOLOGY_CHOICE_ATTACHMENT_TITLE); ?>"<?php echo esc_attr(self::selected($typology, Constants::AATXT_OPTION_TYPOLOGY_CHOICE_ATTACHMENT_TITLE)); ?>><?php esc_html_e("Title of the attachment (not AI)", 'auto-alt-text'); ?></option>
174 285 </select>
@@ -291,9 +402,36 @@
291 402
292 403 echo '</div>';
293 404 endif;
294 405
295 - echo '<div class="plugin-option type-article-title type-attachment-title type-openai type-azure">';
406 + echo '<div class="plugin-option type-anthropic">';
407 + echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_API_KEY_ANTHROPIC) . '">' . esc_html__('Antrhopic API Key', 'auto-alt-text') . '</label>';
408 + echo '<p class="description">' . esc_html__("Enter your API Key", 'auto-alt-text') . '</p>';
409 + $apiKey = get_option(Constants::AATXT_OPTION_FIELD_API_KEY_ANTHROPIC);
410 + echo '<input type="password" name="' . esc_attr(Constants::AATXT_OPTION_FIELD_API_KEY_ANTHROPIC) . '" value="' . esc_attr((Encryption::make())->decrypt($apiKey)) . '" />';
411 + echo '</div>';
412 +
413 + $anthropicModel = self::anthropicModel();
414 +
415 + echo '<div class="plugin-option type-anthropic">';
416 + echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_MODEL_ANTHROPIC) . '">' . esc_html__('Model', 'auto-alt-text') . '</label>';
417 +
418 + echo '<select name="' . esc_attr(Constants::AATXT_OPTION_FIELD_MODEL_ANTHROPIC) . '" id="' . esc_attr(Constants::AATXT_OPTION_FIELD_MODEL_ANTHROPIC) . '">';
419 + foreach(Constants::AATXT_OPTION_FIELD_MODEL_ANTHROPIC_OPTIONS as $key => $value) {
420 + echo '<option value="' . esc_attr($key) . '" ' . esc_attr(self::selected($anthropicModel, $key)) . '>' . esc_html($value) . '</option>';
421 + }
422 + echo '</select>';
423 + echo '</div>';
424 +
425 + echo '<div class="plugin-option type-anthropic">';
426 + echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_PROMPT_ANTHROPIC) . '">' . esc_html__('Prompt', 'auto-alt-text') . '</label>';
427 + echo '<p class="description">' . esc_html__("Enter a specific and detailed prompt according to your needs.", 'auto-alt-text') . '</p>';
428 + $defaultPrompt = sprintf(esc_html__("Act like an SEO expert and write an alt text of up to 125 characters for this image. Return only the complete alt text.", 'auto-alt-text'), Constants::AATXT_IMAGE_URL_TAG);
429 + $prompt = get_option(Constants::AATXT_OPTION_FIELD_PROMPT_ANTHROPIC) ?: $defaultPrompt;
430 + echo '<textarea name="' . esc_attr(Constants::AATXT_OPTION_FIELD_PROMPT_ANTHROPIC) . '" rows="5" cols="50">' . esc_textarea($prompt) . '</textarea>';
431 + echo '</div>';
432 +
433 + echo '<div class="plugin-option type-article-title type-attachment-title type-openai type-azure type-anthropic">';
296 434 echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_PRESERVE_EXISTING_ALT_TEXT) . '">' . esc_html__('Keep existing alt text', 'auto-alt-text') . '</label>';
297 435 echo '<p class="description">' . esc_html__("If checked, the existing alt text of images will not be overwritten.", 'auto-alt-text') . '</p>';
298 436 $preserveAltText = get_option(Constants::AATXT_OPTION_FIELD_PRESERVE_EXISTING_ALT_TEXT);
299 437 echo '<input type="checkbox" name="' . esc_attr(Constants::AATXT_OPTION_FIELD_PRESERVE_EXISTING_ALT_TEXT) . '" value="1" class="notRequired" ' . checked(1, $preserveAltText, false) . ' />';
@@ -337,8 +475,11 @@
337 475 register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_ENDPOINT_AZURE_TRANSLATE_INSTANCE, [self::class, 'sanitizeUrl']);
338 476 register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_REGION_AZURE_TRANSLATE_INSTANCE, [self::class, 'sanitizeText']);
339 477 register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_LANGUAGE_AZURE_TRANSLATE_INSTANCE, [self::class, 'sanitizeText']);
340 478 register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_PRESERVE_EXISTING_ALT_TEXT);
479 + register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_API_KEY_ANTHROPIC);
480 + register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_PROMPT_ANTHROPIC, [self::class, 'sanitizeTextArea']);
481 + register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_MODEL_ANTHROPIC, [self::class, 'sanitizeText']);
341 482 }
342 483
343 484 /**
344 485 * @return string
@@ -352,12 +493,17 @@
352 493 {
353 494 return get_option(Constants::AATXT_OPTION_FIELD_MODEL_OPENAI) ?: Constants::AATXT_GPT4O;
354 495 }
355 496
497 + public static function anthropicModel(): string
498 + {
499 + return get_option(Constants::AATXT_OPTION_FIELD_MODEL_ANTHROPIC) ?: Constants::AATXT_CLAUDE_HAIKU_3_5;
500 + }
501 +
356 502 /**
357 503 * @return string
358 504 */
359 - public static function prompt(): string
505 + public static function openAiPrompt(): string
360 506 {
361 507 return get_option(Constants::AATXT_OPTION_FIELD_PROMPT_OPENAI);
362 508 }
363 509
@@ -363,8 +509,16 @@
363 509
364 510 /**
365 511 * @return string
366 512 */
513 + public static function anthropicPrompt(): string
514 + {
515 + return get_option(Constants::AATXT_OPTION_FIELD_PROMPT_ANTHROPIC);
516 + }
517 +
518 + /**
519 + * @return string
520 + */
367 521 public static function apiKeyOpenAI(): string
368 522 {
369 523 $apiKey = get_option(Constants::AATXT_OPTION_FIELD_API_KEY_OPENAI);
370 524 return (Encryption::make())->decrypt($apiKey);
@@ -372,8 +526,17 @@
372 526
373 527 /**
374 528 * @return string
375 529 */
530 + public static function apiKeyAnthropic(): string
531 + {
532 + $apiKey = get_option(Constants::AATXT_OPTION_FIELD_API_KEY_ANTHROPIC);
533 + return (Encryption::make())->decrypt($apiKey);
534 + }
535 +
536 + /**
537 + * @return string
538 + */
376 539 public static function apiKeyAzureComputerVision(): string
377 540 {
378 541 $apiKey = get_option(Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_COMPUTER_VISION);
379 542 return (Encryption::make())->decrypt($apiKey);
@@ -441,6 +604,5 @@
441 604 public static function sanitizeTextArea($input): string
442 605 {
443 606 return sanitize_textarea_field($input);
444 607 }
445 -
446 608 }