| 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\Exceptions\Azure\AzureTranslateInstanceException; |
| 8 |
use AATXT\App\Utilities\AssetsManager; |
| 9 |
use AATXT\App\Utilities\Encryption; |
| 10 |
use AATXT\Config\Constants; |
| 11 |
|
| 12 |
class PluginOptions |
| 13 |
{ |
| 14 |
private static ?self $instance = null; |
| 15 |
private static AssetsManager $assetsManager; |
| 16 |
|
| 17 |
private function __construct() |
| 18 |
{ |
| 19 |
// |
| 20 |
} |
| 21 |
|
| 22 |
/** |
| 23 |
* Manage the necessary hooks to implement plugin options and their pages |
| 24 |
* @return void |
| 25 |
*/ |
| 26 |
public static function register(): void |
| 27 |
{ |
| 28 |
if (is_null(self::$instance)) { |
| 29 |
self::$instance = new self(); |
| 30 |
} |
| 31 |
|
| 32 |
self::$assetsManager = AssetsManager::make(); |
| 33 |
|
| 34 |
add_action('admin_notices', [self::$instance, 'showEncryptionConstantsNotice']); |
| 35 |
|
| 36 |
add_action('admin_enqueue_scripts', [self::$instance, 'enqueueAdminScripts'], 1); |
| 37 |
add_action('admin_menu', [self::$instance, 'addOptionsPageToTheMenu']); |
| 38 |
add_action('admin_init', [self::$instance, 'setupPluginOptions'], 10); |
| 39 |
add_action('admin_init', [self::$instance, 'migrateEncryptionKeys'], 9); |
| 40 |
|
| 41 |
// Encrypt API Keys on update |
| 42 |
add_action('pre_update_option_' . Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_COMPUTER_VISION, [self::$instance, 'encryptDataOnUpdate'], 10, 3); |
| 43 |
add_action('pre_update_option_' . Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_TRANSLATE_INSTANCE, [self::$instance, 'encryptDataOnUpdate'], 10, 3); |
| 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']); |
| 48 |
} |
| 49 |
|
| 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 |
/** |
| 156 |
* Encrypt data |
| 157 |
* @param ?string $newValue |
| 158 |
* @return ?string |
| 159 |
*/ |
| 160 |
public function encryptDataOnUpdate(?string $newValue): ?string |
| 161 |
{ |
| 162 |
if (!empty($newValue)) { |
| 163 |
$newValue = (Encryption::make())->encrypt($newValue); |
| 164 |
} |
| 165 |
return $newValue; |
| 166 |
} |
| 167 |
|
| 168 |
/** |
| 169 |
* @return void |
| 170 |
*/ |
| 171 |
public static function enqueueAdminScripts(): void |
| 172 |
{ |
| 173 |
$screen = get_current_screen(); |
| 174 |
$isMainOptionsPage = $screen->id === 'toplevel_page_' . Constants::AATXT_PLUGIN_OPTIONS_PAGE_SLUG; |
| 175 |
|
| 176 |
if ($isMainOptionsPage || strpos($screen->id, Constants::AATXT_PLUGIN_SLUG . '_') !== false) { |
| 177 |
|
| 178 |
$adminCss = self::$assetsManager->getAssetUrl('resources/js/admin.js', true); |
| 179 |
|
| 180 |
wp_enqueue_style(Constants::AATXT_PLUGIN_ASSETS_HANDLE, $adminCss, [], false); |
| 181 |
|
| 182 |
if ($isMainOptionsPage) { |
| 183 |
$adminJs = self::$assetsManager->getAssetUrl('resources/js/admin.js', false); |
| 184 |
wp_enqueue_script(Constants::AATXT_PLUGIN_ASSETS_HANDLE, $adminJs, [], false); |
| 185 |
} |
| 186 |
|
| 187 |
} |
| 188 |
} |
| 189 |
|
| 190 |
/** |
| 191 |
* Create options pages |
| 192 |
* @return void |
| 193 |
*/ |
| 194 |
public static function addOptionsPageToTheMenu(): void |
| 195 |
{ |
| 196 |
add_menu_page('Auto Alt Text Options', 'Auto Alt Text', 'manage_options', Constants::AATXT_PLUGIN_OPTIONS_PAGE_SLUG, [self::$instance, 'optionsMainPage'], null, 99); |
| 197 |
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']); |
| 198 |
} |
| 199 |
|
| 200 |
/** |
| 201 |
* Implement the page showing error log |
| 202 |
* @return void |
| 203 |
*/ |
| 204 |
public static function logOptionsPage(): void |
| 205 |
{ |
| 206 |
?> |
| 207 |
<div class="wrap"> |
| 208 |
<h1><?php esc_html_e('Auto Alt Text Error Log', 'auto-alt-text') ?></h1> |
| 209 |
<div class="aat-options plugin-description"> |
| 210 |
<p> |
| 211 |
<?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'); ?> |
| 212 |
<br> |
| 213 |
<?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'); ?> |
| 214 |
</p> |
| 215 |
<?php |
| 216 |
$logs = DBLogger::make()->getImageLog(); |
| 217 |
if ($logs) { |
| 218 |
echo '<textarea id="error-log" name="error-log" readonly>' . esc_textarea($logs) . '</textarea>'; |
| 219 |
} else { |
| 220 |
echo '<p>' . esc_html__('There is no error log yet!', 'auto-alt-text') . '</p>'; |
| 221 |
} |
| 222 |
|
| 223 |
?> |
| 224 |
</div> |
| 225 |
</div> |
| 226 |
<?php |
| 227 |
} |
| 228 |
|
| 229 |
/** |
| 230 |
* Implement the main option page |
| 231 |
* @return void |
| 232 |
*/ |
| 233 |
public static function optionsMainPage(): void |
| 234 |
{ |
| 235 |
?> |
| 236 |
<div class="wrap"> |
| 237 |
<h1><?php esc_html_e('Auto Alt Text Options', 'auto-alt-text') ?></h1> |
| 238 |
|
| 239 |
<div class="aat-options plugin-description"> |
| 240 |
<p> |
| 241 |
<?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'); ?> |
| 242 |
<br> |
| 243 |
<?php esc_html_e("The following methods are available to generate the alt text:", 'auto-alt-text'); ?> |
| 244 |
</p> |
| 245 |
<ul> |
| 246 |
<li> |
| 247 |
<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'); ?> |
| 248 |
</li> |
| 249 |
<li> |
| 250 |
<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'); ?> |
| 251 |
</li> |
| 252 |
<li> |
| 253 |
<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'); ?> |
| 254 |
</li> |
| 255 |
<li> |
| 256 |
<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'); ?> |
| 257 |
</li> |
| 258 |
</ul> |
| 259 |
<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> |
| 260 |
<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> |
| 261 |
<p> |
| 262 |
<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'); ?> |
| 263 |
<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> |
| 264 |
</p> |
| 265 |
</div> |
| 266 |
<form method="post" action="options.php" class="aat-options"> |
| 267 |
<?php |
| 268 |
settings_fields('auto_alt_text_options'); |
| 269 |
|
| 270 |
echo '<div>'; |
| 271 |
echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_TYPOLOGY) . '">' . esc_html__('Generation method', 'auto-alt-text') . '</label>'; |
| 272 |
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>'; |
| 273 |
$typology = self::typology(); |
| 274 |
|
| 275 |
?> |
| 276 |
|
| 277 |
<select name="<?php echo esc_attr(Constants::AATXT_OPTION_FIELD_TYPOLOGY); ?>" |
| 278 |
id="<?php echo esc_attr(Constants::AATXT_OPTION_FIELD_TYPOLOGY); ?>"> |
| 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> |
| 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> |
| 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> |
| 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> |
| 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> |
| 285 |
</select> |
| 286 |
<?php |
| 287 |
echo '</div>'; |
| 288 |
|
| 289 |
echo '<div class="plugin-option type-article-title"><strong>' . esc_html__('Notice', 'auto-alt-text') . '</strong>: ' . |
| 290 |
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') . ' ' . |
| 291 |
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') . |
| 292 |
'</div>'; |
| 293 |
|
| 294 |
$openaiModel = self::openAiModel(); |
| 295 |
|
| 296 |
echo '<div class="plugin-option type-openai">'; |
| 297 |
echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_MODEL_OPENAI) . '">' . esc_html__('Model', 'auto-alt-text') . '</label>'; |
| 298 |
|
| 299 |
echo '<select name="' . esc_attr(Constants::AATXT_OPTION_FIELD_MODEL_OPENAI) . '" id="' . esc_attr(Constants::AATXT_OPTION_FIELD_MODEL_OPENAI) . '">'; |
| 300 |
foreach(Constants::AATXT_OPTION_FIELD_MODEL_OPENAI_OPTIONS as $key => $value) { |
| 301 |
echo '<option value="' . esc_attr($key) . '" ' . esc_attr(self::selected($openaiModel, $key)) . '>' . esc_html($value) . '</option>'; |
| 302 |
} |
| 303 |
echo '</select>'; |
| 304 |
echo '</div>'; |
| 305 |
|
| 306 |
echo '<div class="plugin-option type-openai"><strong>' . esc_html__('Notice', 'auto-alt-text') . '</strong>: ' . |
| 307 |
esc_html__('Rarely it may happen that the chosen model fails to generate correct alt text for the image.', 'auto-alt-text') . ' ' . |
| 308 |
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>' . |
| 309 |
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>.' . |
| 310 |
'</div>'; |
| 311 |
|
| 312 |
echo '<div class="plugin-option type-openai">'; |
| 313 |
echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_API_KEY_OPENAI) . '">' . esc_html__('OpenAI API Key', 'auto-alt-text') . '</label>'; |
| 314 |
echo '<p class="description">' . esc_html__("Enter your API Key", 'auto-alt-text') . '</p>'; |
| 315 |
$apiKey = get_option(Constants::AATXT_OPTION_FIELD_API_KEY_OPENAI); |
| 316 |
echo '<input type="password" name="' . esc_attr(Constants::AATXT_OPTION_FIELD_API_KEY_OPENAI) . '" value="' . esc_attr((Encryption::make())->decrypt($apiKey)) . '" />'; |
| 317 |
echo '</div>'; |
| 318 |
|
| 319 |
echo '<div class="plugin-option type-openai">'; |
| 320 |
echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_PROMPT_OPENAI) . '">' . esc_html__('Prompt', 'auto-alt-text') . '</label>'; |
| 321 |
echo '<p class="description">' . esc_html__("Enter a specific and detailed prompt according to your needs.", 'auto-alt-text') . '</p>'; |
| 322 |
$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); |
| 323 |
$prompt = get_option(Constants::AATXT_OPTION_FIELD_PROMPT_OPENAI) ?: $defaultPrompt; |
| 324 |
echo '<textarea name="' . esc_attr(Constants::AATXT_OPTION_FIELD_PROMPT_OPENAI) . '" rows="5" cols="50">' . esc_textarea($prompt) . '</textarea>'; |
| 325 |
echo '</div>'; |
| 326 |
|
| 327 |
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>'; |
| 328 |
|
| 329 |
echo '<div class="plugin-option type-azure">'; |
| 330 |
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>'; |
| 331 |
echo '<p class="description">' . esc_html__("Enter the API key for the Computer Vision service of your Azure account.", 'auto-alt-text') . '</p>'; |
| 332 |
$apiKey = get_option(Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_COMPUTER_VISION); |
| 333 |
echo '<input type="password" name="' . esc_attr(Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_COMPUTER_VISION) . '" value="' . esc_attr((Encryption::make())->decrypt($apiKey)) . '" />'; |
| 334 |
echo '</div>'; |
| 335 |
|
| 336 |
echo '<div class="plugin-option type-azure">'; |
| 337 |
echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_ENDPOINT_AZURE_COMPUTER_VISION) . '">' . esc_html__('Azure Computer Vision Endpoint', 'auto-alt-text') . '</label>'; |
| 338 |
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>'; |
| 339 |
$endpoint = get_option(Constants::AATXT_OPTION_FIELD_ENDPOINT_AZURE_COMPUTER_VISION); |
| 340 |
echo '<input type="text" name="' . esc_attr(Constants::AATXT_OPTION_FIELD_ENDPOINT_AZURE_COMPUTER_VISION) . '" value="' . esc_attr($endpoint) . '" />'; |
| 341 |
echo '</div>'; |
| 342 |
|
| 343 |
echo '<div class="plugin-option type-azure">' . |
| 344 |
'<strong>' . esc_html__('The default alt text language is English.', 'auto-alt-text') . '</strong><br>' . |
| 345 |
esc_html__('If you want to translate into another language, enter the following data necessary for the translation API to work.', 'auto-alt-text') . ' ' . |
| 346 |
esc_html__('After saving the changes you can select the desired language.', 'auto-alt-text') . |
| 347 |
'</div>'; |
| 348 |
|
| 349 |
echo '<div class="plugin-option type-azure">'; |
| 350 |
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>'; |
| 351 |
echo '<p class="description">' . esc_html__("Enter your API key for the Azure Translate Instance service.", 'auto-alt-text') . '</p>'; |
| 352 |
$translationApiKey = get_option(Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_TRANSLATE_INSTANCE); |
| 353 |
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" />'; |
| 354 |
echo '</div>'; |
| 355 |
|
| 356 |
echo '<div class="plugin-option type-azure">'; |
| 357 |
echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_ENDPOINT_AZURE_TRANSLATE_INSTANCE) . '">' . esc_html__('Azure Translate Instance Endpoint', 'auto-alt-text') . '</label>'; |
| 358 |
echo '<p class="description">' . esc_html__("Enter the endpoint of the Translate Instance service", 'auto-alt-text') . ' (es. https://api.cognitive.microsofttranslator.com/)</p>'; |
| 359 |
$translationEndpoint = get_option(Constants::AATXT_OPTION_FIELD_ENDPOINT_AZURE_TRANSLATE_INSTANCE); |
| 360 |
echo '<input type="text" name="' . esc_attr(Constants::AATXT_OPTION_FIELD_ENDPOINT_AZURE_TRANSLATE_INSTANCE) . '" value="' . esc_attr($translationEndpoint) . '" class="notRequired" />'; |
| 361 |
echo '</div>'; |
| 362 |
|
| 363 |
echo '<div class="plugin-option type-azure">'; |
| 364 |
echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_REGION_AZURE_TRANSLATE_INSTANCE) . '">' . esc_html__('Azure Translate Instance Region', 'auto-alt-text') . '</label>'; |
| 365 |
echo '<p class="description">' . esc_html__("Enter the region of the Azure Translate Instance service.", 'auto-alt-text') . ' (es. westeurope)</p>'; |
| 366 |
$translationRegion = get_option(Constants::AATXT_OPTION_FIELD_REGION_AZURE_TRANSLATE_INSTANCE); |
| 367 |
echo '<input type="text" name="' . esc_attr(Constants::AATXT_OPTION_FIELD_REGION_AZURE_TRANSLATE_INSTANCE) . '" value="' . esc_attr($translationRegion) . '" class="notRequired" />'; |
| 368 |
echo '</div>'; |
| 369 |
|
| 370 |
if ($translationApiKey && $translationEndpoint && $translationRegion): |
| 371 |
echo '<div class="plugin-option type-azure">'; |
| 372 |
echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_LANGUAGE_AZURE_TRANSLATE_INSTANCE) . '">' . esc_html__('Alt Text Language', 'auto-alt-text') . '</label>'; |
| 373 |
echo '<p class="description">' . esc_html__("Select the language in which the alt text should be written.", 'auto-alt-text') . '</p>'; |
| 374 |
$currentLanguage = get_option(Constants::AATXT_OPTION_FIELD_LANGUAGE_AZURE_TRANSLATE_INSTANCE); |
| 375 |
|
| 376 |
try { |
| 377 |
$supportedLanguages = (AzureTranslator::make())->supportedLanguages(); |
| 378 |
} catch (AzureTranslateInstanceException $e) { |
| 379 |
$supportedLanguages = [ |
| 380 |
"en" => [ |
| 381 |
"name" => "English", |
| 382 |
"nativeName" => "English", |
| 383 |
"dir" => "ltr", |
| 384 |
] |
| 385 |
]; |
| 386 |
echo '<p style="color:red"><strong>' . esc_html($e->getMessage()) . '</strong></p>'; |
| 387 |
} |
| 388 |
|
| 389 |
?> |
| 390 |
<select name="<?php echo esc_attr(Constants::AATXT_OPTION_FIELD_LANGUAGE_AZURE_TRANSLATE_INSTANCE); ?>" |
| 391 |
id="<?php echo esc_attr(Constants::AATXT_OPTION_FIELD_LANGUAGE_AZURE_TRANSLATE_INSTANCE); ?>"> |
| 392 |
<?php |
| 393 |
foreach ($supportedLanguages as $key => $language): |
| 394 |
?> |
| 395 |
<option value="<?php echo esc_attr($key) ?>" <?php echo esc_attr(self::selected($currentLanguage, $key)); ?>><?php echo esc_html($language['name']); ?></option> |
| 396 |
<?php |
| 397 |
endforeach; |
| 398 |
?> |
| 399 |
</select> |
| 400 |
|
| 401 |
<?php |
| 402 |
|
| 403 |
echo '</div>'; |
| 404 |
endif; |
| 405 |
|
| 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">'; |
| 434 |
echo '<label for="' . esc_attr(Constants::AATXT_OPTION_FIELD_PRESERVE_EXISTING_ALT_TEXT) . '">' . esc_html__('Keep existing alt text', 'auto-alt-text') . '</label>'; |
| 435 |
echo '<p class="description">' . esc_html__("If checked, the existing alt text of images will not be overwritten.", 'auto-alt-text') . '</p>'; |
| 436 |
$preserveAltText = get_option(Constants::AATXT_OPTION_FIELD_PRESERVE_EXISTING_ALT_TEXT); |
| 437 |
echo '<input type="checkbox" name="' . esc_attr(Constants::AATXT_OPTION_FIELD_PRESERVE_EXISTING_ALT_TEXT) . '" value="1" class="notRequired" ' . checked(1, $preserveAltText, false) . ' />'; |
| 438 |
echo '</div>'; |
| 439 |
|
| 440 |
submit_button(); |
| 441 |
?> |
| 442 |
</form> |
| 443 |
</div> |
| 444 |
<?php |
| 445 |
} |
| 446 |
|
| 447 |
/** |
| 448 |
* If option is selected return the selected attribute |
| 449 |
* @param string $selectedValue |
| 450 |
* @param string $inputValue |
| 451 |
* @return string |
| 452 |
*/ |
| 453 |
public static function selected(string $selectedValue, string $inputValue): string |
| 454 |
{ |
| 455 |
if (empty($selectedValue) && 'en' == $inputValue) { |
| 456 |
return ' selected'; |
| 457 |
} |
| 458 |
return $selectedValue == $inputValue ? ' selected' : ''; |
| 459 |
} |
| 460 |
|
| 461 |
|
| 462 |
/** |
| 463 |
* Register input fields and option settings |
| 464 |
* @return void |
| 465 |
*/ |
| 466 |
public static function setupPluginOptions(): void |
| 467 |
{ |
| 468 |
register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_API_KEY_OPENAI); |
| 469 |
register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_PROMPT_OPENAI, [self::class, 'sanitizeTextArea']); |
| 470 |
register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_TYPOLOGY, [self::class, 'sanitizeText']); |
| 471 |
register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_MODEL_OPENAI, [self::class, 'sanitizeText']); |
| 472 |
register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_COMPUTER_VISION); |
| 473 |
register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_ENDPOINT_AZURE_COMPUTER_VISION, [self::class, 'sanitizeUrl']); |
| 474 |
register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_TRANSLATE_INSTANCE); |
| 475 |
register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_ENDPOINT_AZURE_TRANSLATE_INSTANCE, [self::class, 'sanitizeUrl']); |
| 476 |
register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_REGION_AZURE_TRANSLATE_INSTANCE, [self::class, 'sanitizeText']); |
| 477 |
register_setting('auto_alt_text_options', Constants::AATXT_OPTION_FIELD_LANGUAGE_AZURE_TRANSLATE_INSTANCE, [self::class, 'sanitizeText']); |
| 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']); |
| 482 |
} |
| 483 |
|
| 484 |
/** |
| 485 |
* @return string |
| 486 |
*/ |
| 487 |
public static function typology(): string |
| 488 |
{ |
| 489 |
return get_option(Constants::AATXT_OPTION_FIELD_TYPOLOGY); |
| 490 |
} |
| 491 |
|
| 492 |
public static function openAiModel(): string |
| 493 |
{ |
| 494 |
return get_option(Constants::AATXT_OPTION_FIELD_MODEL_OPENAI) ?: Constants::AATXT_GPT4O; |
| 495 |
} |
| 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 |
|
| 502 |
/** |
| 503 |
* @return string |
| 504 |
*/ |
| 505 |
public static function openAiPrompt(): string |
| 506 |
{ |
| 507 |
return get_option(Constants::AATXT_OPTION_FIELD_PROMPT_OPENAI); |
| 508 |
} |
| 509 |
|
| 510 |
/** |
| 511 |
* @return string |
| 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 |
*/ |
| 521 |
public static function apiKeyOpenAI(): string |
| 522 |
{ |
| 523 |
$apiKey = get_option(Constants::AATXT_OPTION_FIELD_API_KEY_OPENAI); |
| 524 |
return (Encryption::make())->decrypt($apiKey); |
| 525 |
} |
| 526 |
|
| 527 |
/** |
| 528 |
* @return string |
| 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 |
*/ |
| 539 |
public static function apiKeyAzureComputerVision(): string |
| 540 |
{ |
| 541 |
$apiKey = get_option(Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_COMPUTER_VISION); |
| 542 |
return (Encryption::make())->decrypt($apiKey); |
| 543 |
} |
| 544 |
|
| 545 |
/** |
| 546 |
* @return string |
| 547 |
*/ |
| 548 |
public static function endpointAzureComputerVision(): string |
| 549 |
{ |
| 550 |
return get_option(Constants::AATXT_OPTION_FIELD_ENDPOINT_AZURE_COMPUTER_VISION); |
| 551 |
} |
| 552 |
|
| 553 |
/** |
| 554 |
* @return string |
| 555 |
*/ |
| 556 |
public static function apiKeyAzureTranslateInstance(): string |
| 557 |
{ |
| 558 |
$apiKey = get_option(Constants::AATXT_OPTION_FIELD_API_KEY_AZURE_TRANSLATE_INSTANCE); |
| 559 |
return (Encryption::make())->decrypt($apiKey); |
| 560 |
} |
| 561 |
|
| 562 |
/** |
| 563 |
* @return string |
| 564 |
*/ |
| 565 |
public static function endpointAzureTranslateInstance(): string |
| 566 |
{ |
| 567 |
return get_option(Constants::AATXT_OPTION_FIELD_ENDPOINT_AZURE_TRANSLATE_INSTANCE); |
| 568 |
} |
| 569 |
|
| 570 |
/** |
| 571 |
* @return string |
| 572 |
*/ |
| 573 |
public static function regionAzureTranslateInstance(): string |
| 574 |
{ |
| 575 |
return get_option(Constants::AATXT_OPTION_FIELD_REGION_AZURE_TRANSLATE_INSTANCE); |
| 576 |
} |
| 577 |
|
| 578 |
/** |
| 579 |
* @return string |
| 580 |
*/ |
| 581 |
public static function languageAzureTranslateInstance(): string |
| 582 |
{ |
| 583 |
return get_option(Constants::AATXT_OPTION_FIELD_LANGUAGE_AZURE_TRANSLATE_INSTANCE) ?: 'en'; |
| 584 |
} |
| 585 |
|
| 586 |
/** |
| 587 |
* @return bool |
| 588 |
*/ |
| 589 |
public static function preserveExistingAltText(): bool |
| 590 |
{ |
| 591 |
return get_option(Constants::AATXT_OPTION_FIELD_PRESERVE_EXISTING_ALT_TEXT); |
| 592 |
} |
| 593 |
|
| 594 |
public static function sanitizeUrl($input): string |
| 595 |
{ |
| 596 |
return sanitize_url($input); |
| 597 |
} |
| 598 |
|
| 599 |
public static function sanitizeText($input): string |
| 600 |
{ |
| 601 |
return sanitize_text_field($input); |
| 602 |
} |
| 603 |
|
| 604 |
public static function sanitizeTextArea($input): string |
| 605 |
{ |
| 606 |
return sanitize_textarea_field($input); |
| 607 |
} |
| 608 |
} |
| 609 |
|