PluginProbe
Auto Alt Text / 2.7.0
Auto Alt Text v2.7.0
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
auto-alt-text / src / App / Admin / PluginOptions.php

PluginOptions.php in Auto Alt Text 2.7.0, at src/App/Admin/PluginOptions.php

626 lines 33.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace AATXT\App\Admin;
4
5 use AATXT\App\Logging\DBLogger;
6 use AATXT\App\AIProviders\Azure\AzureTranslator;
7 use AATXT\App\Configuration\AzureConfig;
8 use AATXT\App\Core\Container;
9 use AATXT\App\Exceptions\Azure\AzureTranslateInstanceException;
10 use AATXT\App\Infrastructure\Http\WordPressHttpClient;
11 use AATXT\App\Utilities\AssetsManager;
12 use AATXT\App\Utilities\Encryption;
13 use AATXT\Config\Constants;
14
15 class PluginOptions
16 {
17 private static ?self $instance = null;
18 private static AssetsManager $assetsManager;
19
20 private function __construct()
21 {
22 //
23 }
24
25 /**
26 * Manage the necessary hooks to implement plugin options and their pages
27 * @return void
28 */
29 public static function register(): void
30 {
31 if (is_null(self::$instance)) {
32 self::$instance = new self();
33 }
34
35 self::$assetsManager = AssetsManager::make();
36
37 add_action('admin_notices', [self::$instance, 'showEncryptionConstantsNotice']);
38
39 add_action('admin_enqueue_scripts', [self::$instance, 'enqueueAdminScripts'], 1);
40 add_action('admin_menu', [self::$instance, 'addOptionsPageToTheMenu']);
41 add_action('admin_init', [self::$instance, 'setupPluginOptions'], 10);
42 add_action('admin_init', [self::$instance, 'migrateEncryptionKeys'], 9);
43
44 // Encrypt API Keys on update
45 add_action('pre_update_option_' . Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_COMPUTER_VISION, [self::$instance, 'encryptDataOnUpdate'], 10, 3);
46 add_action('pre_update_option_' . Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_TRANSLATE_INSTANCE, [self::$instance, 'encryptDataOnUpdate'], 10, 3);
47 add_action('pre_update_option_' . Constants::AATXT_OPTION_FIELD_API_KEY_OPENAI, [self::$instance, 'encryptDataOnUpdate'], 10, 3);
48 add_action('pre_update_option_' . Constants::AATXT_OPTION_FIELD_API_KEY_ANTHROPIC, [self::$instance, 'encryptDataOnUpdate'], 10, 3);
49
50 add_action('admin_notices', [self::$instance, 'encryptionErrorNotice']);
51 }
52
53 /**
54 * Show an error notice if the encryption of the API Key fails
55 * @return void
56 */
57 public function encryptionErrorNotice(): void
58 {
59 $raw = get_option(Constants::AATXT_OPTION_FIELD_API_KEY_OPENAI);
60 $decrypted = Encryption::make()->decrypt((string) $raw);
61
62 if ($raw && $decrypted === '') {
63 $screen = get_current_screen();
64 $settingsScreenId = 'toplevel_page_' . Constants::AATXT_PLUGIN_OPTIONS_PAGE_SLUG;
65 $showLink = ! ( $screen && $screen->id === $settingsScreenId );
66
67 $settingsUrl = esc_url( menu_page_url(Constants::AATXT_PLUGIN_OPTIONS_PAGE_SLUG, false) );
68
69 echo '<div class="notice notice-error is-dismissible">';
70 echo '<p>';
71
72 echo '<strong>' . esc_html__('Auto Alt Text', 'auto-alt-text') . ':</strong> ';
73
74 echo esc_html__(
75 'There was a problem with the encryption of your API Key for alt text generation. Please re-enter the key and save.',
76 'auto-alt-text'
77 );
78
79 if ($showLink) {
80 echo ' <a href="' . $settingsUrl . '">'
81 . esc_html__('Go to settings page', 'auto-alt-text')
82 . '</a>.';
83 }
84 echo '</p>';
85 echo '</div>';
86 }
87 }
88
89 /**
90 * Print an admin notice containing the defines to be added
91 * to the wp-config.php.
92 */
93 public function showEncryptionConstantsNotice(): void
94 {
95 if ( ! is_admin() || ! current_user_can('manage_options') ) {
96 return;
97 }
98
99 $screen = function_exists('get_current_screen') ? get_current_screen() : null;
100 if ( ! $screen || $screen->id !== 'toplevel_page_' . Constants::AATXT_PLUGIN_OPTIONS_PAGE_SLUG ) {
101 return;
102 }
103
104 if ( defined('AATXT_ENCRYPTION_KEY') && defined('AATXT_ENCRYPTION_SALT') ) {
105 return;
106 }
107
108 $suggestedKey = bin2hex(random_bytes(32));
109 $suggestedSalt = bin2hex(random_bytes(32));
110 ?>
111 <div class="notice notice-info is-dismissible">
112 <p>
113 <?php
114 printf(
115 /* translators: “Optional” label */
116 __( '%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' ),
117 '<strong>' . esc_html__( 'Optional', 'auto-alt-text' ) . '</strong>'
118 );
119 ?>
120 </p>
121 <pre style="background:#f5f5f5; padding:10px; border-radius:4px;">
122 define('AATXT_ENCRYPTION_KEY', '<?php echo esc_html( $suggestedKey ); ?>');
123 define('AATXT_ENCRYPTION_SALT', '<?php echo esc_html( $suggestedSalt ); ?>');
124 </pre>
125 <p>
126 <?php esc_html_e(
127 'Just paste them before the line “/* That\'s all, stop editing! Happy publishing. */” in wp-config.php.',
128 'auto-alt-text'
129 ); ?>
130 </p>
131 <p>
132 <?php esc_html_e(
133 "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.",
134 "auto-alt-text"
135 ); ?>
136 </p>
137 </div>
138 <?php
139 }
140
141 public function migrateEncryptionKeys(): void
142 {
143 if ( ! defined('AATXT_ENCRYPTION_KEY') || ! defined('AATXT_ENCRYPTION_SALT') ) {
144 return;
145 }
146
147 update_option(Constants::AATXT_LEGACY_ENCRYPTION_MIGRATION_DONE, '0');
148
149 if ( get_option(Constants::AATXT_LEGACY_ENCRYPTION_MIGRATION_DONE) ) {
150 return;
151 }
152
153 Encryption::make()->migrateLegacyApiKeys();
154
155 update_option(Constants::AATXT_LEGACY_ENCRYPTION_MIGRATION_DONE, '1');
156 }
157
158 /**
159 * Encrypt data
160 * @param ?string $newValue
161 * @return ?string
162 */
163 public function encryptDataOnUpdate(?string $newValue): ?string
164 {
165 if (!empty($newValue)) {
166 $newValue = (Encryption::make())->encrypt($newValue);
167 }
168 return $newValue;
169 }
170
171 /**
172 * @return void
173 */
174 public static function enqueueAdminScripts(): void
175 {
176 $screen = get_current_screen();
177 $isMainOptionsPage = $screen->id === 'toplevel_page_' . Constants::AATXT_PLUGIN_OPTIONS_PAGE_SLUG;
178
179 if ($isMainOptionsPage || strpos($screen->id, Constants::AATXT_PLUGIN_SLUG . '_') !== false) {
180
181 $adminCss = self::$assetsManager->getAssetUrl('resources/js/admin.js', true);
182
183 wp_enqueue_style(Constants::AATXT_PLUGIN_ASSETS_HANDLE, $adminCss, [], false);
184
185 if ($isMainOptionsPage) {
186 $adminJs = self::$assetsManager->getAssetUrl('resources/js/admin.js', false);
187 wp_enqueue_script(Constants::AATXT_PLUGIN_ASSETS_HANDLE, $adminJs, [], false);
188 }
189
190 }
191 }
192
193 /**
194 * Create options pages
195 * @return void
196 */
197 public static function addOptionsPageToTheMenu(): void
198 {
199 add_menu_page('Auto Alt Text Options', 'Auto Alt Text', 'manage_options', Constants::AATXT_PLUGIN_OPTIONS_PAGE_SLUG, [self::$instance, 'optionsMainPage'], null, 99);
200 add_submenu_page(Constants::AATXT_PLUGIN_OPTIONS_PAGE_SLUG, 'Error Log', 'Error log', 'manage_options', Constants::AATXT_PLUGIN_OPTION_LOG_PAGE_SLUG, [self::$instance, 'logOptionsPage']);
201 }
202
203 /**
204 * Implement the page showing error log
205 * @return void
206 */
207 public static function logOptionsPage(): void
208 {
209 ?>
210 <div class="wrap">
211 <h1><?php esc_html_e('Auto Alt Text Error Log', 'auto-alt-text') ?></h1>
212 <div class="aat-options plugin-description">
213 <p>
214 <?php esc_html_e("If the alt text of your images was not generated correctly, you can find the reason in this log.", 'auto-alt-text'); ?>
215 <br>
216 <?php esc_html_e("Each line corresponds to an error generated by a call to the Azure or OpenAI API after uploading an image.", 'auto-alt-text'); ?>
217 </p>
218 <?php
219 $container = Container::make();
220 $logger = $container->get(DBLogger::class);
221 $logs = $logger->getImageLog();
222 if ($logs) {
223 echo '<textarea id="error-log" name="error-log" readonly>' . esc_textarea($logs) . '</textarea>';
224 } else {
225 echo '<p>' . esc_html__('There is no error log yet!', 'auto-alt-text') . '</p>';
226 }
227
228 ?>
229 </div>
230 </div>
231 <?php
232 }
233
234 /**
235 * Implement the main option page
236 * @return void
237 */
238 public static function optionsMainPage(): void
239 {
240 ?>
241 <div class="wrap">
242 <h1><?php esc_html_e('Auto Alt Text Options', 'auto-alt-text') ?></h1>
243
244 <div class="aat-options plugin-description">
245 <p>
246 <?php esc_html_e("This plugin allows you to automatically generate Alt Text for the images that are uploaded to the site's media library.", 'auto-alt-text'); ?>
247 <br>
248 <?php esc_html_e("The following methods are available to generate the alt text:", 'auto-alt-text'); ?>
249 </p>
250 <ul>
251 <li>
252 <strong><?php esc_html_e("OpenAI's APIs", 'auto-alt-text'); ?></strong>: <?php esc_html_e("the image will be analyzed by the AI services provided by OpenAI and an alt text will be generated based on the prompt you set;", 'auto-alt-text'); ?>
253 </li>
254 <li>
255 <strong><?php esc_html_e("Azure's APIs", 'auto-alt-text'); ?></strong>: <?php esc_html_e("the image will be analyzed by the AI services provided by Azure and an alt text will be generated in the language of your choice;", 'auto-alt-text'); ?>
256 </li>
257 <li>
258 <strong><?php esc_html_e("Title of the article (not AI)", 'auto-alt-text'); ?></strong>: <?php esc_html_e("if the image is uploaded within an article, the title of the article will be used as alt text;", 'auto-alt-text'); ?>
259 </li>
260 <li>
261 <strong><?php esc_html_e("Title of the attachment (not AI)", 'auto-alt-text'); ?></strong>: <?php esc_html_e("the title of the attachment will be copied into the alt text;", 'auto-alt-text'); ?>
262 </li>
263 </ul>
264 <p><?php esc_html_e("Once all the necessary data for the chosen generation method has been entered, the alt texts will be created automatically upon uploading each image.", 'auto-alt-text'); ?></p>
265 <p><strong>New</strong>: <?php esc_html_e("For images already in the media library, you can create bulk alt texts. Open the Media Library in the \"list\" view, select the images for which to generate the alt text, and choose the \"Generate alt text\" bulk action. (Depending on the number of images chosen and their weight, this may take some time.)", 'auto-alt-text'); ?></p>
266 <p>
267 <strong><?php esc_html_e('Pay attention please:', 'auto-alt-text') ?></strong> <?php esc_html_e("if the alt text for an image is not generated, check the logs on the", 'auto-alt-text'); ?>
268 <a href="<?php esc_url(menu_page_url(Constants::AATXT_PLUGIN_OPTION_LOG_PAGE_SLUG, true)); ?>"><?php esc_html_e('designated page.', 'auto-alt-text') ?></a>
269 </p>
270 </div>
271 <form method="post" action="options.php" class="aat-options">
272 <?php
273 settings_fields('auto_alt_text_options');
274
275 echo '<div>';
276 echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_TYPOLOGY) . '">' . esc_html__('Generation method', 'auto-alt-text') . '</label>';
277 echo '<p class="description">' . esc_html__("Which method do you want to use to generate the alt text for the images?", 'auto-alt-text') . '</p>';
278 $typology = self::typology();
279
280 ?>
281
282 <select name="<?php echo esc_attr(Constants::AATXT_OPTION_FIELD_TYPOLOGY); ?>"
283 id="<?php echo esc_attr(Constants::AATXT_OPTION_FIELD_TYPOLOGY); ?>">
284 <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>
285 <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>
286 <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>
287 <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>
288 <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>
289 <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>
290 </select>
291 <?php
292 echo '</div>';
293
294 echo '<div class="plugin-option type-article-title"><strong>' . esc_html__('Notice', 'auto-alt-text') . '</strong>: ' .
295 esc_html__('If you try to insert an image into a post that has not yet been saved as a draft or published, the plugin cannot generate an alt text based on the post\'s title since the title itself has not yet been saved.', 'auto-alt-text') . ' ' .
296 esc_html__('Therefore, the alt text "Auto draft" will be inserted. To avoid this behavior, save the article draft first and then upload the image.', 'auto-alt-text') .
297 '</div>';
298
299 $openaiModel = self::openAiModel();
300
301 echo '<div class="plugin-option type-openai">';
302 echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_MODEL_OPENAI) . '">' . esc_html__('Model', 'auto-alt-text') . '</label>';
303
304 echo '<select name="' . esc_attr(Constants::AATXT_OPTION_FIELD_MODEL_OPENAI) . '" id="' . esc_attr(Constants::AATXT_OPTION_FIELD_MODEL_OPENAI) . '">';
305 foreach(Constants::AATXT_OPTION_FIELD_MODEL_OPENAI_OPTIONS as $key => $value) {
306 echo '<option value="' . esc_attr($key) . '" ' . esc_attr(self::selected($openaiModel, $key)) . '>' . esc_html($value) . '</option>';
307 }
308 echo '</select>';
309 echo '</div>';
310
311 echo '<div class="plugin-option type-openai"><strong>' . esc_html__('Notice', 'auto-alt-text') . '</strong>: ' .
312 esc_html__('Rarely it may happen that the chosen model fails to generate correct alt text for the image.', 'auto-alt-text') . ' ' .
313 esc_html__('In these cases, the call to the api will be re-performed using the gpt-4o-mini model as fallback.', 'auto-alt-text') . '<br>' .
314 esc_html__('In case of errors, it is still possible to find the specific reason stated on the', 'auto-alt-text') . ' <a href="' . esc_url(menu_page_url(Constants::AATXT_PLUGIN_OPTION_LOG_PAGE_SLUG, false)) . '">' . esc_html__('error log page', 'auto-alt-text') . '</a>.' .
315 '</div>';
316
317 echo '<div class="plugin-option type-openai">';
318 echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_API_KEY_OPENAI) . '">' . esc_html__('OpenAI API Key', 'auto-alt-text') . '</label>';
319 echo '<p class="description">' . esc_html__("Enter your API Key", 'auto-alt-text') . '</p>';
320 $apiKey = get_option(Constants::AATXT_OPTION_FIELD_API_KEY_OPENAI);
321 echo '<input type="password" name="' . esc_attr(Constants::AATXT_OPTION_FIELD_API_KEY_OPENAI) . '" value="' . esc_attr((Encryption::make())->decrypt($apiKey)) . '" />';
322 echo '</div>';
323
324 echo '<div class="plugin-option type-openai">';
325 echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_PROMPT_OPENAI) . '">' . esc_html__('Prompt', 'auto-alt-text') . '</label>';
326 echo '<p class="description">' . esc_html__("Enter a specific and detailed prompt according to your needs.", 'auto-alt-text') . '</p>';
327 $defaultPrompt = sprintf(esc_html__("Act like an SEO expert and write an alt text of up to 125 characters for this image.", 'auto-alt-text'), Constants::AATXT_IMAGE_URL_TAG);
328 $prompt = get_option(Constants::AATXT_OPTION_FIELD_PROMPT_OPENAI) ?: $defaultPrompt;
329 echo '<textarea name="' . esc_attr(Constants::AATXT_OPTION_FIELD_PROMPT_OPENAI) . '" rows="5" cols="50">' . esc_textarea($prompt) . '</textarea>';
330 echo '</div>';
331
332 echo '<div class="plugin-option type-azure">' . esc_html__("Fill out the following fields to leverage Azure's computer vision services to generate the Alt texts.", 'auto-alt-text') . '</div>';
333
334 echo '<div class="plugin-option type-azure">';
335 echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_COMPUTER_VISION) . '">' . esc_html__('Azure Computer Vision API Key', 'auto-alt-text') . '</label>';
336 echo '<p class="description">' . esc_html__("Enter the API key for the Computer Vision service of your Azure account.", 'auto-alt-text') . '</p>';
337 $apiKey = get_option(Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_COMPUTER_VISION);
338 echo '<input type="password" name="' . esc_attr(Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_COMPUTER_VISION) . '" value="' . esc_attr((Encryption::make())->decrypt($apiKey)) . '" />';
339 echo '</div>';
340
341 echo '<div class="plugin-option type-azure">';
342 echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_ENDPOINT_AZURE_COMPUTER_VISION) . '">' . esc_html__('Azure Computer Vision Endpoint', 'auto-alt-text') . '</label>';
343 echo '<p class="description">' . esc_html__("Enter the endpoint of the Computer Vision service.", 'auto-alt-text') . ' (es. https://computer-vision-france-central.cognitiveservices.azure.com/)</p>';
344 $endpoint = get_option(Constants::AATXT_OPTION_FIELD_ENDPOINT_AZURE_COMPUTER_VISION);
345 echo '<input type="text" name="' . esc_attr(Constants::AATXT_OPTION_FIELD_ENDPOINT_AZURE_COMPUTER_VISION) . '" value="' . esc_attr($endpoint) . '" />';
346 echo '</div>';
347
348 echo '<div class="plugin-option type-azure">' .
349 '<strong>' . esc_html__('The default alt text language is English.', 'auto-alt-text') . '</strong><br>' .
350 esc_html__('If you want to translate into another language, enter the following data necessary for the translation API to work.', 'auto-alt-text') . ' ' .
351 esc_html__('After saving the changes you can select the desired language.', 'auto-alt-text') .
352 '</div>';
353
354 echo '<div class="plugin-option type-azure">';
355 echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_TRANSLATE_INSTANCE) . '">' . esc_html__('Azure Translate Instance API Key', 'auto-alt-text') . '</label>';
356 echo '<p class="description">' . esc_html__("Enter your API key for the Azure Translate Instance service.", 'auto-alt-text') . '</p>';
357 $translationApiKey = get_option(Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_TRANSLATE_INSTANCE);
358 echo '<input type="password" name="' . esc_attr(Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_TRANSLATE_INSTANCE) . '" value="' . esc_attr((Encryption::make())->decrypt($translationApiKey)) . '" class="notRequired" />';
359 echo '</div>';
360
361 echo '<div class="plugin-option type-azure">';
362 echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_ENDPOINT_AZURE_TRANSLATE_INSTANCE) . '">' . esc_html__('Azure Translate Instance Endpoint', 'auto-alt-text') . '</label>';
363 echo '<p class="description">' . esc_html__("Enter the endpoint of the Translate Instance service", 'auto-alt-text') . ' (es. https://api.cognitive.microsofttranslator.com/)</p>';
364 $translationEndpoint = get_option(Constants::AATXT_OPTION_FIELD_ENDPOINT_AZURE_TRANSLATE_INSTANCE);
365 echo '<input type="text" name="' . esc_attr(Constants::AATXT_OPTION_FIELD_ENDPOINT_AZURE_TRANSLATE_INSTANCE) . '" value="' . esc_attr($translationEndpoint) . '" class="notRequired" />';
366 echo '</div>';
367
368 echo '<div class="plugin-option type-azure">';
369 echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_REGION_AZURE_TRANSLATE_INSTANCE) . '">' . esc_html__('Azure Translate Instance Region', 'auto-alt-text') . '</label>';
370 echo '<p class="description">' . esc_html__("Enter the region of the Azure Translate Instance service.", 'auto-alt-text') . ' (es. westeurope)</p>';
371 $translationRegion = get_option(Constants::AATXT_OPTION_FIELD_REGION_AZURE_TRANSLATE_INSTANCE);
372 echo '<input type="text" name="' . esc_attr(Constants::AATXT_OPTION_FIELD_REGION_AZURE_TRANSLATE_INSTANCE) . '" value="' . esc_attr($translationRegion) . '" class="notRequired" />';
373 echo '</div>';
374
375 if ($translationApiKey && $translationEndpoint && $translationRegion):
376 echo '<div class="plugin-option type-azure">';
377 echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_LANGUAGE_AZURE_TRANSLATE_INSTANCE) . '">' . esc_html__('Alt Text Language', 'auto-alt-text') . '</label>';
378 echo '<p class="description">' . esc_html__("Select the language in which the alt text should be written.", 'auto-alt-text') . '</p>';
379 $currentLanguage = get_option(Constants::AATXT_OPTION_FIELD_LANGUAGE_AZURE_TRANSLATE_INSTANCE);
380
381 try {
382 $httpClient = new WordPressHttpClient();
383 $azureConfig = new AzureConfig(
384 self::apiKeyAzureComputerVision(),
385 self::endpointAzureComputerVision(),
386 '',
387 '',
388 self::apiKeyAzureTranslateInstance(),
389 self::endpointAzureTranslateInstance(),
390 self::regionAzureTranslateInstance(),
391 self::languageAzureTranslateInstance()
392 );
393 $azureTranslator = new AzureTranslator($httpClient, $azureConfig);
394 $supportedLanguages = $azureTranslator->supportedLanguages();
395 } catch (AzureTranslateInstanceException $e) {
396 $supportedLanguages = [
397 "en" => [
398 "name" => "English",
399 "nativeName" => "English",
400 "dir" => "ltr",
401 ]
402 ];
403 echo '<p style="color:red"><strong>' . esc_html($e->getMessage()) . '</strong></p>';
404 }
405
406 ?>
407 <select name="<?php echo esc_attr(Constants::AATXT_OPTION_FIELD_LANGUAGE_AZURE_TRANSLATE_INSTANCE); ?>"
408 id="<?php echo esc_attr(Constants::AATXT_OPTION_FIELD_LANGUAGE_AZURE_TRANSLATE_INSTANCE); ?>">
409 <?php
410 foreach ($supportedLanguages as $key => $language):
411 ?>
412 <option value="<?php echo esc_attr($key) ?>" <?php echo esc_attr(self::selected($currentLanguage, $key)); ?>><?php echo esc_html($language['name']); ?></option>
413 <?php
414 endforeach;
415 ?>
416 </select>
417
418 <?php
419
420 echo '</div>';
421 endif;
422
423 echo '<div class="plugin-option type-anthropic">';
424 echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_API_KEY_ANTHROPIC) . '">' . esc_html__('Antrhopic API Key', 'auto-alt-text') . '</label>';
425 echo '<p class="description">' . esc_html__("Enter your API Key", 'auto-alt-text') . '</p>';
426 $apiKey = get_option(Constants::AATXT_OPTION_FIELD_API_KEY_ANTHROPIC);
427 echo '<input type="password" name="' . esc_attr(Constants::AATXT_OPTION_FIELD_API_KEY_ANTHROPIC) . '" value="' . esc_attr((Encryption::make())->decrypt($apiKey)) . '" />';
428 echo '</div>';
429
430 $anthropicModel = self::anthropicModel();
431
432 echo '<div class="plugin-option type-anthropic">';
433 echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_MODEL_ANTHROPIC) . '">' . esc_html__('Model', 'auto-alt-text') . '</label>';
434
435 echo '<select name="' . esc_attr(Constants::AATXT_OPTION_FIELD_MODEL_ANTHROPIC) . '" id="' . esc_attr(Constants::AATXT_OPTION_FIELD_MODEL_ANTHROPIC) . '">';
436 foreach(Constants::AATXT_OPTION_FIELD_MODEL_ANTHROPIC_OPTIONS as $key => $value) {
437 echo '<option value="' . esc_attr($key) . '" ' . esc_attr(self::selected($anthropicModel, $key)) . '>' . esc_html($value) . '</option>';
438 }
439 echo '</select>';
440 echo '</div>';
441
442 echo '<div class="plugin-option type-anthropic">';
443 echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_PROMPT_ANTHROPIC) . '">' . esc_html__('Prompt', 'auto-alt-text') . '</label>';
444 echo '<p class="description">' . esc_html__("Enter a specific and detailed prompt according to your needs.", 'auto-alt-text') . '</p>';
445 $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);
446 $prompt = get_option(Constants::AATXT_OPTION_FIELD_PROMPT_ANTHROPIC) ?: $defaultPrompt;
447 echo '<textarea name="' . esc_attr(Constants::AATXT_OPTION_FIELD_PROMPT_ANTHROPIC) . '" rows="5" cols="50">' . esc_textarea($prompt) . '</textarea>';
448 echo '</div>';
449
450 echo '<div class="plugin-option type-article-title type-attachment-title type-openai type-azure type-anthropic">';
451 echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_PRESERVE_EXISTING_ALT_TEXT) . '">' . esc_html__('Keep existing alt text', 'auto-alt-text') . '</label>';
452 echo '<p class="description">' . esc_html__("If checked, the existing alt text of images will not be overwritten.", 'auto-alt-text') . '</p>';
453 $preserveAltText = get_option(Constants::AATXT_OPTION_FIELD_PRESERVE_EXISTING_ALT_TEXT);
454 echo '<input type="checkbox" name="' . esc_attr(Constants::AATXT_OPTION_FIELD_PRESERVE_EXISTING_ALT_TEXT) . '" value="1" class="notRequired" ' . checked(1, $preserveAltText, false) . ' />';
455 echo '</div>';
456
457 submit_button();
458 ?>
459 </form>
460 </div>
461 <?php
462 }
463
464 /**
465 * If option is selected return the selected attribute
466 * @param string $selectedValue
467 * @param string $inputValue
468 * @return string
469 */
470 public static function selected(string $selectedValue, string $inputValue): string
471 {
472 if (empty($selectedValue) && 'en' == $inputValue) {
473 return ' selected';
474 }
475 return $selectedValue == $inputValue ? ' selected' : '';
476 }
477
478
479 /**
480 * Register input fields and option settings
481 * @return void
482 */
483 public static function setupPluginOptions(): void
484 {
485 register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_API_KEY_OPENAI);
486 register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_PROMPT_OPENAI, [self::class, 'sanitizeTextArea']);
487 register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_TYPOLOGY, [self::class, 'sanitizeText']);
488 register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_MODEL_OPENAI, [self::class, 'sanitizeText']);
489 register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_COMPUTER_VISION);
490 register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_ENDPOINT_AZURE_COMPUTER_VISION, [self::class, 'sanitizeUrl']);
491 register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_TRANSLATE_INSTANCE);
492 register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_ENDPOINT_AZURE_TRANSLATE_INSTANCE, [self::class, 'sanitizeUrl']);
493 register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_REGION_AZURE_TRANSLATE_INSTANCE, [self::class, 'sanitizeText']);
494 register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_LANGUAGE_AZURE_TRANSLATE_INSTANCE, [self::class, 'sanitizeText']);
495 register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_PRESERVE_EXISTING_ALT_TEXT);
496 register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_API_KEY_ANTHROPIC);
497 register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_PROMPT_ANTHROPIC, [self::class, 'sanitizeTextArea']);
498 register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_MODEL_ANTHROPIC, [self::class, 'sanitizeText']);
499 }
500
501 /**
502 * @return string
503 */
504 public static function typology(): string
505 {
506 return get_option(Constants::AATXT_OPTION_FIELD_TYPOLOGY);
507 }
508
509 public static function openAiModel(): string
510 {
511 return get_option(Constants::AATXT_OPTION_FIELD_MODEL_OPENAI) ?: Constants::AATXT_GPT4O;
512 }
513
514 public static function anthropicModel(): string
515 {
516 return get_option(Constants::AATXT_OPTION_FIELD_MODEL_ANTHROPIC) ?: Constants::AATXT_CLAUDE_HAIKU_3_5;
517 }
518
519 /**
520 * @return string
521 */
522 public static function openAiPrompt(): string
523 {
524 return get_option(Constants::AATXT_OPTION_FIELD_PROMPT_OPENAI);
525 }
526
527 /**
528 * @return string
529 */
530 public static function anthropicPrompt(): string
531 {
532 return get_option(Constants::AATXT_OPTION_FIELD_PROMPT_ANTHROPIC);
533 }
534
535 /**
536 * @return string
537 */
538 public static function apiKeyOpenAI(): string
539 {
540 $apiKey = get_option(Constants::AATXT_OPTION_FIELD_API_KEY_OPENAI);
541 return (Encryption::make())->decrypt($apiKey);
542 }
543
544 /**
545 * @return string
546 */
547 public static function apiKeyAnthropic(): string
548 {
549 $apiKey = get_option(Constants::AATXT_OPTION_FIELD_API_KEY_ANTHROPIC);
550 return (Encryption::make())->decrypt($apiKey);
551 }
552
553 /**
554 * @return string
555 */
556 public static function apiKeyAzureComputerVision(): string
557 {
558 $apiKey = get_option(Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_COMPUTER_VISION);
559 return (Encryption::make())->decrypt($apiKey);
560 }
561
562 /**
563 * @return string
564 */
565 public static function endpointAzureComputerVision(): string
566 {
567 return get_option(Constants::AATXT_OPTION_FIELD_ENDPOINT_AZURE_COMPUTER_VISION);
568 }
569
570 /**
571 * @return string
572 */
573 public static function apiKeyAzureTranslateInstance(): string
574 {
575 $apiKey = get_option(Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_TRANSLATE_INSTANCE);
576 return (Encryption::make())->decrypt($apiKey);
577 }
578
579 /**
580 * @return string
581 */
582 public static function endpointAzureTranslateInstance(): string
583 {
584 return get_option(Constants::AATXT_OPTION_FIELD_ENDPOINT_AZURE_TRANSLATE_INSTANCE);
585 }
586
587 /**
588 * @return string
589 */
590 public static function regionAzureTranslateInstance(): string
591 {
592 return get_option(Constants::AATXT_OPTION_FIELD_REGION_AZURE_TRANSLATE_INSTANCE);
593 }
594
595 /**
596 * @return string
597 */
598 public static function languageAzureTranslateInstance(): string
599 {
600 return get_option(Constants::AATXT_OPTION_FIELD_LANGUAGE_AZURE_TRANSLATE_INSTANCE) ?: 'en';
601 }
602
603 /**
604 * @return bool
605 */
606 public static function preserveExistingAltText(): bool
607 {
608 return get_option(Constants::AATXT_OPTION_FIELD_PRESERVE_EXISTING_ALT_TEXT);
609 }
610
611 public static function sanitizeUrl($input): string
612 {
613 return sanitize_url($input);
614 }
615
616 public static function sanitizeText($input): string
617 {
618 return sanitize_text_field($input);
619 }
620
621 public static function sanitizeTextArea($input): string
622 {
623 return sanitize_textarea_field($input);
624 }
625 }
626