version = $version;
}
/**
* Fetches and populates user account details from the remote API.
*
*/
private function load_account() {
$api_key = AltTextLab_Utility::get_api_key();
$this->account = array(
'plan' => '',
'isActive' => '',
'credits' => '',
'nextReceiving' => '',
'nextReceivingAt' => '',
);
if ( empty( $api_key ) ) {
return;
}
$api = new AltTextLab_API( $api_key );
$this->account = $api->get_account();
}
/**
* Returns the transient key used to review block.
*/
private function get_account_review_block_hidden_key( $user_id ) {
return 'alttextlab_account_review_block_hidden_' . absint( $user_id );
}
/**
* Returns the total number of generated alt texts in history.
*/
private function get_total_generated_assets_count() {
$storage = new AltTextLab_Storage();
return (int) $storage->get_count_total_assets();
}
/**
* Prepares the review block visibility state for the account page.
*/
private function load_review_block_state() {
$user_id = get_current_user_id();
$this->review_block_generated_count = $this->get_total_generated_assets_count();
$has_valid_api_key = ! empty( AltTextLab_Utility::get_api_key() ) && is_array( $this->account );
$has_positive_credits = $has_valid_api_key
&& isset( $this->account['credits'] )
&& (int) $this->account['credits'] > 0;
$is_hidden = (bool) get_transient( $this->get_account_review_block_hidden_key( $user_id ) );
$review_block_minimum_generated_count = 150;
$this->show_review_block = $this->review_block_generated_count >= $review_block_minimum_generated_count
&& $has_positive_credits
&& ! $is_hidden;
}
/**
* Incorporates CSS resources needed for the administration dashboard.
*
*/
public function enqueue_styles() {
wp_enqueue_style( 'alttextlab-admin', plugin_dir_url( __FILE__ ) . 'css/tailwind.min.css', array(), $this->version, 'all' );
}
/**
* Creates and configures the admin menu pages for plugin configuration.
*
*/
public function register_settings_pages() {
// Main page
add_menu_page(
__( 'AltTextLab WordPress Settings', 'alttextlab' ),
__( 'AltTextLab', 'alttextlab' ),
'manage_options',
'alttextlab-account-setting',
array( $this, 'render_account_settings_page' ),
plugin_dir_url(__FILE__) . 'img/logo_white_17x17.svg'
);
// Account Setting Page
$hook_suffix = add_submenu_page(
'alttextlab-account-setting',
__( 'Account Settings', 'alttextlab' ),
__( 'Account Settings', 'alttextlab' ),
'manage_options',
'alttextlab-account-setting'
);
add_action("admin_head-{$hook_suffix}", array($this, 'enqueue_styles') );
$hook_suffix = add_submenu_page(
'alttextlab-account-setting',
__( 'AltTextLab WordPress Settings', 'alttextlab' ),
__( 'Settings', 'alttextlab' ),
'manage_options',
'alttextlab',
array( $this, 'render_settings_page' )
);
add_action("admin_head-{$hook_suffix}", array($this, 'enqueue_styles') );
// Bulk Generate Page
$hook_suffix = add_submenu_page(
'alttextlab-account-setting',
__( 'Bulk Generation', 'alttextlab' ),
__( 'Bulk Generation', 'alttextlab' ),
'manage_options',
'alttextlab-bulk-generate',
array( $this, 'render_bulk_generate_page' )
);
add_action("admin_head-{$hook_suffix}", array($this, 'enqueue_styles') );
// Sync Alt in Content Page
$hook_suffix = add_submenu_page(
'alttextlab-account-setting',
__( 'Sync Alt Text in Content', 'alttextlab' ),
__( 'Sync Alt Text in Content', 'alttextlab' ),
'manage_options',
'alttextlab-sync-alt-in-posts',
array( $this, 'render_sync_alt_in_posts_page' )
);
add_action("admin_head-{$hook_suffix}", array($this, 'enqueue_styles') );
// History Page
if ( AltTextLab_Utility::get_api_key() ) {
$hook_suffix = add_submenu_page(
'alttextlab-account-setting',
__( 'History', 'alttextlab' ),
__( 'History', 'alttextlab' ),
'manage_options',
'alttextlab-history',
array( $this, 'render_history_page' )
);
add_action("admin_head-{$hook_suffix}", array($this, 'enqueue_styles') );
}
}
/**
* Establishes and defines the user account options.
*
*/
public function register_account_settings()
{
register_setting(
'alttextlab-account-settings',
'alttextlab_api_key',
array(
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
'default' => '',
)
);
}
/**
* Establishes and defines the plugin's configuration options.
*
*/
public function register_settings() {
register_setting(
'alttextlab-settings',
'alttextlab_lang',
array(
'type' => 'string',
'sanitize_callback' => array($this, 'sanitize_language_option'),
'default' => 'en',
)
);
register_setting(
'alttextlab-settings',
'alttextlab_model_name',
array(
'type' => 'string',
'sanitize_callback' => array($this, 'sanitize_model_name_option'),
'default' => null,
)
);
register_setting(
'alttextlab-settings',
'alttextlab_update_title',
array(
'type' => 'string',
'sanitize_callback' => array( $this, 'sanitize_yes_no_checkbox' ),
'default' => 'no',
)
);
register_setting(
'alttextlab-settings',
'alttextlab_update_caption',
array(
'type' => 'string',
'sanitize_callback' => array( $this, 'sanitize_yes_no_checkbox' ),
'default' => 'no',
)
);
register_setting(
'alttextlab-settings',
'alttextlab_update_description',
array(
'type' => 'string',
'sanitize_callback' => array( $this, 'sanitize_yes_no_checkbox' ),
'default' => 'no',
)
);
register_setting(
'alttextlab-settings',
'alttextlab_auto_generation_enabled',
array(
'type' => 'string',
'sanitize_callback' => array( $this, 'sanitize_yes_no_checkbox' ),
'default' => 'yes',
)
);
register_setting(
'alttextlab-settings',
'alttextlab_keywords',
array(
'type' => 'string',
'sanitize_callback' => array( $this, 'sanitize_yes_no_checkbox' ),
'default' => 'yes',
)
);
register_setting(
'alttextlab-settings',
'alttextlab_use_post_title_as_context',
array(
'type' => 'string',
'sanitize_callback' => array( $this, 'sanitize_yes_no_checkbox' ),
'default' => 'no',
)
);
register_setting(
'alttextlab-settings',
'alttextlab_ecomm',
array(
'type' => 'string',
'sanitize_callback' => array( $this, 'sanitize_yes_no_checkbox' ),
'default' => 'yes',
)
);
register_setting(
'alttextlab-settings',
'alttextlab_ecomm_use_title',
array(
'type' => 'string',
'sanitize_callback' => array( $this, 'sanitize_yes_no_checkbox' ),
'default' => 'no',
)
);
register_setting(
'alttextlab-settings',
'alttextlab_ecomm_use_color',
array(
'type' => 'string',
'sanitize_callback' => array( $this, 'sanitize_yes_no_checkbox' ),
'default' => 'no',
)
);
register_setting(
'alttextlab-settings',
'alttextlab_ecomm_use_material',
array(
'type' => 'string',
'sanitize_callback' => array( $this, 'sanitize_yes_no_checkbox' ),
'default' => 'no',
)
);
register_setting(
'alttextlab-settings',
'alttextlab_ecomm_color_field',
array(
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
'default' => '',
)
);
register_setting(
'alttextlab-settings',
'alttextlab_ecomm_material_field',
array(
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
'default' => '',
)
);
register_setting(
'alttextlab-settings',
'alttextlab_public',
array(
'type' => 'string',
'sanitize_callback' => array( $this, 'sanitize_yes_no_checkbox' ),
'default' => 'yes'
)
);
register_setting(
'alttextlab-settings',
'alttextlab_alt_prefix',
array(
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
'default' => '',
)
);
register_setting(
'alttextlab-settings',
'alttextlab_alt_suffix',
array(
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
'default' => '',
)
);
register_setting(
'alttextlab-settings',
'alttextlab_timeout',
array(
'type' => 'integer',
'sanitize_callback' => 'absint',
'default' => 20,
)
);
register_setting(
'alttextlab-settings',
'alttextlab_use_single_lang',
array(
'type' => 'string',
'sanitize_callback' => array( $this, 'sanitize_yes_no_checkbox' ),
'default' => 'no',
)
);
register_setting(
'alttextlab-settings',
'alttextlab_wpml_selected_langs',
array(
'type' => 'array',
'sanitize_callback' => array( $this, 'sanitize_selected_wpml_languages' ),
)
);
}
/**
* Validates and normalizes checkbox values to guarantee they are either 'yes' or 'no'.
*
*/
public function sanitize_yes_no_checkbox( $input ) {
return $input === 'yes' ? 'yes' : 'no';
}
/**
* Sanitizes language selection input
*
*/
public function sanitize_language_option( $input ){
$available_languages = AltTextLab_Utility::supported_languages();
return array_key_exists($input, $available_languages) ? $input : 'en';
}
/**
* Sanitizes model names selection input
*
*/
public function sanitize_model_name_option( $input ){
$available_model_names = AltTextLab_Utility::supported_model_names();
return in_array($input, $available_model_names, true) ? $input : null;
}
/**
* Displays the interface for account settings.
*
*/
public function render_account_settings_page() {
$this->load_account();
$this->load_review_block_state();
require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/partials/account-settings.php';
}
/**
* Displays the interface for batch alt text generation.
*
*/
public function render_bulk_generate_page() {
$this->load_account();
require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/partials/bulk-generate.php';
}
/**
* Displays the interface for batch alt text sync.
*
*/
public function render_sync_alt_in_posts_page() {
$this->load_account();
require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/partials/sync-alt-in-posts.php';
}
/**
* Presents the log of past alt text generation activities.
*
*/
public function render_history_page() {
$this->load_account();
require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/partials/history.php';
}
/**
* Generates the configuration interface for plugin options.
*
*/
public function render_settings_page() {
$this->load_account();
require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/partials/settings.php';
}
/**
* Manages the storage or removal of API authentication credentials.
*
*/
public function save_api_key( $api_key, $old_api_key ) {
$delete = is_null( $api_key );
if ( $delete ) {
delete_option( 'alttextlab_api_key' );
}
if ( empty( $api_key ) ) {
return $api_key;
}
if ( $api_key === '*********' ) {
return $old_api_key;
}
$api = new AltTextLab_API( $api_key );
if ( ! $api->get_account() ) {
$invalid_key_message = wp_kses(
__( 'The API key you entered is not valid.
Please check that it was copied correctly or create a new API key in your AltTextLab dashboard.', 'alttextlab' ),
array( 'br' => array() )
);
add_settings_error( 'invalid-api-key', '', $invalid_key_message );
return $old_api_key;
}
// Add custom success message
/* translators: %s: URL for the Bulk Generate feature. */
$message = __( 'API Key saved. Pro tip: Add alt text to all your existing images with our Bulk Generate feature!', 'alttextlab' );
$message = sprintf( $message, admin_url( 'admin.php?page=alttextlab-bulk-generate' ) );
$message = wp_kses(
$message,
array(
'a' => array(
'href' => array(),
'class' => array(),
),
)
);
add_settings_error( 'alttextlab_api_key_updated', '', $message, 'updated' );
return $api_key;
}
/**
* Purges stored error records when the page is initialized
*
*/
public function clear_error_logs() {
if ( ! isset( $_GET['alttextlab_action'] ) ) {
return;
}
if ( $_GET['alttextlab_action'] !== 'clear-error-logs' ) {
return;
}
delete_option( 'alttextlab_error_logs' );
wp_safe_redirect( add_query_arg( 'alttextlab_action', false ) );
}
/**
* Removes the temporary warning about insufficient credits to prevent it from showing again.
*
*/
public function expire_insufficient_credits_notice() {
check_ajax_referer( 'alttextlab_insufficient_credits_notice', 'security' );
delete_transient( 'alttextlab_insufficient_credits' );
wp_send_json( array(
'status' => 'success',
'message' => __( 'Notice expired.', 'alttextlab' ),
) );
}
/**
* Removes the temporary warning about the monthly token limit to prevent it from showing again.
*
*/
public function expire_monthly_limit_notice() {
check_ajax_referer( 'alttextlab_monthly_limit_notice', 'security' );
delete_transient( 'alttextlab_monthly_limit_exceeded' );
wp_send_json( array(
'status' => 'success',
'message' => __( 'Notice expired.', 'alttextlab' ),
) );
}
/**
* Cleans up the browser address by eliminating the API key warning parameter.
*
*/
public function remove_api_key_missing_param() {
if ( ! isset( $_GET['api_key_missing'] ) ) {
return;
}
$api_key = AltTextLab_Utility::get_api_key();
if ( empty( $api_key ) ) {
return;
}
$current_url = ( is_ssl() ? 'https://' : 'http://' ) .
( isset($_SERVER['HTTP_HOST']) ? sanitize_text_field($_SERVER['HTTP_HOST']) : '') .
( isset($_SERVER['REQUEST_URI']) ? sanitize_url($_SERVER['REQUEST_URI']) : '');
$updated_url = remove_query_arg( 'api_key_missing', $current_url );
if ( $current_url !== $updated_url ) {
wp_safe_redirect( $updated_url );
exit;
}
}
/**
* Handles user preferences for the review prompt banner
*
*/
function handleReviewBannerSettings()
{
check_ajax_referer('alttextlab_security_review_settings', 'security');
$setting = sanitize_text_field($_POST['setting'] ? : '');
$user_id = get_current_user_id();
if ($setting === 'later') {
set_transient('alttextlab_review_hidden_' . $user_id, true, 7 * DAY_IN_SECONDS);
} elseif ($setting === 'never') {
set_transient('alttextlab_review_hidden_' . $user_id, true, 0);
}
}
/**
* Handles user preferences for the account review block.
*/
public function handleAccountReviewBlockSettings() {
check_ajax_referer( 'alttextlab_security_account_review_block_settings', 'security' );
$setting = sanitize_text_field( $_POST['setting'] ? : '' );
$user_id = get_current_user_id();
$account_review_key = $this->get_account_review_block_hidden_key( $user_id );
$allowed_settings = array(
'alttextlab-review-block-maybe-later-button',
'alttextlab-review-block-dont-show-again-button',
'alttextlab-review-block-skip-button',
'account_not_really',
);
if ( ! in_array( $setting, $allowed_settings, true ) ) {
wp_send_json( array(
'status' => 'error',
'message' => __( 'Unknown account review setting.', 'alttextlab' ),
) );
}
if ( $setting === 'alttextlab-review-block-maybe-later-button' ) {
set_transient( $account_review_key, true, 14 * DAY_IN_SECONDS );
} elseif ( $setting === 'alttextlab-review-block-skip-button' ) {
set_transient( $account_review_key, true, 45 * DAY_IN_SECONDS );
} elseif ( $setting === 'account_not_really' || $setting === 'alttextlab-review-block-dont-show-again-button' ) {
set_transient( $account_review_key, true, 0 );
}
wp_send_json( array(
'status' => 'success',
'message' => __( 'Account review block preference saved.', 'alttextlab' ),
) );
}
/**
* Sanitizes selected WPML languages.
*/
public function sanitize_selected_wpml_languages( $input ) {
if ( $input === null || $input === '' || ! is_array( $input ) ) {
return array();
}
$active_languages = apply_filters( 'wpml_active_languages', null );
$valid_codes = is_array( $active_languages ) ? array_keys( $active_languages ) : array();
$sanitized_codes = array_map( 'sanitize_text_field', $input );
$sanitized_codes = array_map( 'trim', $sanitized_codes );
$sanitized_codes = array_filter( $sanitized_codes, 'strlen' );
$sanitized_codes = array_values( array_unique( $sanitized_codes ) );
if ( empty( $valid_codes ) ) {
return $sanitized_codes;
}
return array_values( array_intersect( $sanitized_codes, $valid_codes ) );
}
/**
* Check whether this is plugin settings form submission.
*/
private function is_settings_form_request() {
return isset( $_POST['option_page'], $_POST['action'] ) &&
$_POST['option_page'] === 'alttextlab-settings' &&
$_POST['action'] === 'update';
}
/**
* Preserve selected WPML languages if the selector block was not rendered.
*/
public function keep_selected_wpml_languages( $value, $old_value, $option ) {
if ( ! $this->is_settings_form_request() ) {
return $value;
}
if ( empty( $_POST['alttextlab_wpml_languages_block_rendered'] ) ) {
return $old_value;
}
if ( ! isset( $_POST['alttextlab_wpml_selected_langs'] ) ) {
return array();
}
return $value;
}
}