*/ class All_Sources_Images_Admin { /** * The ID of this plugin. * * @since 4.0.0 * @access private * @var string $plugin_name The ID of this plugin. */ private $plugin_name; /** * The version of this plugin. * * @since 4.0.0 * @access private * @var string $version The current version of this plugin. */ private $version; /** * Hook suffix for the Media submenu page. * * @var string */ private $media_picker_hook = ''; /** * Hook suffix for the top-level media browser page. * * @var string */ private $media_picker_top_level_hook = ''; /** * Hook suffix for the Search submenu under the plugin menu. * * @var string */ private $media_picker_search_submenu_hook = ''; /** * Default post ID for media picker. * * @since 6.1.8 * @var int */ private $media_picker_default_post_id = 0; /** * Initialize the class and set its properties. * * @since 4.0.0 * @param string $plugin_name The name of this plugin. * @param string $version The version of this plugin. */ public function __construct( $plugin_name, $version ) { $this->plugin_name = $plugin_name; $this->version = $version; $ALLSI_generation = $this->ALLSI_generation_features(); // Cron settings for scheduled generation $cron_options = wp_parse_args( get_option( 'ALLSI_plugin_cron_settings' ) ); // Register cron hook for automated image generation add_action( 'ALLSI_cron_image_generation', array(&$this, 'ALLSI_execute_cron_generation') ); // Register hook for scheduled image generation (used by plugin integrations) add_action( 'ALLSI_generate_scheduled_image', array(&$this, 'ALLSI_generate_scheduled_image') ); // Robust publish hooks for automatic image generation on publish. add_action( 'transition_post_status', array( $this, 'ALLSI_auto_image_transition_post_status' ), 20, 3 ); add_action( 'wp_after_insert_post', array( $this, 'ALLSI_auto_image_after_insert_post' ), 20, 4 ); // Testing APIs function with Ajax call add_action( 'wp_ajax_allsi_test_apis', array(&$this, 'ALLSI_test_apis') ); /* Gutenberg Block */ // Scripts for Block & Translations add_action( 'init', array(&$this, 'ALLSI_register_mpt_block') ); add_action( 'enqueue_block_editor_assets', array(&$this, 'ALLSI_enqueue_style_block') ); // Register media picker page only once (avoid duplicates from multiple class instances) static $media_picker_registered = false; if ( ! $media_picker_registered ) { add_action( 'admin_menu', array( $this, 'ALLSI_register_media_picker_page' ) ); $media_picker_registered = true; } add_action( 'admin_enqueue_scripts', array( $this, 'ALLSI_enqueue_media_picker_assets' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'ALLSI_enqueue_media_modal_assets' ) ); add_action( 'enqueue_block_editor_assets', array( $this, 'ALLSI_enqueue_media_modal_assets' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'ALLSI_enqueue_front_media_modal_assets' ) ); // Gutenberg Block : Translate search term (called once before searching multiple banks) add_action( 'wp_ajax_allsi_translate_search', array(&$this, 'ALLSI_translate_search_ajax') ); // Gutenberg Block : Searching images with APIs add_action( 'wp_ajax_allsi_block_searching_images', array(&$this, 'ALLSI_block_searching_images') ); // Gutenberg Block : Download images from APIs add_action( 'wp_ajax_allsi_block_downloading_image', array(&$this, 'ALLSI_block_downloading_image') ); // Log viewer actions (Others > Logging) add_action( 'wp_ajax_allsi_get_log_contents', array( $this, 'ALLSI_get_log_contents_ajax' ) ); add_action( 'wp_ajax_allsi_clear_log', array( $this, 'ALLSI_clear_log_ajax' ) ); // Extend timeout request for wp_remote_request() with dalle $options_banks = wp_parse_args( get_option( 'ALLSI_plugin_banks_settings' ), $this->ALLSI_default_options_banks_settings( TRUE ) ); if ( isset( $options_banks['api_chosen_auto'] ) && true === in_array( 'dallev1', $options_banks['api_chosen_auto'] ) ) { add_filter( 'http_request_timeout', array(&$this, 'ALLSI_custom_http_request_timeout') ); } // Migration v5 to v6 add_action( 'init', array(&$this, 'ALLSI_migration') ); add_action( 'plugins_loaded', array( $this, 'ALLSI_maybe_boot_elementor' ), 20 ); if ( did_action( 'elementor/loaded' ) ) { $this->ALLSI_maybe_boot_elementor(); } else { add_action( 'elementor/init', array( $this, 'ALLSI_maybe_boot_elementor' ) ); } } /** * Trigger the automatic image generation process upon saving a post * * @since 5.0.0 * @access public */ public function ALLSI_trigger_save_post() { $log = $this->ALLSI_monolog_call(); $log->info( 'Launch Automatic plugin' ); //$automatic_generation = new All_Sources_Images_Generation($this->plugin_name, $this->version); // Add a callback function to the save_post action to handle image generation add_action( 'save_post', function ( $post_id ) { // Skip if this is an autosave if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } // Skip if this is a revision if ( wp_is_post_revision( $post_id ) ) { return; } // Check if WPeMatico campaign_fetch class is active (it's instantiated during feed processing) if ( class_exists( 'wpematico_campaign_fetch' ) ) { return; } // Check if we're in a WPeMatico cron/ajax context if ( defined( 'WPEMATICO_VERSION' ) && ( doing_action( 'wpematico_cron' ) || did_action( 'wpematico_init_fetching' ) || did_action( 'Wpematico_init_fetching' ) ) ) { return; } // Skip if triggered by FeedWordPress if ( class_exists( 'FeedWordPress' ) && doing_action( 'syndicated_item' ) ) { return; } // Check if we already scheduled generation for this post (prevents duplicates) $transient_key = 'ALLSI_scheduled_' . $post_id; if ( get_transient( $transient_key ) ) { return; } set_transient( $transient_key, true, 120 ); // Schedule a single event with a delay to avoid conflicts wp_schedule_single_event( time() + 5, // 5 seconds apart for each block 'ALLSI_generate_scheduled_image', array($post_id) ); } ); } /** * Generate images for all blocks when called by the scheduled event * Uses post meta flags per image block to prevent duplicates * * @since 6.0.0 * @access public */ public function ALLSI_generate_scheduled_image( $post_id ) { $post_id = absint( $post_id ); $log = $this->ALLSI_monolog_call(); $log->info( '=== SCHEDULED GENERATION START for post ID: ' . $post_id . ' ===' ); if ( 0 === $post_id ) { $log->info( 'Invalid post ID, exiting scheduled generation' ); return; } // Always clear queue lock for this post when the scheduled task starts. delete_transient( 'ALLSI_auto_image_queued_' . $post_id ); if ( ! $this->ALLSI_auto_image_should_process_post( $post_id ) ) { $log->info( 'Post does not match Auto Image filters, exiting' ); return; } // Retrieve main settings from the plugin options $main_settings = get_option( 'ALLSI_plugin_main_settings' ); $img_blocks = isset( $main_settings['image_block'] ) ? $main_settings['image_block'] : array(); if ( empty( $img_blocks ) ) { $log->info( 'No image blocks configured, exiting' ); return; } // Check rewrite_featured setting $rewrite_featured = isset( $main_settings['rewrite_featured'] ) && $main_settings['rewrite_featured'] === 'true'; $log->info( 'Rewrite featured setting: ' . ( $rewrite_featured ? 'enabled' : 'disabled' ) ); $automatic_generation = new All_Sources_Images_Generation($this->plugin_name, $this->version); // Iterate through each image block foreach ( $img_blocks as $key_img_block => $img_block ) { $meta_key = '_ALLSI_block_' . $key_img_block . '_status'; $current_status = get_post_meta( $post_id, $meta_key, true ); $log->info( 'Block ' . $key_img_block . ' status: ' . ( $current_status ?: 'empty' ) ); // Skip if already completed or in progress if ( $current_status === 'completed' ) { $log->info( 'Block ' . $key_img_block . ' already completed, skipping' ); continue; } if ( $current_status === 'processing' ) { // Check if processing started more than 2 minutes ago (stuck process) $started_at = get_post_meta( $post_id, '_ALLSI_block_' . $key_img_block . '_started', true ); if ( $started_at && ( time() - intval( $started_at ) ) < 120 ) { $log->info( 'Block ' . $key_img_block . ' is being processed by another instance, skipping' ); continue; } $log->info( 'Block ' . $key_img_block . ' was stuck, retrying' ); } // Mark as processing with timestamp update_post_meta( $post_id, $meta_key, 'processing' ); update_post_meta( $post_id, '_ALLSI_block_' . $key_img_block . '_started', time() ); $log->info( 'Starting generation for block ' . $key_img_block ); try { // Generate the image for this specific block $automatic_generation->ALLSI_create_thumb( $post_id, 0, // $check_value_enable 1, // $check_post_type 1, // $check_category $rewrite_featured, // $rewrite_featured - pass the setting! false, null, null, $key_img_block, true ); // Mark as completed update_post_meta( $post_id, $meta_key, 'completed' ); delete_post_meta( $post_id, '_ALLSI_block_' . $key_img_block . '_started' ); $log->info( 'Block ' . $key_img_block . ' completed successfully' ); } catch ( Exception $e ) { // Mark as failed so it can be retried update_post_meta( $post_id, $meta_key, 'failed' ); $log->error( 'Block ' . $key_img_block . ' failed: ' . $e->getMessage() ); } } $log->info( '=== SCHEDULED GENERATION END for post ID: ' . $post_id . ' ===' ); } /** * Schedule auto image generation when a post transitions to publish. * * @param string $new_status New post status. * @param string $old_status Old post status. * @param WP_Post $post Post object. */ public function ALLSI_auto_image_transition_post_status( $new_status, $old_status, $post ) { if ( 'publish' !== $new_status || 'publish' === $old_status ) { return; } if ( ! ( $post instanceof WP_Post ) ) { return; } $this->ALLSI_auto_image_schedule_post( $post->ID, 'transition_post_status' ); } /** * Fallback publish trigger for importers and integrations that bypass the classic editor flow. * * @param int $post_id Post ID. * @param WP_Post $post Post object. * @param bool $update Whether this is an existing post being updated. * @param WP_Post|null $post_before Previous post object. */ public function ALLSI_auto_image_after_insert_post( $post_id, $post, $update, $post_before ) { if ( ! ( $post instanceof WP_Post ) ) { return; } if ( 'publish' !== $post->post_status ) { return; } // Skip normal content updates that were already published before. if ( true === (bool) $update && ( $post_before instanceof WP_Post ) && 'publish' === $post_before->post_status ) { return; } $this->ALLSI_auto_image_schedule_post( $post_id, 'wp_after_insert_post' ); } /** * Queue a single delayed event for image generation. * * @param int $post_id Post ID. * @param string $reason Hook name used for logging. */ private function ALLSI_auto_image_schedule_post( $post_id, $reason = '' ) { $post_id = absint( $post_id ); if ( 0 === $post_id ) { return; } if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) { return; } if ( ! $this->ALLSI_auto_image_should_process_post( $post_id ) ) { return; } $transient_key = 'ALLSI_auto_image_queued_' . $post_id; if ( get_transient( $transient_key ) ) { return; } if ( wp_next_scheduled( 'ALLSI_generate_scheduled_image', array( $post_id ) ) ) { return; } set_transient( $transient_key, true, 120 ); wp_schedule_single_event( time() + 5, 'ALLSI_generate_scheduled_image', array( $post_id ) ); $log = $this->ALLSI_monolog_call(); $log->info( 'Auto image scheduled', array( 'post_id' => $post_id, 'reason' => sanitize_text_field( (string) $reason ), ) ); } /** * Validate if a post matches Auto Image publish filters. * * @param int|WP_Post $post Post object or post ID. * @return bool */ private function ALLSI_auto_image_should_process_post( $post ) { if ( ! ( $post instanceof WP_Post ) ) { $post = get_post( $post ); } if ( ! ( $post instanceof WP_Post ) ) { return false; } if ( wp_is_post_revision( $post->ID ) || wp_is_post_autosave( $post->ID ) ) { return false; } if ( 'publish' !== $post->post_status ) { return false; } $options = wp_parse_args( get_option( 'ALLSI_plugin_auto_image_settings' ), $this->ALLSI_default_options_auto_image_settings( true ) ); if ( empty( $options['enabled'] ) || 'enable' !== $options['enabled'] ) { return false; } $allowed_post_types = array(); if ( ! empty( $options['post_types'] ) && is_array( $options['post_types'] ) ) { $allowed_post_types = array_values( array_unique( array_filter( array_map( 'sanitize_key', $options['post_types'] ) ) ) ); } if ( ! empty( $allowed_post_types ) && ! in_array( $post->post_type, $allowed_post_types, true ) ) { return false; } $excluded_ids = $this->ALLSI_auto_image_parse_ids( isset( $options['exclude_post_ids'] ) ? $options['exclude_post_ids'] : '' ); if ( in_array( $post->ID, $excluded_ids, true ) ) { return false; } $selected_term_ids = array(); if ( ! empty( $options['term_ids'] ) && is_array( $options['term_ids'] ) ) { $selected_term_ids = array_values( array_unique( array_filter( array_map( 'absint', $options['term_ids'] ) ) ) ); } if ( ! empty( $selected_term_ids ) ) { $taxonomies = get_object_taxonomies( $post->post_type, 'names' ); if ( empty( $taxonomies ) ) { return false; } $post_term_ids = wp_get_post_terms( $post->ID, $taxonomies, array( 'fields' => 'ids' ) ); if ( is_wp_error( $post_term_ids ) || empty( $post_term_ids ) ) { return false; } $post_term_ids = array_map( 'absint', $post_term_ids ); if ( empty( array_intersect( $selected_term_ids, $post_term_ids ) ) ) { return false; } } return true; } /** * Parse a comma-separated list of IDs and return unique integers. * * @param string $ids_raw Raw CSV string. * @return array */ private function ALLSI_auto_image_parse_ids( $ids_raw ) { if ( ! is_string( $ids_raw ) || '' === trim( $ids_raw ) ) { return array(); } preg_match_all( '/\d+/', $ids_raw, $matches ); if ( empty( $matches[0] ) ) { return array(); } return array_values( array_unique( array_filter( array_map( 'absint', $matches[0] ) ) ) ); } /** * Clear ASI block status meta for a post (useful for re-generation) * * @param int $post_id The post ID. */ public function clear_block_status( $post_id ) { $post_id = absint( $post_id ); if ( 0 === $post_id ) { return; } $all_meta = get_post_meta( $post_id ); if ( empty( $all_meta ) || ! is_array( $all_meta ) ) { return; } foreach ( array_keys( $all_meta ) as $meta_key ) { if ( 0 === strpos( (string) $meta_key, '_ALLSI_block_' ) ) { delete_post_meta( $post_id, $meta_key ); } } } /** * Execute cron-based automated image generation * Called by WordPress cron hook 'ALLSI_cron_image_generation' * * @since 6.1.7 * @access public */ public function ALLSI_execute_cron_generation() { $log = $this->ALLSI_monolog_call(); $log->info( 'Starting cron image generation' ); // Get cron settings $cron_options = get_option( 'ALLSI_plugin_cron_settings' ); // Verify cron is enabled if ( !isset( $cron_options['enable_cron'] ) || $cron_options['enable_cron'] !== 'enable' ) { $log->info( 'Cron is disabled, exiting' ); return; } // Get post types to process $post_types = isset( $cron_options['cron_post_types'] ) ? $cron_options['cron_post_types'] : array( 'post' ); // Get posts per run $posts_per_run = isset( $cron_options['posts_per_run'] ) ? absint( $cron_options['posts_per_run'] ) : 5; // Build date query $date_query = array(); if ( isset( $cron_options['posts_date_mode'] ) && $cron_options['posts_date_mode'] === 'relative' ) { $date_value = isset( $cron_options['posts_date_value'] ) ? absint( $cron_options['posts_date_value'] ) : 5; $date_unit = isset( $cron_options['posts_date_unit'] ) ? $cron_options['posts_date_unit'] : 'days'; $date_query = array( array( 'after' => $date_value . ' ' . $date_unit . ' ago', 'inclusive' => true, ), ); } // Query posts without featured images $args = array( 'post_type' => $post_types, 'posts_per_page' => $posts_per_run, 'post_status' => 'publish', 'meta_query' => array( array( 'key' => '_thumbnail_id', 'compare' => 'NOT EXISTS', ), ), 'orderby' => 'date', 'order' => 'DESC', ); if ( !empty( $date_query ) ) { $args['date_query'] = $date_query; } $query = new WP_Query( $args ); $log->info( 'Found ' . $query->found_posts . ' posts without featured images' ); if ( $query->have_posts() ) { $automatic_generation = new All_Sources_Images_Generation( $this->plugin_name, $this->version ); $main_settings = get_option( 'ALLSI_plugin_main_settings' ); $img_blocks = isset( $main_settings['image_block'] ) ? $main_settings['image_block'] : array(); while ( $query->have_posts() ) { $query->the_post(); $post_id = get_the_ID(); $log->info( 'Processing post ID: ' . $post_id ); // Process each image block for this post foreach ( $img_blocks as $key_img_block => $img_block ) { $automatic_generation->ALLSI_create_thumb( $post_id, 0, 1, 1, 0, false, null, null, $key_img_block, true ); } } wp_reset_postdata(); } $log->info( 'Cron image generation completed' ); } /** * Trigger when hook wp_insert_post is fired * * @since 5.0.3 * @access public */ public function ALLSI_trigger_wp_insert_post( $post_ID ) { } public function ALLSI_trigger_wp_automatic( $post_data ) { $log = $this->ALLSI_monolog_call(); $log->info( 'Launch WordPress Automatic Plugin' ); $automatic_generation = new All_Sources_Images_Generation($this->plugin_name, $this->version); $main_settings = get_option( 'ALLSI_plugin_main_settings' ); $img_blocks = $main_settings['image_block']; foreach ( $img_blocks as $key_img_block => $img_block ) { $automatic_generation->ALLSI_create_thumb( $post_data['post_id'], '0', '1', '1', '0', false, null, null, $key_img_block, false ); } } /** * Register all of the hooks related to the admin area functionality * of the plugin. * * @since 4.0.0 * @access private */ private function ALLSI_generation_features() { $ALLSI_generation = new All_Sources_Images_Generation($this->plugin_name, $this->version); return $ALLSI_generation; } /** * Get proxy configuration for HTTP requests * Returns proxy args to merge with wp_remote_get/wp_remote_post args * * @since 6.1.7 * @return array Proxy configuration array */ // Cloudflare Worker constants (hardcoded for automatic fallback) const ALLSI_CLOUDFLARE_WORKER_URL = 'https://noisy-heart-6060.estebandezafra.workers.dev'; const ALLSI_CLOUDFLARE_WORKER_TOKEN = 'TTSHFHRwllzM1RDctruMT0daz4sYmTKAj1V3y8VFlL6mVwdt4oYWWolwYrvLz1PS'; // Sources that support automatic Cloudflare fallback when no API key is configured const ALLSI_CLOUDFLARE_FALLBACK_SOURCES = array( 'pixabay', 'pexels', 'unsplash', 'flickr', 'giphy' ); public function ALLSI_get_proxy_args() { $proxy_options = $this->ALLSI_get_proxy_settings(); // Only return proxy args if legacy proxy is enabled and configured if ( empty( $proxy_options['enable_proxy'] ) || 'enable' !== $proxy_options['enable_proxy'] ) { return array(); } if ( empty( $proxy_options['proxy_address'] ) ) { return array(); } $proxy_host = preg_replace( '#^https?://#i', '', trim( $proxy_options['proxy_address'] ) ); $proxy_port = ! empty( $proxy_options['proxy_port'] ) ? $proxy_options['proxy_port'] : '80'; $proxy_url = $proxy_host . ':' . $proxy_port; if ( ! empty( $proxy_options['proxy_username'] ) && ! empty( $proxy_options['proxy_password'] ) ) { $proxy_url = $proxy_options['proxy_username'] . ':' . $proxy_options['proxy_password'] . '@' . $proxy_url; } return array( 'proxy' => 'https://' . $proxy_url, ); } private function ALLSI_get_proxy_settings() { static $cache = null; if ( null === $cache ) { $cache = wp_parse_args( get_option( 'ALLSI_plugin_proxy_settings' ), $this->ALLSI_default_options_proxy_settings( false ) ); } return $cache; } /** * Check if a source supports Cloudflare fallback */ public function ALLSI_source_supports_cloudflare_fallback( $service ) { return in_array( $service, self::ALLSI_CLOUDFLARE_FALLBACK_SOURCES, true ); } /** * Check if legacy proxy is enabled by user */ public function ALLSI_is_legacy_proxy_enabled() { $options = $this->ALLSI_get_proxy_settings(); return ! empty( $options['enable_proxy'] ) && 'enable' === $options['enable_proxy'] && ! empty( $options['proxy_address'] ); } public function ALLSI_current_proxy_mode() { // For backward compatibility - now only returns 'legacy' or 'disabled' $options = $this->ALLSI_get_proxy_settings(); if ( ! empty( $options['enable_proxy'] ) && 'enable' === $options['enable_proxy'] ) { return 'legacy'; } return 'disabled'; } /** * Get Cloudflare Worker bootstrap configuration (uses hardcoded constants) */ private function ALLSI_get_cloudflare_bootstrap() { return array( 'worker_url' => self::ALLSI_CLOUDFLARE_WORKER_URL, 'token' => self::ALLSI_CLOUDFLARE_WORKER_TOKEN, ); } private function ALLSI_get_cloudflare_service_map() { return array( 'pixabay' => array( 'key_location' => 'query', 'key_name' => 'key' ), 'pexels' => array( 'key_location' => 'header', 'key_name' => 'Authorization' ), 'unsplash' => array( 'key_location' => 'header', 'key_name' => 'Authorization' ), 'giphy' => array( 'key_location' => 'query', 'key_name' => 'api_key' ), 'flickr' => array( 'key_location' => 'query', 'key_name' => 'api_key' ), 'openverse' => array( 'key_location' => 'none' ), 'cc_search' => array( 'key_location' => 'none' ), 'youtube' => array( 'key_location' => 'query', 'key_name' => 'key' ), 'google_image' => array( 'key_location' => 'query', 'key_name' => 'key' ), 'pixabay_video' => array( 'key_location' => 'query', 'key_name' => 'key' ), 'dallev1' => array( 'key_location' => 'header', 'key_name' => 'Authorization' ), 'stability' => array( 'key_location' => 'header', 'key_name' => 'Authorization' ), 'replicate' => array( 'key_location' => 'header', 'key_name' => 'Authorization' ), 'gemini' => array( 'key_location' => 'query', 'key_name' => 'key' ), 'workers_ai' => array( 'key_location' => 'header', 'key_name' => 'Authorization' ), 'google_translate'=> array( 'key_location' => 'query', 'key_name' => 'key' ), 'cloudflare_ai' => array( 'key_location' => 'header', 'key_name' => 'Authorization' ), ); } private function ALLSI_strip_credentials_from_request( $service, &$url, array &$args ) { $map = $this->ALLSI_get_cloudflare_service_map(); if ( empty( $map[ $service ] ) ) { return; } $config = $map[ $service ]; if ( 'header' === $config['key_location'] && ! empty( $config['key_name'] ) ) { if ( isset( $args['headers'][ $config['key_name'] ] ) ) { unset( $args['headers'][ $config['key_name'] ] ); } } if ( 'query' === $config['key_location'] && ! empty( $config['key_name'] ) ) { $url = remove_query_arg( $config['key_name'], $url ); } } private function ALLSI_merge_http_args( array $base, array $extra ) { if ( empty( $extra ) ) { return $base; } if ( isset( $extra['headers'] ) ) { if ( isset( $base['headers'] ) && is_array( $base['headers'] ) ) { $base['headers'] = array_merge( $base['headers'], $extra['headers'] ); } else { $base['headers'] = $extra['headers']; } unset( $extra['headers'] ); } return array_merge( $base, $extra ); } private function ALLSI_prepare_http_args( array $args ) { if ( empty( $args['method'] ) ) { $args['method'] = 'GET'; } $args['method'] = strtoupper( $args['method'] ); return $args; } private function ALLSI_remote_request_via_cloudflare( $service, $url, array $args ) { $bootstrap = $this->ALLSI_get_cloudflare_bootstrap(); $worker_url = untrailingslashit( $bootstrap['worker_url'] ); // Use URL-safe base64 encoding: replace + with -, / with _, remove = $encoded_url_b64 = rtrim( strtr( base64_encode( $url ), '+/', '-_' ), '=' ); $final_url = add_query_arg( array( 'servicio' => $service, 'token' => $bootstrap['token'], 'url_b64' => $encoded_url_b64, ), $worker_url ); unset( $args['proxy'] ); return wp_remote_request( $final_url, $args ); } /** * Main remote request function * * Logic: * 1. If source supports Cloudflare fallback AND use_cloudflare_fallback is true -> use Cloudflare * 2. If legacy proxy is enabled -> use legacy proxy * 3. Otherwise -> direct request */ public function ALLSI_remote_request( $service, $url, array $args = array(), $use_cloudflare_fallback = false ) { $args = $this->ALLSI_prepare_http_args( $args ); // Check if we should use Cloudflare fallback (source has no API key configured) if ( $use_cloudflare_fallback && $this->ALLSI_source_supports_cloudflare_fallback( $service ) ) { return $this->ALLSI_remote_request_via_cloudflare( $service, $url, $args ); } // Apply legacy proxy if enabled $proxy_args = $this->ALLSI_get_proxy_args(); $args = $this->ALLSI_merge_http_args( $args, $proxy_args ); return wp_remote_request( $url, $args ); } public function ALLSI_remote_get( $service, $url, array $args = array(), $use_cloudflare_fallback = false ) { $args['method'] = 'GET'; return $this->ALLSI_remote_request( $service, $url, $args, $use_cloudflare_fallback ); } public function ALLSI_remote_post( $service, $url, array $args = array(), $use_cloudflare_fallback = false ) { $args['method'] = 'POST'; return $this->ALLSI_remote_request( $service, $url, $args, $use_cloudflare_fallback ); } /** * Register the stylesheets for the admin area. * * @since 4.0.0 */ public function enqueue_styles( $hook ) { // Post Editor if ( $hook == 'post.php' || $hook == 'post-new.php' ) { wp_enqueue_style( 'mpt-post', plugin_dir_url( __FILE__ ) . 'css/all-sources-images-post.css', array(), $this->version, 'all' ); } // ASI Admin Pages (new UI) $ALLSI_pages = array( 'toplevel_page_allsi-new-settings', 'all-sources-images_page_allsi-new-settings', 'all-sources-images_page_allsi-new-automatic', 'all-sources-images_page_allsi-new-bulk-generation' ); if ( in_array( $hook, $ALLSI_pages ) ) { wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/all-sources-images-admin.css', array(), $this->version, 'all' ); } } /** * Register the JavaScript for the admin area. * * @since 4.0.0 */ public function enqueue_scripts( $hook ) { global $pagenow; $post_types_default = $this->ALLSI_default_posts_types(); $block = wp_parse_args( get_option( 'ALLSI_plugin_block_settings' ), $this->ALLSI_default_options_block_settings( TRUE ) ); $options_banks = wp_parse_args( get_option( 'ALLSI_plugin_banks_settings' ), $this->ALLSI_default_options_banks_settings( TRUE ) ); $options_auto = wp_parse_args( get_option( 'ALLSI_plugin_main_settings' ), $this->ALLSI_default_options_main_settings( TRUE ) ); // Scripts for new UI pages $ALLSI_new_pages = array( 'toplevel_page_allsi-new-settings', 'all-sources-images_page_allsi-new-settings', 'all-sources-images_page_allsi-new-automatic', 'all-sources-images_page_allsi-new-bulk-generation' ); if ( in_array( $hook, $ALLSI_new_pages ) ) { // Use WordPress Core jQuery UI wp_enqueue_script( 'jquery-ui-core' ); wp_enqueue_script( 'jquery-ui-tabs' ); wp_enqueue_script( 'jquery-ui-sortable' ); wp_enqueue_script( 'jquery-ui-draggable' ); wp_enqueue_script( 'jquery-ui-droppable' ); } wp_enqueue_script( 'mpt-rating', plugin_dir_url( __FILE__ ) . 'js/rating-admin.js', array('jquery'), $this->version, false ); // Old bulk generation scripts removed - now using bulk-generation.js via new-ui-assets.php // Source/Settings scripts (new page) if ( in_array( $hook, array( 'toplevel_page_allsi-new-settings', 'all-sources-images_page_allsi-new-settings' ), true ) ) { wp_enqueue_script( 'allsi-source', plugins_url( 'js/source.js', __FILE__ ), array('jquery', 'jquery-ui-core') ); wp_localize_script( 'allsi-source', 'allsiApisTestingAjax', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'api_testing_nonce' ), 'successful_testing' => esc_html__( 'Your API key works!', 'all-sources-images' ), 'error_key' => esc_html__( "Your API key doesn't work.", 'all-sources-images' ), 'error_testing' => esc_html__( 'An error has occurred with the remote API.', 'all-sources-images' ), ) ); } // JavaScript Variables for nonce, admin-jax.php path and translations $js_vars = array( 'wp_ajax_url' => admin_url( 'admin-ajax.php' ), 'translations' => array( 'successful' => esc_html__( 'Successful generation !!', 'all-sources-images' ), 'error_generation' => esc_html__( 'Error with images generation', 'all-sources-images' ), 'error_plugin' => esc_html__( 'Error with the plugin', 'all-sources-images' ), 'search_terms' => esc_html__( 'Search Terms', 'all-sources-images' ), 'img_resolution' => esc_html__( 'Image Resolution', 'all-sources-images' ), 'img_size' => esc_html__( 'Image Size', 'all-sources-images' ), 'img_bank' => esc_html__( 'Image Bank', 'all-sources-images' ), ), ); $bulk_generation_flag = filter_input( INPUT_POST, 'all-sources-images', FILTER_UNSAFE_RAW ); $ids_mpt_generation_raw = filter_input( INPUT_GET, 'ids_mpt_generation', FILTER_UNSAFE_RAW ); $cats_raw = filter_input( INPUT_GET, 'cats', FILTER_UNSAFE_RAW ); if ( ! empty( $bulk_generation_flag ) || ! empty( $ids_mpt_generation_raw ) || ! empty( $cats_raw ) ) { $ids_from_cats = ''; if ( ! empty( $cats_raw ) ) { $taxo_term = get_term( absint( $cats_raw ) ); if ( empty( $taxo_term ) ) { return false; } $cpts = get_post_types( array( 'public' => true, ), 'names' ); $post_ids = get_posts( array( 'numberposts' => -1, 'tax_query' => array(array( 'taxonomy' => $taxo_term->taxonomy, 'field' => 'slug', 'terms' => $taxo_term->slug, )), 'post_type' => array(), 'post_status' => array( 'publish', 'draft', 'pending', 'future', 'private' ), 'fields' => 'ids', ) ); $ids = ''; foreach ( $post_ids as $post_id ) { $ids .= $post_id . ','; } $ids_from_cats = substr_replace( $ids, '', -1 ); } $ids_param = is_string( $ids_mpt_generation_raw ) ? sanitize_text_field( $ids_mpt_generation_raw ) : ''; // Use IDs from category if available, otherwise from URL parameter $ids = ! empty( $ids_from_cats ) ? $ids_from_cats : $ids_param; $ids = array_map( 'intval', explode( ',', trim( $ids, ',' ) ) ); $count = count( $ids ); $ids = json_encode( $ids ); $ajax_nonce = wp_create_nonce( 'ajax_nonce_All_Sources_Images' ); $image_blocks = isset( $options_auto['image_block'] ) && is_array( $options_auto['image_block'] ) ? $options_auto['image_block'] : array(); $counter_image_block = ! empty( $image_blocks ) ? count( $image_blocks ) : 1; if ( isset( $options_auto['bulk_generation_interval'] ) && (int) $options_auto['bulk_generation_interval'] !== 0 ) { $remaining_seconds = $this->cron_scheduled(); } else { $remaining_seconds = 0; } $js_vars['sendposts'] = array( 'posts' => $ids, 'count' => $count, 'block_counter' => $counter_image_block, 'interval' => $remaining_seconds, 'nonce' => $ajax_nonce, ); } //Include Main dashboard Js if ( in_array( $hook, $ALLSI_new_pages ) || ($pagenow == 'index.php' || $pagenow == 'post.php' || $pagenow == 'post-new.php') && in_array( get_post_type( get_the_ID() ), $post_types_default['choosed_post_type'] ) ) { wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/all-sources-images-admin.js', array('jquery'), $this->version, array( 'strategy' => 'defer', ) ); } if ( $hook == 'all-sources-images_page_allsi-new-automatic' ) { $image_blocks = ( isset( $options_auto['image_block'] ) ? $options_auto['image_block'] : array() ); // Calculating the current block index based on existing blocks $blockIndex = count( $image_blocks ) + 1; // Starts after the existing blocks wp_localize_script( $this->plugin_name, 'allsiAutomaticSettings', array( 'blockIndex' => $blockIndex, ) ); } $current_post_ID = json_encode( array_map( 'intval', array(get_the_ID()) ) ); $post_nounce = wp_create_nonce( 'ajax_nonce_All_Sources_Images' ); // Check if dalle is the first chosen image bank if ( isset( $options_banks['api_chosen_auto'] ) && true === in_array( 'dallev1', $options_banks['api_chosen_auto'] ) && 'dallev1' === reset( $options_banks['api_chosen_auto'] ) ) { $dalle = "true"; } else { $dalle = "false"; } if ( TRUE === $block['enable_manual_search'] ) { $block['enable_manual_search'] = "true"; } /* General settings */ $post_vars['postgeneration'] = array( 'wp_ajax_url' => admin_url( 'admin-ajax.php' ), 'postID' => $current_post_ID, 'generateImg' => plugin_dir_url( __FILE__ ) . 'img/generate.png', 'strGenerate' => esc_html__( 'Generation', 'all-sources-images' ), 'strNoGenerate' => esc_html__( 'Generate Automatically', 'all-sources-images' ), 'strDalleGenerate' => esc_html__( 'Dall-e v3 Generation may take 20 to 40 seconds. Please be patient', 'all-sources-images' ), 'strManualGenerate' => esc_html__( 'Generate Manually', 'all-sources-images' ), 'strNoRewrite' => esc_html__( 'Edit your overwrite settings if you want a new image', 'all-sources-images' ), 'strConfirmReplace' => esc_html__( 'A featured image already exists. Do you want to replace it with a new one?', 'all-sources-images' ), 'manual_search' => $block['enable_manual_search'], 'dalle' => $dalle, 'nonce' => $post_nounce, ); // Do not include this file into unselected posts types if ( ($pagenow == 'index.php' || $pagenow == 'post.php' || $pagenow == 'post-new.php') && in_array( get_post_type( get_the_ID() ), $post_types_default['choosed_post_type'] ) ) { wp_localize_script( $this->plugin_name, 'allsiGenerationSpecificPostJsVars', $post_vars ); } /* Translation for JS file */ $translations_var['translations'] = array( 'only_one_featured' => esc_html__( 'Only one featured image per post is possible', 'all-sources-images' ), 'delete_logs' => esc_html__( 'Are you sure to delete all logs ?', 'all-sources-images' ), 'no_interval' => esc_html__( 'No interval', 'all-sources-images' ), 'per_minute' => esc_html__( 'per minute', 'all-sources-images' ), 'per_hour' => esc_html__( 'per hour', 'all-sources-images' ), ); wp_localize_script( $this->plugin_name, 'allsiTranslationsJsVars', $translations_var ); wp_localize_script( 'common-mpt', 'allsiTranslationsJsVars', $translations_var ); wp_localize_script( 'mpt-rating', 'allsiTranslationsJsVars', $translations_var ); wp_localize_script( 'images-generation', 'allsiGenerationVars', $js_vars ); } public function cron_scheduled() { // interval delay $options = wp_parse_args( get_option( 'ALLSI_plugin_main_settings' ) ); $options_interval = $options['bulk_generation_interval']; // Switch interval options into seconds switch ( $options_interval ) { case '0': return false; case '1': $interval_seconds = 20; break; case '2': $interval_seconds = 30; break; case '3': $interval_seconds = 60; break; case '4': $interval_seconds = 120; break; case '5': $interval_seconds = 240; break; case '6': $interval_seconds = 600; break; case '7': $interval_seconds = 3600; break; default: return false; } return $interval_seconds; } /** * Show main menu item * * @since 4.0.0 * @since 6.2.0 Simplified to only use new UI pages */ public function ALLSI_main_settings() { // Main menu now opens Search directly. $this->media_picker_top_level_hook = add_menu_page( __( 'All Sources Images Options', 'all-sources-images' ), 'All Sources Images', 'ALLSI_manage', 'allsi-media-browser', array( $this, 'ALLSI_render_media_picker_page' ), 'dashicons-images-alt2', 81 ); // First child: Search (same page as parent). $this->media_picker_search_submenu_hook = add_submenu_page( 'allsi-media-browser', __( 'Search', 'all-sources-images' ), __( 'Search', 'all-sources-images' ), 'ALLSI_manage', 'allsi-media-browser', array( $this, 'ALLSI_render_media_picker_page' ) ); // Include new UI menus (Settings, Automatic, Bulk Generation) include_once plugin_dir_path( __FILE__ ) . 'partials/new-ui/new-ui-menus.php'; ALLSI_set_new_ui_admin( $this ); ALLSI_register_new_ui_menus(); /* Bulk Generation link for posts & custom post type */ $post_type_availables = get_option( 'ALLSI_plugin_posts_settings' ); if ( isset( $post_type_availables['choosed_post_type'] ) ) { if ( false == $post_type_availables['choosed_post_type'] ) { $post_type_availables['choosed_post_type'] = array(); } } else { $post_types_default = get_post_types( '', 'objects' ); unset( $post_types_default['attachment'], $post_types_default['revision'], $post_types_default['nav_menu_item'], $post_types_default['custom_css'], $post_types_default['customize_changeset'], $post_types_default['oembed_cache'], $post_types_default['user_request'], $post_types_default['wp_block'], $post_types_default['wp_template'], $post_types_default['wp_template_part'], $post_types_default['wp_global_styles'], $post_types_default['wp_navigation'] ); $post_type_availables = array(); $post_type_availables['choosed_post_type'] = array(); foreach ( $post_types_default as $post_type ) { $post_type_availables['choosed_post_type'][$post_type->name] = $post_type->name; } } foreach ( $post_type_availables['choosed_post_type'] as $screen ) { add_filter( 'bulk_actions-edit-' . $screen, array(&$this, 'ALLSI_add_bulk_actions') ); // Text on dropdown add_action( 'handle_bulk_actions-edit-' . $screen, array(&$this, 'ALLSI_bulk_action_handler'), 10, 3 ); // Redirection } // Genererate link for Categories add_filter( 'category_row_actions', array(&$this, 'ALLSI_add_bulk_action_category'), 10, 2 ); // Loop with each taxo for Genrate link $args_taxo = array( 'public' => true, ); $taxonomies = get_taxonomies( $args_taxo ); $taxonomies = array_diff( $taxonomies, ['post_tag', 'post_format'] ); foreach ( $taxonomies as $taxonomy ) { add_filter( $taxonomy . '_row_actions', array(&$this, 'ALLSI_add_bulk_action_category'), 10, 2 ); } } /** * Main actions * * @since 4.0.0 */ public function ALLSI_main_actions() { if ( !current_user_can( 'ALLSI_manage' ) ) { wp_die( esc_html__( 'You do not have sufficient permissions.', 'all-sources-images' ) ); } //register_setting('ASI-plugin-posts-settings', 'ALLSI_plugin_posts_settings'); register_setting( 'ASI-plugin-block-settings', 'ALLSI_plugin_block_settings', array( 'sanitize_callback' => array( $this, 'ALLSI_sanitize_block_settings' ), ) ); register_setting( 'ASI-plugin-main-settings', 'ALLSI_plugin_main_settings', array( 'sanitize_callback' => array($this, 'ALLSI_sanitize_main_settings'), ) ); register_setting( 'ASI-plugin-auto-image-settings', 'ALLSI_plugin_auto_image_settings', array( 'sanitize_callback' => array( $this, 'ALLSI_sanitize_auto_image_settings' ), ) ); register_setting( 'ASI-plugin-banks-settings', 'ALLSI_plugin_banks_settings', array( 'sanitize_callback' => array($this, 'ALLSI_sanitize_banks_settings'), ) ); register_setting( 'ASI-plugin-interval-settings', 'ALLSI_plugin_interval_settings', array( 'sanitize_callback' => array( $this, 'ALLSI_sanitize_interval_settings' ), ) ); register_setting( 'ASI-plugin-cron-settings', 'ALLSI_plugin_cron_settings', array( 'sanitize_callback' => array($this, 'ALLSI_sanitize_cron_settings'), ) ); register_setting( 'ASI-plugin-rights-settings', 'ALLSI_plugin_rights_settings', array( 'sanitize_callback' => array( $this, 'ALLSI_sanitize_rights_settings' ), ) ); register_setting( 'ASI-plugin-proxy-settings', 'ALLSI_plugin_proxy_settings', array( 'sanitize_callback' => array( $this, 'ALLSI_sanitize_proxy_settings' ), ) ); register_setting( 'ASI-plugin-logs-settings', 'ALLSI_plugin_logs_settings', array( 'sanitize_callback' => array( $this, 'ALLSI_sanitize_logs_settings' ), ) ); require_once dirname( __FILE__ ) . '/partials/delete_log.php'; add_filter( 'map_meta_cap', array(&$this, 'ALLSI_map_manage_options_capability'), 10, 4 ); } /** * Change to "ALLSI_manage" rights for all plugin forms * * @since 5.2.11 */ public function ALLSI_map_manage_options_capability( $caps, $cap, $user_id, $args ) { // Check if the capability being checked is 'manage_options' if ( $cap === 'manage_options' ) { // Check if the form is submitting a specific option related to your plugin // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified by WordPress Settings API in options.php $option_page = isset( $_POST['option_page'] ) ? sanitize_text_field( wp_unslash( $_POST['option_page'] ) ) : ''; if ( $option_page && in_array( $option_page, array( 'ASI-plugin-proxy-settings', 'ASI-plugin-main-settings', 'ASI-plugin-auto-image-settings', 'ASI-plugin-block-settings', 'ASI-plugin-cron-settings', 'ASI-plugin-logs-settings', 'ASI-plugin-rights-settings', 'ASI-plugin-banks-settings' ) ) ) { // If the user has the 'ALLSI_manage' capability, grant access to manage the options if ( current_user_can( 'ALLSI_manage' ) ) { $caps = array('ALLSI_manage'); } } } return $caps; } /** * Get current url * * @since 4.0.0 */ public function ALLSI_current_url() { $requested_url = ( is_ssl() ? 'https://' : 'http://' ); $requested_url .= isset( $_SERVER['HTTP_HOST'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : ''; $requested_url .= isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; return $requested_url; } /** * Default banks name * * @since 6.0.0 */ public function ALLSI_banks_name_auto() { /* Banks for Automatic Bulk */ $list_api_auto = array( esc_html__( 'Google Image (API)', 'all-sources-images' ) => array('google_image', true), esc_html__( 'DALL·E (v3)', 'all-sources-images' ) => array('dallev1', true), esc_html__( 'Openverse', 'all-sources-images' ) => array('cc_search', true), esc_html__( 'Flickr', 'all-sources-images' ) => array('flickr', true), esc_html__( 'Pixabay', 'all-sources-images' ) => array('pixabay', true), esc_html__( 'GIPHY', 'all-sources-images' ) => array('giphy', true), esc_html__( 'Youtube', 'all-sources-images' ) => array('youtube', true), esc_html__( 'Unsplash', 'all-sources-images' ) => array('unsplash', true), esc_html__( 'Pexels', 'all-sources-images' ) => array('pexels', true), esc_html__( 'Stable Diffusion', 'all-sources-images' ) => array('stability', true), esc_html__( 'Replicate', 'all-sources-images' ) => array('replicate', true), esc_html__( 'Gemini (Google AI)', 'all-sources-images' ) => array('gemini', true), esc_html__( 'Cloudflare Workers AI', 'all-sources-images' ) => array('workers_ai', true), esc_html__( 'Google Translate', 'all-sources-images' ) => array('google_translate', true), ); return $list_api_auto; } public function ALLSI_banks_name_manual() { $list_api_manual = array( esc_html__( 'Google Image (API)', 'all-sources-images' ) => array('google_image', true), esc_html__( 'DALL·E (v3)', 'all-sources-images' ) => array('dallev1', true), esc_html__( 'Openverse', 'all-sources-images' ) => array('cc_search', true), esc_html__( 'Flickr', 'all-sources-images' ) => array('flickr', true), esc_html__( 'Pixabay', 'all-sources-images' ) => array('pixabay', true), esc_html__( 'GIPHY', 'all-sources-images' ) => array('giphy', true), esc_html__( 'Youtube', 'all-sources-images' ) => array('youtube', true), esc_html__( 'Unsplash', 'all-sources-images' ) => array('unsplash', true), esc_html__( 'Pexels', 'all-sources-images' ) => array('pexels', true), esc_html__( 'Stable Diffusion', 'all-sources-images' ) => array('stability', true), esc_html__( 'Replicate', 'all-sources-images' ) => array('replicate', true), esc_html__( 'Gemini (Google AI)', 'all-sources-images' ) => array('gemini', true), esc_html__( 'Cloudflare Workers AI', 'all-sources-images' ) => array('workers_ai', true), ); return $list_api_manual; } /** * Codes for AI-based generators * * @since 6.1.8 */ public function ALLSI_ai_source_codes() { return array( 'dallev1', 'stability', 'replicate', 'gemini', 'workers_ai', ); } /** * Get default posts types & categories * * @since 5.0.0 */ public function ALLSI_default_posts_types() { $post_types_default['choosed_post_type'] = get_post_types(); unset( $post_types_default['choosed_post_type']['attachment'], $post_types_default['choosed_post_type']['revision'], $post_types_default['choosed_post_type']['nav_menu_item'], $post_types_default['choosed_post_type']['custom_css'], $post_types_default['choosed_post_type']['customize_changeset'], $post_types_default['choosed_post_type']['oembed_cache'], $post_types_default['choosed_post_type']['user_request'], $post_types_default['choosed_post_type']['wp_block'], $post_types_default['choosed_post_type']['wp_template'], $post_types_default['choosed_post_type']['wp_template_part'], $post_types_default['choosed_post_type']['wp_global_styles'], $post_types_default['choosed_post_type']['wp_navigation'] ); $categories_default = get_terms( array( 'taxonomy' => 'category', 'hide_empty' => false, ) ); foreach ( $categories_default as $category ) { $post_types_default['choosed_categories'][$category->slug] = $category->name; } return $post_types_default; } /** * Default values for Image admin tabs * * @since 4.0.0 */ public function ALLSI_default_options_main_settings( $never_set = FALSE ) { $default_options = array( 'image_block' => array( 1 => array( 'image_location' => 'featured', 'api_chosen' => 'pixabay', 'based_on' => 'title', 'translation_EN' => 'true', 'title_selection' => 'full_title', 'selected_image' => 'first_result', ), ), 'image_filename' => 'title', 'rewrite_featured' => '', 'image_reuse' => '', 'image_flip' => '', 'image_crop' => '', 'bulk_generation_interval' => 0, ); return $default_options; } /** * Default values for Auto Image tab * * @since 6.2.0 */ public function ALLSI_default_options_auto_image_settings( $never_set = FALSE ) { $default_options = array( 'enabled' => 'disable', 'post_types' => array(), 'term_ids' => array(), 'exclude_post_ids' => '', ); return $default_options; } /** * Default values for "Source" (Banks) admin tabs * * @since 4.0.0 */ public function ALLSI_default_options_banks_settings( $never_set = FALSE ) { // Migration from v4 to v5 $options_banks = get_option( 'ALLSI_plugin_banks_settings' ); if ( isset( $options_banks['api_chosen'] ) && !isset( $options_banks['api_chosen_auto'] ) ) { // Already chosen bank as selected $ar_bank_auto = array($options_banks['api_chosen']); } else { // Default banks - Pixabay as primary $ar_bank_auto = array('pixabay', 'openverse'); } // Default manual banks: stock image sources selected by default (as shown in settings UI) // Order: Pixabay, Flickr, Openverse (cc_search), Unsplash, GIPHY, Pexels // AI sources and YouTube/Google Image excluded by default $default_options = array( 'api_chosen_manual' => array( 'pixabay', 'flickr', 'cc_search', 'unsplash', 'giphy', 'pexels', ), 'api_chosen_auto' => $ar_bank_auto, 'googleimage' => array( 'cxid' => '', 'apikey' => '', 'search_country' => 'en', 'img_color' => '', 'filetype' => '', 'imgsz' => 'large', 'imgtype' => '', 'safe' => 'moderate', 'rights' => array(), ), 'dallev1' => array( 'apikey' => '', 'imgsize' => '1024x1024', ), 'stability' => array( 'apikey' => '', 'model' => 'sd3-large', 'aspect_ratio' => '16:9', 'output_format' => 'jpeg', 'use_negative_prompt' => '', ), 'replicate' => array( 'apikey' => '', 'model' => 'black-forest-labs/flux-schnell', 'output_format' => 'webp', 'aspect_ratio' => '16:9', ), 'gemini' => array( 'apikey' => '', 'model' => 'gemini-2.5-flash-image', 'aspect_ratio' => '1:1', 'image_size' => '', ), 'workers_ai' => array( 'account_id' => '', 'api_token' => '', 'model' => '@cf/black-forest-labs/flux-1-schnell', 'steps' => 4, 'negative_prompt' => '', ), 'cc_search' => array( 'source' => 1, 'rights' => '', 'imgtype' => '', 'aspect_ratio' => 'tall', ), 'flickr' => array( 'apikey' => '', 'rights' => '', 'imgtype' => 7, ), 'pixabay' => array( 'username' => '', 'apikey' => '', 'imgtype' => 'all', 'search_country' => 'en', 'orientation' => 'all', 'min_width' => 0, 'min_height' => 0, 'safesearch' => 'false', ), 'pexels' => array( 'apikey' => '', 'orientation' => '', 'size' => 'large', 'color' => '', 'locale' => 'en-US', ), 'giphy' => array( 'apikey' => '', 'rating' => 'g', 'lang' => 'en', 'limit' => 25, 'media_type'=> 'gifs', 'bundle' => '', ), 'unsplash' => array( 'apikey' => '', 'orientation' => '', 'content_filter' => 'low', 'color' => '', ), 'youtube' => array( 'apikey' => '', 'thumbnail_quality' => 'high', 'search_order' => 'relevance', 'video_duration' => 'any', 'safe_search' => 'moderate', 'max_results' => 5, 'region_code' => '', ), 'google_translate' => array( 'apikey' => '', ), ); return $default_options; } /** * Default values for Interval admin tabs * * @since 4.0.0 */ /* public function ALLSI_default_options_interval_settings( $never_set = FALSE ) { $default_options = array( // Interval 'bulk_generation_interval' => 0 ); return $default_options; }*/ /** * Default values for Gutenberg Block * * @since 4.0.0 */ public function ALLSI_default_options_block_settings( $never_set = FALSE ) { $default_options = array( 'enable_manual_search' => true, 'translation_EN' => 'true', 'source_lang' => '', // Empty = auto-detect from WordPress language 'translate_alt' => '', 'translate_alt_lang' => '', 'google_translate_apikey' => '', ); return $default_options; } /** * Default values for Cron admin tabs * * @since 4.0.0 */ public function ALLSI_default_options_cron_settings( $never_set = FALSE ) { $default_options = array( 'enable_cron' => 'disable', 'cron_post_types' => array('post', 'page'), 'cron_interval_value'=> 5, 'cron_interval_unit' => 'minutes', 'posts_date_mode' => 'all', 'posts_date_value' => 5, 'posts_date_unit' => 'days', 'posts_per_run' => 5, ); return $default_options; } /** * Default values for Rights admin tabs * * @since 5.2.11 */ public function ALLSI_default_options_rights_settings( $never_set = FALSE ) { $default_options = array( 'rights_editor' => '', 'rights_author' => '', 'rights_contributor' => '', 'rights_subscriber' => '', ); return $default_options; } /** * Default values for Proxy admin tabs * * @since 4.0.0 */ public function ALLSI_default_options_proxy_settings( $never_set = FALSE ) { $default_options = array( 'enable_proxy' => 'disable', 'proxy_mode' => 'legacy', 'proxy_address' => '', 'proxy_port' => '80', 'proxy_username' => '', 'proxy_password' => '', 'cloudflare_worker_url' => '', 'cloudflare_token' => '', ); return $default_options; } /** * Default values for logs admin tabs * * @since 4.0.0 */ public function ALLSI_default_options_logs_settings( $never_set = FALSE ) { $default_options = array( 'logs' => '', ); return $default_options; } /** * Create file for logs * * @since 4.0.0 */ private function ALLSI_log_file( $check = false ) { $logs_dir = ALLSI_ensure_logs_dir(); if ( false === $logs_dir ) { if ( true === $check ) { return false; } return false; } $files = @scandir( $logs_dir ); $result = ''; if ( !empty( $files ) ) { foreach ( $files as $key => $value ) { if ( !in_array( $value, array('.', '..'), true ) ) { if ( !is_dir( $logs_dir . $value ) && strstr( $value, '.log' ) ) { $result = $value; } } } } if ( true == $check && empty( $result ) ) { return false; } if ( empty( $result ) ) { $result = 'mpt-' . wp_generate_password( 14, false, false ) . '.log'; } return $result; } /** * Get the absolute path to the current log file. * * @return string|false */ private function ALLSI_get_log_file_path() { $logs_dir = ALLSI_ensure_logs_dir(); if ( false === $logs_dir ) { return false; } $log_file = $this->ALLSI_log_file( true ); if ( empty( $log_file ) ) { return false; } $log_path = $logs_dir . $log_file; if ( ! is_file( $log_path ) || ! is_readable( $log_path ) ) { return false; } return $log_path; } /** * Read log contents, optionally tailing large files for performance. * * @param int $max_bytes Maximum bytes to read from the end of the file. * * @return array */ private function ALLSI_read_log_contents( $max_bytes = 200000 ) { $log_path = $this->ALLSI_get_log_file_path(); if ( false === $log_path ) { return array( 'content' => '', 'filename' => '', 'truncated' => false, 'size' => 0, ); } $file_size = filesize( $log_path ); if ( false === $file_size || $file_size <= 0 ) { return array( 'content' => '', 'filename' => basename( $log_path ), 'truncated' => false, 'size' => 0, ); } $truncated = false; $content = ''; $max_bytes = max( 1024, absint( $max_bytes ) ); if ( $file_size > $max_bytes ) { $truncated = true; $handle = fopen( $log_path, 'rb' ); if ( false !== $handle ) { fseek( $handle, -$max_bytes, SEEK_END ); $content = fread( $handle, $max_bytes ); fclose( $handle ); } if ( false === $content ) { $content = ''; } $content = ltrim( (string) $content ); } else { $content = file_get_contents( $log_path ); if ( false === $content ) { $content = ''; } } return array( 'content' => (string) $content, 'filename' => basename( $log_path ), 'truncated' => $truncated, 'size' => (int) $file_size, ); } /** * Build log viewer data for rendering and AJAX responses. * * @param int $max_bytes Maximum bytes to read from log file. * * @return array */ public function ALLSI_get_log_view_data( $max_bytes = 200000 ) { $log_data = $this->ALLSI_read_log_contents( $max_bytes ); if ( empty( $log_data['filename'] ) ) { return array( 'content' => '', 'filename' => '', 'truncated' => false, 'size' => 0, 'message' => __( 'No log file found.', 'all-sources-images' ), ); } $message = sprintf( /* translators: %s: log file name */ __( 'Viewing log file: %s', 'all-sources-images' ), $log_data['filename'] ); if ( ! empty( $log_data['truncated'] ) ) { $message .= ' ' . __( '(showing latest entries)', 'all-sources-images' ); } $log_data['message'] = $message; return $log_data; } /** * AJAX: return log contents for the Logging viewer. */ public function ALLSI_get_log_contents_ajax() { check_ajax_referer( 'allsi_log_viewer_nonce', 'nonce' ); if ( ! current_user_can( 'ALLSI_manage' ) ) { wp_send_json_error( array( 'message' => __( 'Permission denied.', 'all-sources-images' ) ), 403 ); } $log_data = $this->ALLSI_get_log_view_data(); wp_send_json_success( $log_data ); } /** * AJAX: clear the active log file. */ public function ALLSI_clear_log_ajax() { check_ajax_referer( 'allsi_log_viewer_nonce', 'nonce' ); if ( ! current_user_can( 'ALLSI_manage' ) ) { wp_send_json_error( array( 'message' => __( 'Permission denied.', 'all-sources-images' ) ), 403 ); } $log_path = $this->ALLSI_get_log_file_path(); if ( false === $log_path ) { wp_send_json_success( array( 'content' => '', 'filename' => '', 'truncated' => false, 'size' => 0, 'message' => __( 'No log file found.', 'all-sources-images' ), ) ); } $deleted = wp_delete_file( $log_path ); if ( false === $deleted && file_exists( $log_path ) ) { wp_send_json_error( array( 'message' => __( 'Unable to clear log file.', 'all-sources-images' ) ) ); } wp_send_json_success( array( 'content' => '', 'filename' => '', 'truncated' => false, 'size' => 0, 'message' => __( 'Log file cleared.', 'all-sources-images' ), ) ); } /** * Create file for logs * * @since 4.0.0 */ public function ALLSI_monolog_call() { $main_settings = get_option( 'ALLSI_plugin_logs_settings' ); // Ensure the logger class is loaded if ( ! class_exists( 'ALLSI_Logger' ) ) { require_once plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-allsi-logger.php'; } // Check if logs enabled if ( ! empty( $main_settings['logs'] ) && true == $main_settings['logs'] ) { $logs_dir = ALLSI_ensure_logs_dir(); if ( false === $logs_dir ) { return new ALLSI_Nolog(); } $logfile = $this->ALLSI_log_file(); $log = new ALLSI_Logger( 'ALLSI_logger', $logs_dir . $logfile, true ); } else { $log = new ALLSI_Nolog(); } return $log; } /** * Check if interval generation is enabled * * @since 4.0.0 */ public function ALLSI_check_interval() { $options = wp_parse_args( get_option( 'ALLSI_plugin_main_settings' ), $this->ALLSI_default_options_main_settings( TRUE ) ); $value_bulk_generation_interval = ( isset( $options['bulk_generation_interval'] ) ? (int) $options['bulk_generation_interval'] : 0 ); if ( 0 == $value_bulk_generation_interval ) { return false; } else { return true; } } /** * Check if interval generation is enabled * * @since 4.0.0 */ public function ALLSI_do_interval_cron( $new_ids_to_add = false ) { // Get processing ids $interval_posts_to_generate = get_transient( 'ALLSI_interval_generation' ); if ( ! is_array( $interval_posts_to_generate ) ) { $interval_posts_to_generate = array(); } // Default status to "generation done". The loop below sets it to false when it finds work. $no_more_post_to_generate = true; $new_ids_to_add_array = is_array( $new_ids_to_add ) ? $new_ids_to_add : array(); $has_new_ids_to_add = ! empty( $new_ids_to_add_array ); $new_ids_to_add_is_false = ( false === $new_ids_to_add ); foreach ( $interval_posts_to_generate as $post => $post_val ) { $continue_loop = false; // Get the first post not already generated if ( FALSE == $interval_posts_to_generate[$post]['processed'] ) { // Change default status to "generation processing" $no_more_post_to_generate = false; // Generation $launch_MPT = new All_Sources_Images_Generation($this->plugin_name, $this->version); $ALLSI_return = $launch_MPT->ALLSI_create_thumb( $interval_posts_to_generate[$post]['id'], '0', '0', '0', '0' ); // Get the return status if ( $ALLSI_return == null ) { // Settings $main_settings = get_option( 'ALLSI_plugin_main_settings' ); // Image location $image_location = ( !empty( $main_settings['image_location'] ) ? $main_settings['image_location'] : 'featured' ); if ( has_post_thumbnail( $interval_posts_to_generate[$post]['id'] ) && "featured" === $image_location ) { $interval_posts_to_generate[$post]['processed'] = 'already-exist'; $continue_loop = true; } else { // Add the status to this speficic post : Problem $interval_posts_to_generate[$post]['processed'] = 'error'; } } else { // Add the status to theis speficic post : ok $interval_posts_to_generate[$post]['processed'] = true; } // Limit only to the first post if ( TRUE !== $continue_loop ) { break; } } } // Generation done and new ids to generate if ( TRUE == $no_more_post_to_generate && $has_new_ids_to_add ) { // Delete old posts delete_transient( 'ALLSI_interval_generation' ); $new_posts_to_generate = array(); foreach ( $new_ids_to_add_array as $id ) { $new_posts_to_generate[] = array( 'id' => (int) $id, 'processed' => false, ); } // Add news posts set_transient( 'ALLSI_interval_generation', $new_posts_to_generate ); } elseif ( TRUE == $no_more_post_to_generate && $new_ids_to_add_is_false ) { // Generation done // Nothing to add/do } elseif ( FALSE == $no_more_post_to_generate && $has_new_ids_to_add ) { // Update with new ids (added at the end) foreach ( $new_ids_to_add_array as $id ) { $interval_posts_to_generate[] = array( 'id' => (int) $id, 'processed' => false, ); } // Allow ids into the array only once. Avoid duplicate ids (Remove the last ones) $temp_generate_posts = array(); foreach ( $interval_posts_to_generate as &$v ) { if ( !isset( $temp_generate_posts[$v['id']] ) ) { $temp_generate_posts[$v['id']] =& $v; } } $interval_posts_to_generate = array_values( $temp_generate_posts ); // Add news posts set_transient( 'ALLSI_interval_generation', $interval_posts_to_generate ); } else { // Generation not finished : Updating transient set_transient( 'ALLSI_interval_generation', $interval_posts_to_generate ); } } /** * Add "Generate featured images" into bulk menu for categories * * @since 4.0.0 */ public function ALLSI_add_bulk_action_category( $actions, $tag ) { $actions['atp'] = '' . esc_html__( 'Generate featured images', 'all-sources-images' ) . ''; return $actions; } /** * Redirection for bulk action * * @since 4.0.0 */ public function ALLSI_bulk_action_handler( $redirect_to, $action_name, $post_ids ) { if ( 'bulk_regenerate_thumbnails' === $action_name ) { $ids = implode( ',', array_map( 'intval', $post_ids ) ); wp_safe_redirect( admin_url( 'admin.php?page=allsi-new-bulk-generation&auto_generate_ids=' . $ids ) ); exit; } return $redirect_to; } /** * Add "Generate featured images" into bulk menu for posts * * @since 4.0.0 */ public function ALLSI_add_bulk_actions( $actions ) { // Enqueue the bulk actions script with localized text wp_enqueue_script( 'allsi-bulk-actions', plugin_dir_url( __FILE__ ) . 'js/bulk-actions.js', array( 'jquery' ), $this->version, true ); wp_localize_script( 'allsi-bulk-actions', 'allsiBulkActions', array( 'optionText' => esc_html__( 'Generate Images (ASI)', 'all-sources-images' ), ) ); return $actions; } /** * Testing if APIs works into settings page * * @since 5.0.0 */ public function ALLSI_test_apis() { check_ajax_referer( 'api_testing_nonce', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( __( 'Permission denied.', 'all-sources-images' ) ); } // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified above with check_ajax_referer(). $apiKey = isset( $_POST['apikey'] ) ? sanitize_text_field( wp_unslash( $_POST['apikey'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified above with check_ajax_referer(). $cxId = isset( $_POST['cxid'] ) ? sanitize_text_field( wp_unslash( $_POST['cxid'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified above with check_ajax_referer(). $apiBank = isset( $_POST['apibank'] ) ? sanitize_text_field( wp_unslash( $_POST['apibank'] ) ) : ''; $response = ''; if ( 'pixabay' === $apiBank ) { $apiUrl = "https://pixabay.com/api/?key=" . $apiKey . "&q=exemple"; $response = $this->ALLSI_remote_get( 'pixabay', $apiUrl ); } elseif ( 'google_image' === $apiBank ) { if ( ! class_exists( 'ALLSI_Source_Google_Image' ) ) { wp_send_json_error( __( 'Google Image source is unavailable.', 'all-sources-images' ) ); } if ( empty( $cxId ) ) { wp_send_json_error( __( 'CX ID is required for Google Custom Search.', 'all-sources-images' ) ); } $default_options = $this->ALLSI_default_options_banks_settings( true ); $google_defaults = isset( $default_options['googleimage'] ) ? $default_options['googleimage'] : array(); $google_options = array_merge( $google_defaults, array( 'apikey' => $apiKey, 'cxid' => $cxId, 'imgsz' => isset( $google_defaults['imgsz'] ) ? $google_defaults['imgsz'] : 'large', ) ); $proxy_args = $this->ALLSI_get_proxy_args(); $source = new ALLSI_Source_Google_Image(); $context = array( 'options' => array( 'googleimage' => $google_options ), 'search' => 'asi test', 'proxy_args' => $proxy_args, 'generation' => $this, 'get_only_thumb' => true, ); $result = $source->generate( $context ); if ( is_wp_error( $result ) ) { wp_send_json_error( $result->get_error_message() ); } if ( isset( $result['items'] ) && ! empty( $result['items'] ) ) { wp_send_json_success( wp_json_encode( $result ) ); } wp_send_json_error( __( 'No results found or there was an error.', 'all-sources-images' ) ); } elseif ( 'dalle' === $apiBank ) { if ( ! class_exists( 'ALLSI_Source_Dallev1' ) ) { wp_send_json_error( __( 'DALL·E source is unavailable.', 'all-sources-images' ) ); } if ( empty( $apiKey ) ) { wp_send_json_error( __( 'API key is required.', 'all-sources-images' ) ); } $default_options = $this->ALLSI_default_options_banks_settings( true ); $dalle_defaults = isset( $default_options['dallev1'] ) ? $default_options['dallev1'] : array(); $dalle_options = array_merge( $dalle_defaults, array( 'apikey' => $apiKey ) ); $proxy_args = $this->ALLSI_get_proxy_args(); $source = new ALLSI_Source_Dallev1(); $context = array( 'options' => array( 'dallev1' => $dalle_options ), 'search' => 'asi dalle test', 'proxy_args' => $proxy_args, 'get_only_thumb' => true, ); $result = $source->generate( $context ); if ( is_wp_error( $result ) ) { wp_send_json_error( $result->get_error_message() ); } if ( isset( $result['data'] ) && ! empty( $result['data'] ) ) { wp_send_json_success( wp_json_encode( $result ) ); } wp_send_json_error( __( 'No results found or there was an error.', 'all-sources-images' ) ); } elseif ( 'youtube' === $apiBank ) { if ( ! class_exists( 'ALLSI_Source_Youtube' ) ) { wp_send_json_error( __( 'YouTube source is unavailable.', 'all-sources-images' ) ); } $default_options = $this->ALLSI_default_options_banks_settings( true ); $youtube_defaults = isset( $default_options['youtube'] ) ? $default_options['youtube'] : array(); $youtube_options = array_merge( $youtube_defaults, array( 'apikey' => $apiKey, 'max_results' => 1 ) ); $proxy_args = $this->ALLSI_get_proxy_args(); $source = new ALLSI_Source_Youtube(); $context = array( 'options' => array( 'youtube' => $youtube_options ), 'search' => 'asi test', 'proxy_args' => $proxy_args, 'generation' => $this, 'get_only_thumb' => true, ); $result = $source->generate( $context ); if ( is_wp_error( $result ) ) { wp_send_json_error( $result->get_error_message() ); } if ( isset( $result['items'] ) && ! empty( $result['items'] ) ) { wp_send_json_success( wp_json_encode( $result ) ); } wp_send_json_error( __( 'No results found or there was an error.', 'all-sources-images' ) ); } elseif ( 'unsplash' === $apiBank ) { $apiUrl = "https://api.unsplash.com/search/photos?query=test&client_id={$apiKey}"; $response = $this->ALLSI_remote_get( 'unsplash', $apiUrl ); } elseif ( 'giphy' === $apiBank ) { if ( empty( $apiKey ) ) { wp_send_json_error( __( 'API key is required.', 'all-sources-images' ) ); } $query_args = array( 'api_key' => $apiKey, 'q' => 'asi test', 'limit' => 1, 'rating' => 'pg', 'lang' => 'en', ); $apiUrl = add_query_arg( $query_args, 'https://api.giphy.com/v1/gifs/search' ); $response = $this->ALLSI_remote_get( 'giphy', $apiUrl ); if ( is_wp_error( $response ) ) { wp_send_json_error( __( 'Error connecting to the API.', 'all-sources-images' ) ); } $status_code = wp_remote_retrieve_response_code( $response ); $body = wp_remote_retrieve_body( $response ); if ( 200 !== $status_code ) { $decoded = json_decode( $body, true ); $message = isset( $decoded['meta']['msg'] ) ? $decoded['meta']['msg'] : __( 'Unexpected GIPHY API response.', 'all-sources-images' ); wp_send_json_error( $message, $status_code ); } wp_send_json_success( $body ); return; } elseif ( 'pexels' === $apiBank ) { $apiUrl = "https://api.pexels.com/v1/search?query=nature&per_page=1"; $response = $this->ALLSI_remote_get( 'pexels', $apiUrl, array( 'headers' => array( 'Authorization' => $apiKey, ), ) ); } elseif ( 'stability' === $apiBank ) { if ( empty( $apiKey ) ) { wp_send_json_error( __( 'API key is required.', 'all-sources-images' ) ); } $request_args = array( 'headers' => array( 'Authorization' => 'Bearer ' . $apiKey, 'Accept' => 'application/json', ), 'timeout' => 20, ); $response = $this->ALLSI_remote_get( 'stability', 'https://api.stability.ai/v1/user/account', $request_args ); if ( is_wp_error( $response ) ) { wp_send_json_error( __( 'Error connecting to Stability AI.', 'all-sources-images' ) ); } $code = wp_remote_retrieve_response_code( $response ); $body = wp_remote_retrieve_body( $response ); if ( 200 === $code ) { wp_send_json_success( $body ); } $decoded = json_decode( $body, true ); $message = isset( $decoded['message'] ) ? $decoded['message'] : __( 'Unexpected Stability AI response.', 'all-sources-images' ); wp_send_json_error( $message, $code ); } elseif ( 'replicate' === $apiBank ) { // test Replicate API key by listing models $apiUrl = 'https://api.replicate.com/v1/models'; $response = $this->ALLSI_remote_get( 'replicate', $apiUrl, array( 'headers' => array( 'Authorization' => 'Token ' . $apiKey, 'Content-Type' => 'application/json', ), 'timeout' => 10, ) ); // Handle HTTP error if ( is_wp_error( $response ) ) { wp_send_json_error( 'Error connecting to Replicate API.' ); } // Check status code for unauthorized vs ok $code = wp_remote_retrieve_response_code( $response ); if ( 200 === $code ) { wp_send_json_success( wp_remote_retrieve_body( $response ) ); } elseif ( 401 === $code ) { wp_send_json_error( 'unauthorized', 401 ); } else { wp_send_json_error( 'Unexpected status: ' . $code, $code ); } } elseif ( 'gemini' === $apiBank ) { if ( empty( $apiKey ) ) { wp_send_json_error( __( 'API key is required.', 'all-sources-images' ) ); } $apiUrl = add_query_arg( array( 'key' => $apiKey, 'pageSize' => 1, ), 'https://generativelanguage.googleapis.com/v1beta/models' ); $response = $this->ALLSI_remote_get( 'gemini', $apiUrl, array( 'timeout' => 15, ) ); if ( is_wp_error( $response ) ) { wp_send_json_error( 'Error connecting to Gemini API.' ); } $code = wp_remote_retrieve_response_code( $response ); $body = wp_remote_retrieve_body( $response ); if ( 200 === $code ) { wp_send_json_success( $body ); } $decoded = json_decode( $body, true ); $message = isset( $decoded['error']['message'] ) ? $decoded['error']['message'] : __( 'Unexpected Gemini API response.', 'all-sources-images' ); wp_send_json_error( $message, $code ); } elseif ( 'workers_ai' === $apiBank ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified at function start with check_ajax_referer(). $account_id = isset( $_POST['account_id'] ) ? sanitize_text_field( wp_unslash( $_POST['account_id'] ) ) : ''; if ( empty( $account_id ) ) { wp_send_json_error( __( 'Account ID is required.', 'all-sources-images' ) ); } if ( empty( $apiKey ) ) { wp_send_json_error( __( 'API token is required.', 'all-sources-images' ) ); } // Use the AI finetunes endpoint to verify credentials (lightweight, read-only) $apiUrl = sprintf( 'https://api.cloudflare.com/client/v4/accounts/%s/ai/finetunes', rawurlencode( $account_id ) ); $request_args = array( 'headers' => array( 'Authorization' => 'Bearer ' . $apiKey, 'Content-Type' => 'application/json', ), 'timeout' => 15, ); $response = $this->ALLSI_remote_get( 'workers_ai', $apiUrl, $request_args ); if ( is_wp_error( $response ) ) { wp_send_json_error( __( 'Error connecting to Cloudflare Workers AI.', 'all-sources-images' ) ); } $code = wp_remote_retrieve_response_code( $response ); $body = wp_remote_retrieve_body( $response ); $decoded = json_decode( $body, true ); // Check if response indicates success (even empty results mean valid credentials) if ( 200 === $code && isset( $decoded['success'] ) && true === $decoded['success'] ) { wp_send_json_success( $body ); } // Handle specific error cases $message = ''; if ( isset( $decoded['errors'][0]['message'] ) ) { $message = $decoded['errors'][0]['message']; } elseif ( isset( $decoded['messages'][0] ) ) { $message = $decoded['messages'][0]; } elseif ( 401 === $code || 403 === $code ) { $message = __( 'Invalid API token or insufficient permissions. Ensure your token has Workers AI:Read permission.', 'all-sources-images' ); } elseif ( 404 === $code ) { $message = __( 'Account not found. Please verify your Account ID.', 'all-sources-images' ); } else { $message = __( 'Unexpected Workers AI response.', 'all-sources-images' ); } wp_send_json_error( $message, $code ); } else { $response = ''; wp_send_json_error( 'Unknown API bank: ' . $apiBank ); } if ( is_wp_error( $response ) ) { wp_send_json_error( 'Error connecting to the API.' ); } else { wp_send_json_success( $response['body'] ); } } /** * Register MPT Gutenberg Block * * @since 5.0.0 */ public function ALLSI_register_mpt_block() { $banks = wp_parse_args( get_option( 'ALLSI_plugin_banks_settings' ), $this->ALLSI_default_options_banks_settings( TRUE ) ); if ( isset( $banks['api_chosen_manual']['envato'] ) ) { unset($banks['api_chosen_manual']['envato']); } $manual_bank_labels = array(); $manual_banks = $this->ALLSI_banks_name_manual(); foreach ( $manual_banks as $label => $data ) { $manual_bank_labels[$data[0]] = $label; } $asset_file = (include plugin_dir_path( __FILE__ ) . 'blocks/allsi-images/build/index.asset.php'); wp_register_script( 'allsi-minimasonry', plugins_url( 'blocks/allsi-images/build/minimasonry.min.js', __FILE__ ), array(), filemtime( plugin_dir_path( __FILE__ ) . 'blocks/allsi-images/build/minimasonry.min.js' ), true ); $script_dependencies = $asset_file['dependencies']; $script_dependencies[] = 'allsi-minimasonry'; wp_register_style( 'allsi-images-editor-style', plugins_url( 'blocks/allsi-images/build/allsi-images-editor.css', __FILE__ ), array(), filemtime( plugin_dir_path( __FILE__ ) . 'blocks/allsi-images/build/allsi-images-editor.css' ) ); wp_register_script( 'allsi-images-script', plugins_url( 'blocks/allsi-images/build/index.js', __FILE__ ), $script_dependencies, $asset_file['version'] ); register_block_type( 'allsi/allsi-images', array( 'editor_script' => 'allsi-images-script', ) ); $default_post_id = apply_filters( 'ALLSI_media_picker_default_post_id', 0 ); $this->media_picker_default_post_id = $default_post_id; $block_settings = wp_parse_args( get_option( 'ALLSI_plugin_block_settings' ), $this->ALLSI_default_options_block_settings( TRUE ) ); $translation_en_active = ( ! empty( $block_settings['translation_EN'] ) && $block_settings['translation_EN'] == 'true' ); $translate_alt_active = ( ! empty( $block_settings['translate_alt'] ) && $block_settings['translate_alt'] == 'true' ); $translate_alt_lang = ( ! empty( $block_settings['translate_alt_lang'] ) ? $block_settings['translate_alt_lang'] : '' ); wp_localize_script( 'allsi-images-script', 'allsiAjax', array( 'ajax_url' => admin_url( 'admin-ajax.php' ), 'admin_url' => admin_url(), 'nonce' => wp_create_nonce( 'ALLSI_gutenberg_block' ), 'choosed_banks' => $banks['api_chosen_manual'], 'available_banks' => $manual_bank_labels, 'licensing_data' => '1', // All features available 'path_default_img' => plugins_url( '/blocks/allsi-images/img/', __FILE__ ), 'ai_sources' => $this->ALLSI_ai_source_codes(), 'default_post_id' => $default_post_id, 'translation_en' => $translation_en_active, 'translate_alt' => $translate_alt_active, 'translate_alt_lang' => $translate_alt_lang, ) ); // Locate character strings wp_set_script_translations( 'allsi-images-script', 'all-sources-images', plugin_dir_path( __DIR__ ) . 'languages' ); add_action( 'admin_enqueue_scripts', function ( $hook ) { // Checks whether you are on an editing page if ( 'post.php' === $hook || 'post-new.php' === $hook ) { wp_enqueue_script( 'manual-search', plugins_url( 'js/manual_search.js', __FILE__ ), array('wp-blocks', 'wp-data'), $this->version, true ); } } ); // Note: Translations are loaded automatically by WordPress.org for plugins hosted there. } /** * Enqueue style for MPT Gutenberg Block * * @since 5.0.0 */ public function ALLSI_enqueue_style_block() { wp_enqueue_style( 'allsi-images-editor-style' ); } /** * Register Media submenu page for standalone image explorer. */ public function ALLSI_register_media_picker_page() { $this->media_picker_hook = add_submenu_page( 'upload.php', __( 'All Sources Images', 'all-sources-images' ), __( 'All Sources Images', 'all-sources-images' ), 'upload_files', 'allsi-media-browser', array( $this, 'ALLSI_render_media_picker_page' ) ); } /** * Render container for the standalone explorer. */ public function ALLSI_render_media_picker_page() { echo '
'; echo '

' . esc_html__( 'All Sources Images', 'all-sources-images' ) . '

'; echo '

' . esc_html__( 'Search and download media from your connected sources without leaving the Media Library.', 'all-sources-images' ) . '

'; echo '
'; echo '
'; } /** * Enqueue assets when viewing the standalone explorer page. */ public function ALLSI_enqueue_media_picker_assets( $hook ) { $allowed_hooks = array_filter( array( $this->media_picker_hook, $this->media_picker_top_level_hook, $this->media_picker_search_submenu_hook, ) ); if ( empty( $allowed_hooks ) || ! in_array( $hook, $allowed_hooks, true ) ) { return; } wp_enqueue_style( 'allsi-images-editor-style' ); wp_enqueue_script( 'allsi-images-script' ); // Enqueue the media picker initialization script wp_enqueue_script( 'allsi-media-picker-init', plugin_dir_url( __FILE__ ) . 'js/media-picker-init.js', array( 'allsi-images-script' ), $this->version, true ); } /** * Enqueue integration script for the WordPress media modal so we can inject the explorer tab. */ public function ALLSI_enqueue_media_modal_assets( $hook = null ) { if ( ! current_user_can( 'upload_files' ) ) { return; } if ( function_exists( 'wp_enqueue_media' ) ) { wp_enqueue_media(); } $this->ALLSI_prepare_media_modal_scripts(); } /** * Ensure the media modal integration is also loaded on the front-end (Elementor, etc.). */ public function ALLSI_enqueue_front_media_modal_assets() { if ( ! is_user_logged_in() || ! current_user_can( 'upload_files' ) ) { return; } if ( function_exists( 'wp_enqueue_media' ) ) { wp_enqueue_media(); } $this->ALLSI_prepare_media_modal_scripts(); } /** * Shared loader for styles and scripts needed by the media modal tab. */ private function ALLSI_prepare_media_modal_scripts() { wp_enqueue_style( 'allsi-images-editor-style' ); wp_enqueue_script( 'allsi-images-script' ); if ( ! wp_script_is( 'allsi-media-modal', 'registered' ) ) { wp_register_script( 'allsi-media-modal', plugins_url( 'js/allsi-media-modal.js', __FILE__ ), array( 'jquery', 'media-views', 'allsi-images-script' ), $this->version, true ); } wp_localize_script( 'allsi-media-modal', 'allsiMediaModal', array( 'tabLabel' => __( 'All Sources Images', 'all-sources-images' ), 'fallbackPostId' => isset( $this->media_picker_default_post_id ) ? intval( $this->media_picker_default_post_id ) : 0, 'tabId' => 'allsi-media-tab', ) ); wp_enqueue_script( 'allsi-media-modal' ); } /** * Initialize the Elementor integration if necessary. */ public function ALLSI_maybe_boot_elementor() { if ( ! class_exists( '\\Elementor\\Plugin' ) ) { return; } if ( class_exists( 'ALLSI_Elementor_Integration' ) ) { return; } require_once plugin_dir_path( __FILE__ ) . 'elementor/class-allsi-elementor-integration.php'; new ALLSI_Elementor_Integration( $this->version ); } /** * Search MPT Gutenberg Block * * @since 5.0.0 */ public function ALLSI_block_searching_images() { // Security check first check_ajax_referer( 'ALLSI_gutenberg_block', 'nonce' ); if ( ! current_user_can( 'edit_posts' ) ) { wp_send_json_error( __( 'Permission denied.', 'all-sources-images' ) ); } if ( defined( 'ALLSI_DEBUG' ) && ALLSI_DEBUG ) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug logging only when ALLSI_DEBUG is true. error_log( '[All Sources Images] ALLSI_block_searching_images CALLED' ); } $search_raw = filter_input( INPUT_GET, 'search', FILTER_UNSAFE_RAW ); $bank_raw = filter_input( INPUT_GET, 'bank', FILTER_UNSAFE_RAW ); $id_raw = filter_input( INPUT_GET, 'id', FILTER_UNSAFE_RAW ); if ( ! is_string( $search_raw ) || ! is_string( $bank_raw ) || ! is_string( $id_raw ) || '' === $search_raw || '' === $bank_raw || '' === $id_raw ) { wp_send_json_error( 'Missing required parameters' ); return; } $search_term = sanitize_text_field( $search_raw ); $bank = sanitize_text_field( strtolower( $bank_raw ) ); $index_raw = filter_input( INPUT_GET, 'index', FILTER_UNSAFE_RAW ); $page_raw = filter_input( INPUT_GET, 'page', FILTER_UNSAFE_RAW ); $skip_translation_raw = filter_input( INPUT_GET, 'skip_translation', FILTER_UNSAFE_RAW ); $index = is_string( $index_raw ) ? absint( $index_raw ) : 0; $id = absint( $id_raw ); $page = is_string( $page_raw ) ? max( 1, absint( $page_raw ) ) : 1; // Skip translation if already translated by frontend (skip_translation=1) $skip_translation = is_string( $skip_translation_raw ) && absint( $skip_translation_raw ) === 1; // Translate search term to English if translation_EN is enabled AND not already translated if ( ! $skip_translation ) { $block_settings = wp_parse_args( get_option( 'ALLSI_plugin_block_settings' ), $this->ALLSI_default_options_block_settings( TRUE ) ); if ( ! empty( $block_settings['translation_EN'] ) && $block_settings['translation_EN'] == 'true' ) { // Use configured source language or auto-detect from WordPress if ( ! empty( $block_settings['source_lang'] ) ) { $source_lang = $block_settings['source_lang']; } else { $wp_lang = get_bloginfo( 'language' ); $source_lang = substr( $wp_lang, 0, 2 ); } if ( $source_lang !== 'en' && ! empty( $search_term ) ) { $REST_generation = new All_Sources_Images_Generation( $this->plugin_name, $this->version ); $translated_term = $REST_generation->ALLSI_translate_text( $search_term, $source_lang, 'en' ); if ( $translated_term !== false && ! empty( $translated_term ) ) { ALLSI_log( array( 'original_search' => $search_term, 'translated_search' => $translated_term, 'source_lang' => $source_lang, ), 'GUTENBERG_BLOCK_TRANSLATION' ); $search_term = $translated_term; } } } } ALLSI_log( array( 'search_term' => $search_term, 'bank' => $bank, 'post_id' => $id, 'index' => $index ), 'GUTENBERG_BLOCK_SEARCH' ); if ( 0 !== $id && FALSE === get_post_status( $id ) ) { wp_send_json_error( 'Error with post ID.' ); } $REST_generation = new All_Sources_Images_Generation($this->plugin_name, $this->version); $results_thumbs = $REST_generation->ALLSI_create_thumb( $id, '0', '1', '1', '1', TRUE, $search_term, $bank, null, null, null, false, array( 'page' => $page ) ); if ( $results_thumbs !== false ) { $response_preview = json_encode( $results_thumbs ); if ( strlen( $response_preview ) > 1200 ) { $response_preview = substr( $response_preview, 0, 1200 ) . '...'; } ALLSI_log( array( 'bank' => $bank, 'response_preview' => $response_preview, ), 'GUTENBERG_BLOCK_RESPONSE' ); } if ( $results_thumbs === false ) { ALLSI_log( 'No results returned from ALLSI_create_thumb', 'GUTENBERG_BLOCK_ERROR' ); wp_send_json_error( 'Error connecting to the API.' ); } else { // Normalize API response to universal format: always use 'images' array $normalized_images = array(); $bank_options = wp_parse_args( get_option( 'ALLSI_plugin_banks_settings' ), $this->ALLSI_default_options_banks_settings( TRUE ) ); $yt_options = isset( $bank_options['youtube'] ) ? $bank_options['youtube'] : array(); $yt_quality = isset( $yt_options['thumbnail_quality'] ) ? $yt_options['thumbnail_quality'] : 'high'; $yt_quality_fallback = array_unique( array( $yt_quality, 'maxresdefault', 'standard', 'high', 'medium', 'default' ) ); $pagination_data = array( 'page' => $page, 'has_more' => false, 'total_pages' => null, 'total' => null, ); // Detect bank and extract images + normalize structure if ( 'giphy' === $bank && isset( $results_thumbs['data'] ) && is_array( $results_thumbs['data'] ) ) { if ( isset( $results_thumbs['pagination'] ) && is_array( $results_thumbs['pagination'] ) ) { $giphy_pagination = $results_thumbs['pagination']; $limit_option = isset( $bank_options['giphy']['limit'] ) ? intval( $bank_options['giphy']['limit'] ) : 25; $limit_option = max( 1, min( 50, $limit_option ) ); $offset = isset( $giphy_pagination['offset'] ) ? max( 0, intval( $giphy_pagination['offset'] ) ) : ( ( $page - 1 ) * $limit_option ); $batch_count = isset( $giphy_pagination['count'] ) ? max( 0, intval( $giphy_pagination['count'] ) ) : count( $results_thumbs['data'] ); $pagination_data['page'] = max( 1, intval( floor( $offset / $limit_option ) + 1 ) ); if ( isset( $giphy_pagination['total_count'] ) ) { $total_count = max( 0, intval( $giphy_pagination['total_count'] ) ); $pagination_data['total'] = $total_count; $pagination_data['total_pages'] = (int) ceil( $total_count / $limit_option ); $pagination_data['has_more'] = ( $offset + $batch_count ) < $total_count; } else { $pagination_data['has_more'] = ( $batch_count >= $limit_option ); } } foreach ( $results_thumbs['data'] as $item ) { if ( empty( $item ) || ! is_array( $item ) ) { continue; } $images = isset( $item['images'] ) && is_array( $item['images'] ) ? $item['images'] : array(); $large_url = ''; $thumb_url = ''; $large_keys = array( 'downsized_large', 'original', 'downsized', 'fixed_height', 'fixed_width' ); $thumb_keys = array( 'fixed_height_small', 'fixed_width_small', 'fixed_height', 'fixed_width', 'downsized_still', 'downsized_small' ); foreach ( $large_keys as $key ) { if ( isset( $images[ $key ]['url'] ) && '' !== $images[ $key ]['url'] ) { $large_url = $images[ $key ]['url']; break; } } foreach ( $thumb_keys as $key ) { if ( isset( $images[ $key ]['url'] ) && '' !== $images[ $key ]['url'] ) { $thumb_url = $images[ $key ]['url']; break; } } if ( empty( $large_url ) && isset( $item['url'] ) ) { $large_url = $item['url']; } if ( empty( $large_url ) ) { continue; } if ( empty( $thumb_url ) ) { $thumb_url = $large_url; } $caption = ''; if ( isset( $item['user']['display_name'] ) && '' !== $item['user']['display_name'] ) { $caption = $item['user']['display_name']; } elseif ( isset( $item['username'] ) ) { $caption = $item['username']; } $normalized_images[] = array( 'url' => $large_url, 'thumb' => $thumb_url, 'title' => isset( $item['title'] ) ? $item['title'] : '', 'alt' => isset( $item['title'] ) ? $item['title'] : '', 'caption' => $caption, 'source' => isset( $item['url'] ) ? $item['url'] : '', 'rating' => isset( $item['rating'] ) ? $item['rating'] : '', 'media_id' => isset( $item['id'] ) ? $item['id'] : '', ); } } elseif (isset($results_thumbs['data']) && is_array($results_thumbs['data'])) { // DALL-E format: { data: [{url, revised_prompt}] } foreach ($results_thumbs['data'] as $item) { $normalized_images[] = array( 'url' => isset($item['url']) ? $item['url'] : '', 'thumb' => isset($item['url']) ? $item['url'] : '', 'title' => isset($item['revised_prompt']) ? $item['revised_prompt'] : '', 'alt' => isset($item['revised_prompt']) ? $item['revised_prompt'] : '', 'caption' => 'DALL-E Generated' ); } } elseif ( 'youtube' === $bank && isset($results_thumbs['items']) && is_array($results_thumbs['items']) ) { foreach ( $results_thumbs['items'] as $item ) { $thumbnails = isset( $item['snippet']['thumbnails'] ) ? $item['snippet']['thumbnails'] : array(); $chosen_large = ''; foreach ( $yt_quality_fallback as $quality_key ) { if ( isset( $thumbnails[ $quality_key ]['url'] ) ) { $chosen_large = $thumbnails[ $quality_key ]['url']; break; } } $thumb_medium = isset( $thumbnails['medium']['url'] ) ? $thumbnails['medium']['url'] : ( isset( $thumbnails['default']['url'] ) ? $thumbnails['default']['url'] : $chosen_large ); $title = isset( $item['snippet']['title'] ) ? $item['snippet']['title'] : ''; $channel = isset( $item['snippet']['channelTitle'] ) ? $item['snippet']['channelTitle'] : ''; $video_id = isset( $item['id']['videoId'] ) ? $item['id']['videoId'] : ''; $normalized_images[] = array( 'url' => $chosen_large, 'thumb' => $thumb_medium, 'title' => $title, 'alt' => $title, 'caption' => $channel, 'video_id' => $video_id, ); } } elseif ( 'flickr' === $bank ) { $photos = array(); if ( isset( $results_thumbs['photos']['photo'] ) && is_array( $results_thumbs['photos']['photo'] ) ) { $photos = $results_thumbs['photos']['photo']; } elseif ( isset( $results_thumbs['photos'] ) && is_array( $results_thumbs['photos'] ) ) { $photos = $results_thumbs['photos']; } if ( isset( $results_thumbs['photos'] ) && is_array( $results_thumbs['photos'] ) ) { if ( isset( $results_thumbs['photos']['page'] ) ) { $pagination_data['page'] = max( 1, intval( $results_thumbs['photos']['page'] ) ); } if ( isset( $results_thumbs['photos']['pages'] ) ) { $pagination_data['total_pages'] = intval( $results_thumbs['photos']['pages'] ); $pagination_data['has_more'] = ( $pagination_data['page'] < $pagination_data['total_pages'] ); } if ( isset( $results_thumbs['photos']['total'] ) ) { $pagination_data['total'] = intval( $results_thumbs['photos']['total'] ); } } foreach ( $photos as $item ) { $server = isset( $item['server'] ) ? $item['server'] : ''; $id = isset( $item['id'] ) ? $item['id'] : ''; $secret = isset( $item['secret'] ) ? $item['secret'] : ''; $large_url = ''; $thumb_url = ''; if ( $server && $id && $secret ) { $base_url = 'https://live.staticflickr.com/' . $server . '/' . $id . '_' . $secret; $large_url = $base_url . '_b.jpg'; $thumb_url = $base_url . '_q.jpg'; } if ( empty( $large_url ) && isset( $item['url'] ) ) { $large_url = $item['url']; } if ( empty( $thumb_url ) ) { $thumb_url = $large_url; } $normalized_images[] = array( 'url' => $large_url, 'thumb' => $thumb_url, 'title' => isset( $item['title'] ) ? $item['title'] : '', 'alt' => isset( $item['title'] ) ? $item['title'] : '', 'caption' => isset( $item['owner'] ) ? $item['owner'] : '', 'photo_id' => $id, ); } } elseif (isset($results_thumbs['items']) && is_array($results_thumbs['items'])) { // Google Custom Search format if ( 'google_image' === $bank ) { $request_info = array(); if ( isset( $results_thumbs['queries']['request'][0] ) ) { $request_info = $results_thumbs['queries']['request'][0]; } $start_index = isset( $request_info['startIndex'] ) ? intval( $request_info['startIndex'] ) : 1; $count_per_page = isset( $request_info['count'] ) ? intval( $request_info['count'] ) : count( $results_thumbs['items'] ); if ( $count_per_page <= 0 ) { $count_per_page = max( 1, count( $results_thumbs['items'] ) ); } $current_page = ( $count_per_page > 0 ) ? intval( floor( max( 0, $start_index - 1 ) / $count_per_page ) + 1 ) : $page; $pagination_data['page'] = max( 1, $current_page ); $total_results = null; if ( isset( $results_thumbs['searchInformation']['totalResults'] ) ) { $total_results = intval( $results_thumbs['searchInformation']['totalResults'] ); $pagination_data['total'] = $total_results; } if ( null !== $total_results && $count_per_page > 0 ) { $max_considered = min( $total_results, 100 ); $pagination_data['total_pages'] = (int) ceil( $max_considered / $count_per_page ); } $pagination_data['has_more'] = ! empty( $results_thumbs['queries']['nextPage'] ); } foreach ($results_thumbs['items'] as $item) { $large_url = ''; if (isset($item['pagemap']['cse_image'][0]['src']) && '' !== $item['pagemap']['cse_image'][0]['src']) { $large_url = $item['pagemap']['cse_image'][0]['src']; } elseif (isset($item['link']) && '' !== $item['link']) { $large_url = $item['link']; } if ('' === $large_url) { continue; } $thumb_url = ''; if (isset($item['pagemap']['cse_thumbnail'][0]['src']) && '' !== $item['pagemap']['cse_thumbnail'][0]['src']) { $thumb_url = $item['pagemap']['cse_thumbnail'][0]['src']; } elseif (isset($item['image']['thumbnailLink']) && '' !== $item['image']['thumbnailLink']) { $thumb_url = $item['image']['thumbnailLink']; } else { $thumb_url = $large_url; } $title = isset($item['title']) ? $item['title'] : ''; $caption = isset($item['displayLink']) ? $item['displayLink'] : ''; $normalized_images[] = array( 'url' => $large_url, 'thumb' => $thumb_url, 'title' => $title, 'alt' => $title, 'caption' => $caption, 'source' => isset($item['link']) ? $item['link'] : '' ); } } elseif (isset($results_thumbs['hits']) && is_array($results_thumbs['hits'])) { // Pixabay format if ( 'pixabay' === $bank ) { $total_hits = isset( $results_thumbs['totalHits'] ) ? intval( $results_thumbs['totalHits'] ) : null; // Pixabay API does not return a per-page value in responses. // Keep this in sync with the request (ALLSI_Source_Pixabay::build_query_args uses per_page=20). $requested_per_page = 20; if ( isset( $results_thumbs['per_page'] ) ) { $requested_per_page = intval( $results_thumbs['per_page'] ); } if ( $requested_per_page <= 0 ) { $requested_per_page = 20; } $pagination_data['page'] = $page; if ( null !== $total_hits ) { $pagination_data['total'] = $total_hits; if ( $requested_per_page > 0 ) { $total_pages = (int) ceil( $total_hits / $requested_per_page ); $pagination_data['total_pages'] = $total_pages; $pagination_data['has_more'] = ( $page < $total_pages ); } } else { $pagination_data['has_more'] = ( count( $results_thumbs['hits'] ) >= $requested_per_page ); } } foreach ($results_thumbs['hits'] as $item) { $normalized_images[] = array( 'url' => isset($item['largeImageURL']) ? $item['largeImageURL'] : '', 'thumb' => isset($item['webformatURL']) ? $item['webformatURL'] : '', 'title' => isset($item['tags']) ? $item['tags'] : '', 'alt' => isset($item['tags']) ? $item['tags'] : '', 'caption' => isset($item['user']) ? $item['user'] : '' ); } } elseif (isset($results_thumbs['results']) && is_array($results_thumbs['results'])) { // Openverse, Unsplash results format if ( 'unsplash' === $bank ) { if ( isset( $results_thumbs['total'] ) ) { $pagination_data['total'] = intval( $results_thumbs['total'] ); } if ( isset( $results_thumbs['total_pages'] ) ) { $total_pages = intval( $results_thumbs['total_pages'] ); $pagination_data['total_pages'] = $total_pages; $pagination_data['has_more'] = ( $page < $total_pages ); } } elseif ( in_array( $bank, array( 'cc_search', 'openverse' ), true ) ) { if ( isset( $results_thumbs['page'] ) ) { $pagination_data['page'] = max( 1, intval( $results_thumbs['page'] ) ); } $total_results = null; if ( isset( $results_thumbs['result_count'] ) ) { $total_results = intval( $results_thumbs['result_count'] ); } elseif ( isset( $results_thumbs['count'] ) ) { $total_results = intval( $results_thumbs['count'] ); } if ( null !== $total_results ) { $pagination_data['total'] = $total_results; } if ( isset( $results_thumbs['page_count'] ) ) { $pagination_data['total_pages'] = intval( $results_thumbs['page_count'] ); $pagination_data['has_more'] = ( $pagination_data['page'] < $pagination_data['total_pages'] ); } else { $pagination_data['has_more'] = ! empty( $results_thumbs['next'] ); } } foreach ($results_thumbs['results'] as $item) { $normalized_images[] = array( 'url' => isset($item['urls']['regular']) ? $item['urls']['regular'] : (isset($item['url']) ? $item['url'] : ''), 'thumb' => isset($item['urls']['small']) ? $item['urls']['small'] : (isset($item['url']) ? $item['url'] : ''), 'title' => isset($item['alt_description']) ? $item['alt_description'] : (isset($item['title']) ? $item['title'] : ''), 'alt' => isset($item['alt_description']) ? $item['alt_description'] : (isset($item['title']) ? $item['title'] : ''), 'caption' => isset($item['user']['name']) ? $item['user']['name'] : (isset($item['creator']) ? $item['creator'] : '') ); } } elseif (isset($results_thumbs['photos']) && is_array($results_thumbs['photos'])) { // Pexels format if ( 'pexels' === $bank ) { $current_page = isset( $results_thumbs['page'] ) ? max( 1, intval( $results_thumbs['page'] ) ) : $page; $per_page = isset( $results_thumbs['per_page'] ) ? intval( $results_thumbs['per_page'] ) : count( $results_thumbs['photos'] ); $total_results = isset( $results_thumbs['total_results'] ) ? intval( $results_thumbs['total_results'] ) : null; $pagination_data['page'] = $current_page; if ( null !== $total_results ) { $pagination_data['total'] = $total_results; if ( $per_page > 0 ) { $pagination_data['total_pages'] = (int) ceil( $total_results / $per_page ); } } if ( ! empty( $results_thumbs['next_page'] ) ) { $pagination_data['has_more'] = true; } elseif ( isset( $pagination_data['total_pages'] ) ) { $pagination_data['has_more'] = ( $current_page < $pagination_data['total_pages'] ); } else { $pagination_data['has_more'] = ! empty( $results_thumbs['photos'] ); } } foreach ($results_thumbs['photos'] as $item) { $normalized_images[] = array( 'url' => isset($item['src']['large2x']) ? $item['src']['large2x'] : '', 'thumb' => isset($item['src']['tiny']) ? $item['src']['tiny'] : '', 'title' => isset($item['alt']) ? $item['alt'] : '', 'alt' => isset($item['alt']) ? $item['alt'] : '', 'caption' => isset($item['photographer']) ? $item['photographer'] : '' ); } } elseif ( 'stability' === $bank && isset( $results_thumbs['image'] ) ) { $stability_options = isset( $bank_options['stability'] ) ? $bank_options['stability'] : array(); $format = isset( $stability_options['output_format'] ) ? $stability_options['output_format'] : 'jpeg'; $mime_map = array( 'jpeg' => 'image/jpeg', 'jpg' => 'image/jpeg', 'png' => 'image/png', 'webp' => 'image/webp', ); $mime = isset( $mime_map[ $format ] ) ? $mime_map[ $format ] : 'image/jpeg'; $data_uri = 'data:' . $mime . ';base64,' . $results_thumbs['image']; $normalized_images[] = array( 'url' => $data_uri, 'thumb' => $data_uri, 'title' => $search_term, 'alt' => $search_term, 'caption' => __( 'Generated with Stability AI', 'all-sources-images' ), 'mime' => $mime, 'is_data' => true, ); } elseif ( 'gemini' === $bank && isset( $results_thumbs['candidates'] ) && is_array( $results_thumbs['candidates'] ) ) { foreach ( $results_thumbs['candidates'] as $candidate ) { if ( empty( $candidate['content']['parts'] ) || ! is_array( $candidate['content']['parts'] ) ) { continue; } foreach ( $candidate['content']['parts'] as $part ) { $inline_data = array(); if ( isset( $part['inline_data'] ) && is_array( $part['inline_data'] ) ) { $inline_data = $part['inline_data']; } elseif ( isset( $part['inlineData'] ) && is_array( $part['inlineData'] ) ) { $inline_data = $part['inlineData']; } if ( empty( $inline_data['data'] ) ) { continue; } $mime = 'image/png'; if ( ! empty( $inline_data['mime_type'] ) ) { $mime = $inline_data['mime_type']; } elseif ( ! empty( $inline_data['mimeType'] ) ) { $mime = $inline_data['mimeType']; } $data_uri = 'data:' . $mime . ';base64,' . $inline_data['data']; $normalized_images[] = array( 'url' => $data_uri, 'thumb' => $data_uri, 'title' => $search_term, 'alt' => $search_term, 'caption' => __( 'Generated with Google Gemini', 'all-sources-images' ), 'mime' => $mime, 'is_data' => true, ); } } } elseif ( 'workers_ai' === $bank && is_array( $results_thumbs ) ) { $image_payload = ''; if ( isset( $results_thumbs['result']['image'] ) ) { $image_payload = $results_thumbs['result']['image']; } elseif ( isset( $results_thumbs['result']['images'][0] ) ) { $image_payload = $results_thumbs['result']['images'][0]; } elseif ( isset( $results_thumbs['result']['output'][0] ) ) { $image_payload = $results_thumbs['result']['output'][0]; } if ( is_string( $image_payload ) && $image_payload !== '' ) { $mime = 'image/png'; $data_uri = $image_payload; if ( 0 !== strpos( $image_payload, 'data:' ) ) { $data_uri = 'data:' . $mime . ';base64,' . $image_payload; } else { if ( preg_match( '/^data:([^;]+);base64,/', $image_payload, $matches ) ) { $mime = $matches[1]; } } $normalized_images[] = array( 'url' => $data_uri, 'thumb' => $data_uri, 'title' => $search_term, 'alt' => $search_term, 'caption' => __( 'Generated with Cloudflare Workers AI', 'all-sources-images' ), 'mime' => $mime, 'is_data' => true, ); } } elseif ( 'replicate' === $bank && isset( $results_thumbs['output'] ) ) { // Replicate format: {"output": ["url1", "url2", ...]} or {"output": "url"} $outputs = is_array( $results_thumbs['output'] ) ? $results_thumbs['output'] : array( $results_thumbs['output'] ); $model_name = isset( $results_thumbs['model'] ) ? $results_thumbs['model'] : 'Replicate'; foreach ( $outputs as $output_url ) { if ( ! is_string( $output_url ) || '' === $output_url ) { continue; } // translators: %s is the AI model name used on Replicate. $caption = sprintf( __( 'Generated with %s on Replicate', 'all-sources-images' ), $model_name ); $normalized_images[] = array( 'url' => $output_url, 'thumb' => $output_url, 'title' => $search_term, 'alt' => $search_term, 'caption' => $caption, ); } } elseif ( 'dallev1' === $bank && isset( $results_thumbs['data'] ) && is_array( $results_thumbs['data'] ) ) { // DALL-E format: {"data": [{"url": "...", "revised_prompt": "..."}]} foreach ( $results_thumbs['data'] as $dalle_item ) { if ( empty( $dalle_item['url'] ) ) { continue; } $alt_text = isset( $dalle_item['revised_prompt'] ) ? $dalle_item['revised_prompt'] : $search_term; $normalized_images[] = array( 'url' => $dalle_item['url'], 'thumb' => $dalle_item['url'], 'title' => $search_term, 'alt' => $alt_text, 'caption' => __( 'Generated with DALL-E', 'all-sources-images' ), ); } } if ( 'gemini' === $bank && ! empty( $results_thumbs['candidates'] ) && empty( $normalized_images ) ) { $first_part_keys = array(); if ( isset( $results_thumbs['candidates'][0]['content']['parts'][0] ) && is_array( $results_thumbs['candidates'][0]['content']['parts'][0] ) ) { $first_part_keys = array_keys( $results_thumbs['candidates'][0]['content']['parts'][0] ); } ALLSI_log( array( 'candidate_count' => is_array( $results_thumbs['candidates'] ) ? count( $results_thumbs['candidates'] ) : 0, 'first_part_keys' => $first_part_keys, ), 'GUTENBERG_BLOCK_GEMINI_EMPTY_NORMALIZATION' ); } // Universal response format $response_data = array( 'images' => $normalized_images, 'index' => $index, 'count' => count($normalized_images), 'pagination' => $pagination_data, 'bank' => $bank, ); ALLSI_log( 'Search successful, returning ' . count($normalized_images) . ' normalized images', 'GUTENBERG_BLOCK_SUCCESS' ); wp_send_json_success( $response_data ); } } /** * Translate search term AJAX endpoint * Called once by frontend before searching multiple image banks * * @since 6.2.0 */ public function ALLSI_translate_search_ajax() { check_ajax_referer( 'ALLSI_gutenberg_block', 'nonce' ); if ( ! current_user_can( 'edit_posts' ) ) { wp_send_json_error( __( 'Permission denied.', 'all-sources-images' ) ); } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified above with check_ajax_referer if ( ! isset( $_GET['search'] ) ) { wp_send_json_error( 'Missing search parameter' ); return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified above with check_ajax_referer $search_term = sanitize_text_field( wp_unslash( $_GET['search'] ) ); $original_term = $search_term; $was_translated = false; // Check if translation is enabled $block_settings = wp_parse_args( get_option( 'ALLSI_plugin_block_settings' ), $this->ALLSI_default_options_block_settings( TRUE ) ); if ( ! empty( $block_settings['translation_EN'] ) && $block_settings['translation_EN'] == 'true' ) { // Use configured source language or auto-detect from WordPress if ( ! empty( $block_settings['source_lang'] ) ) { $source_lang = $block_settings['source_lang']; } else { $wp_lang = get_bloginfo( 'language' ); $source_lang = substr( $wp_lang, 0, 2 ); } if ( $source_lang !== 'en' && ! empty( $search_term ) ) { $REST_generation = new All_Sources_Images_Generation( $this->plugin_name, $this->version ); $translated_term = $REST_generation->ALLSI_translate_text( $search_term, $source_lang, 'en' ); if ( $translated_term !== false && ! empty( $translated_term ) ) { ALLSI_log( array( 'original_search' => $search_term, 'translated_search' => $translated_term, 'source_lang' => $source_lang, ), 'TRANSLATE_SEARCH_AJAX' ); $search_term = $translated_term; $was_translated = true; } } } wp_send_json_success( array( 'original' => $original_term, 'translated' => $search_term, 'was_translated' => $was_translated, ) ); } /** * Downloading image for MPT Gutenberg Block * * @since 5.0.0 */ public function ALLSI_block_downloading_image() { // Check the nonce check_ajax_referer( 'ALLSI_gutenberg_block', 'nonce' ); if ( ! current_user_can( 'upload_files' ) ) { wp_send_json_error( __( 'Permission denied.', 'all-sources-images' ) ); } $raw_url_image = filter_input( INPUT_POST, 'url_image', FILTER_UNSAFE_RAW ); $raw_url_image = is_string( $raw_url_image ) ? $raw_url_image : ''; if ( 0 === strpos( $raw_url_image, 'data:image' ) ) { $url_image = $raw_url_image; } else { $url_image = esc_url_raw( $raw_url_image ); } $search_term_raw = filter_input( INPUT_POST, 'search_term', FILTER_UNSAFE_RAW ); $bank_raw = filter_input( INPUT_POST, 'bank', FILTER_UNSAFE_RAW ); $alt_raw = filter_input( INPUT_POST, 'alt_image', FILTER_UNSAFE_RAW ); $raw_caption = filter_input( INPUT_POST, 'caption_image', FILTER_UNSAFE_RAW ); $title_image_raw = filter_input( INPUT_POST, 'title_image', FILTER_UNSAFE_RAW ); $file_name_raw = filter_input( INPUT_POST, 'file_name', FILTER_UNSAFE_RAW ); $search_term = is_string( $search_term_raw ) ? sanitize_text_field( $search_term_raw ) : 'image'; $bank = is_string( $bank_raw ) ? sanitize_text_field( $bank_raw ) : ''; $alt = is_string( $alt_raw ) ? sanitize_text_field( $alt_raw ) : ''; $caption = is_string( $raw_caption ) ? wp_kses_post( $raw_caption ) : ''; $title_image = is_string( $title_image_raw ) ? sanitize_text_field( $title_image_raw ) : ''; $file_name_raw = is_string( $file_name_raw ) ? $file_name_raw : ''; $file_name_base = sanitize_file_name( $file_name_raw ); if ( '' !== $file_name_base && false !== strpos( $file_name_base, '.' ) ) { $file_name_base = sanitize_file_name( pathinfo( $file_name_base, PATHINFO_FILENAME ) ); } $post_id_raw = filter_input( INPUT_POST, 'post_id', FILTER_UNSAFE_RAW ); $id_fallback_raw = filter_input( INPUT_POST, 'id', FILTER_UNSAFE_RAW ); $post_id = is_string( $post_id_raw ) ? intval( $post_id_raw ) : 0; if ( ! $post_id && is_string( $id_fallback_raw ) ) { $post_id = intval( $id_fallback_raw ); } ALLSI_log( array( 'url' => $url_image, 'post_id' => $post_id, 'bank' => $bank, 'search' => $search_term, ), 'GUTENBERG_DOWNLOAD_START' ); $file_array = array(); $is_data_uri = ( 0 === strpos( $url_image, 'data:image' ) ); $tmp = ''; if ( $is_data_uri ) { if ( preg_match( '/^data:(image\/[a-zA-Z0-9.+-]+);base64,(.*)$/', $url_image, $matches ) ) { $mime_type = $matches[1]; $base64_data = str_replace( ' ', '+', $matches[2] ); $decoded = base64_decode( $base64_data ); if ( false === $decoded ) { wp_send_json_error( array( 'erreur' => 'Invalid image data.' ) ); return; } $tmp = wp_tempnam( 'stability_block' ); if ( false === $tmp ) { wp_send_json_error( array( 'erreur' => 'Unable to create temporary file.' ) ); return; } // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- Saving decoded base64 image to temp file. file_put_contents( $tmp, $decoded ); } else { wp_send_json_error( array( 'erreur' => 'Invalid data URI.' ) ); return; } } else { $tmp = download_url( $url_image ); // Check for error if ( is_wp_error( $tmp ) ) { ALLSI_log( array( 'message' => $tmp->get_error_message(), 'url' => $url_image, ), 'GUTENBERG_DOWNLOAD_ERROR' ); wp_send_json_error( array( 'erreur' => $tmp->get_error_message(), ) ); return; } $mime_type = ''; } $allowed_extensions = array( 'jpg', 'jpeg', 'png', 'gif', 'webp' ); $mime_map = array( 'image/jpeg' => 'jpg', 'image/jpg' => 'jpg', 'image/pjpeg'=> 'jpg', 'image/png' => 'png', 'image/gif' => 'gif', 'image/webp' => 'webp', ); $extension = ''; $detected_mime = wp_get_image_mime( $tmp ); if ( $detected_mime && isset( $mime_map[ $detected_mime ] ) ) { $mime_type = $detected_mime; $extension = $mime_map[ $detected_mime ]; } if ( empty( $extension ) ) { $parsed_path = wp_parse_url( $url_image, PHP_URL_PATH ); if ( $parsed_path ) { $path_info = pathinfo( $parsed_path ); if ( isset( $path_info['extension'] ) ) { $maybe_ext = strtolower( $path_info['extension'] ); if ( in_array( $maybe_ext, $allowed_extensions, true ) ) { $extension = $maybe_ext; } } } } if ( empty( $extension ) ) { $extension = 'jpg'; } $sanitized_search = sanitize_file_name( $search_term ); if ( empty( $sanitized_search ) ) { $sanitized_search = 'allsi-image'; } $final_file_base = ! empty( $file_name_base ) ? $file_name_base : $sanitized_search; $file_array['name'] = $final_file_base . '.' . $extension; // Filename $file_array['tmp_name'] = $tmp; if ( $mime_type ) { $file_array['type'] = $mime_type; } // Check to ensure the image is valid $check = wp_check_filetype_and_ext( $file_array['tmp_name'], $file_array['name'] ); if ( $check["ext"] == "" ) { wp_delete_file( $file_array['tmp_name'] ); ALLSI_log( array( 'reason' => 'Invalid image after download', 'mime' => $mime_type, 'url' => $url_image, ), 'GUTENBERG_DOWNLOAD_ERROR' ); wp_send_json_error( array( 'erreur' => 'Invalid Image', ) ); return; } // wp_handle_sideload to download the image. $uploaded_file = wp_handle_sideload( $file_array, array( 'test_form' => false, ) ); if ( isset( $uploaded_file['error'] ) ) { wp_delete_file( $file_array['tmp_name'] ); ALLSI_log( array( 'reason' => $uploaded_file['error'], 'file' => $file_array['name'], ), 'GUTENBERG_DOWNLOAD_ERROR' ); wp_send_json_error( array( 'error' => $uploaded_file['error'], ) ); return; } // Insert image into the media library. $wp_upload_dir = wp_upload_dir(); $default_title = preg_replace( '/\.[^.]+$/', '', basename( $uploaded_file['file'] ) ); $attachment = array( 'guid' => $wp_upload_dir['url'] . '/' . basename( $uploaded_file['file'] ), 'post_mime_type' => $check['type'], 'post_title' => ( ! empty( $title_image ) ? $title_image : $default_title ), 'post_content' => '', 'post_status' => 'inherit', ); $attach_id = wp_insert_attachment( $attachment, $uploaded_file['file'], $post_id ); // Translate ALT text if enabled in block settings $block_settings = wp_parse_args( get_option( 'ALLSI_plugin_block_settings' ), $this->ALLSI_default_options_block_settings( TRUE ) ); if ( ! empty( $alt ) && ! empty( $block_settings['translate_alt'] ) && $block_settings['translate_alt'] == 'true' ) { $target_lang = ( ! empty( $block_settings['translate_alt_lang'] ) ? $block_settings['translate_alt_lang'] : '' ); // Only translate if target language is set and not English (ALT usually comes in English from image banks) if ( ! empty( $target_lang ) && $target_lang !== 'en' ) { $REST_generation = new All_Sources_Images_Generation( $this->plugin_name, $this->version ); $translated_alt = $REST_generation->ALLSI_translate_text( $alt, 'en', $target_lang ); if ( $translated_alt !== false && ! empty( $translated_alt ) ) { ALLSI_log( array( 'original_alt' => $alt, 'translated_alt' => $translated_alt, 'target_lang' => $target_lang, ), 'GUTENBERG_DOWNLOAD_ALT_TRANSLATED' ); $alt = $translated_alt; } } } // Add alt text for image update_post_meta( $attach_id, '_wp_attachment_image_alt', $alt ); // Add caption text for image if ( !empty( $caption ) ) { wp_update_post( array( 'ID' => $attach_id, 'post_excerpt' => $caption, ) ); } $attach_data = wp_generate_attachment_metadata( $attach_id, $uploaded_file['file'] ); wp_update_attachment_metadata( $attach_id, $attach_data ); $url_media = wp_get_attachment_url( $attach_id ); ALLSI_log( array( 'attachment_id' => $attach_id, 'url' => $url_media, 'post_id' => $post_id, ), 'GUTENBERG_DOWNLOAD_SUCCESS' ); wp_send_json_success( array( 'url_media' => $url_media, 'alt_image' => $alt, 'caption_image' => $caption, 'id_media' => $attach_id, ) ); } /** * Extending http timeout for wp_remote_request() * * @since 5.0.0 */ public function ALLSI_custom_http_request_timeout() { return 40; // 40 seconds } /** * Adds a settings link to the plugins page * * @since 2.0.5 */ public function ALLSI_add_link_parameters( $links ) { $settings_link = '' . esc_html__( 'Settings', 'all-sources-images' ) . ''; array_push( $links, $settings_link ); return $links; } /** * Adds notice to ask for a review * * @since 5.2.6 */ /** * Upgrade plugin : options updated * * @since 6.0.0 */ public function ALLSI_migration() { // Retrieve existing options $optionstomove = get_option( 'ALLSI_plugin_main_settings' ); $options_banks = get_option( 'ALLSI_plugin_banks_settings' ); // Check if old values exist (migration needed) if ( $optionstomove && !isset( $optionstomove['image_block'][1] ) ) { // Fix 6.0.1 : Add image bank in options if ( isset( $options_banks['api_chosen_auto'] ) ) { $ar_bank_auto = array($options_banks['api_chosen_auto']); $default_bank = reset( $ar_bank_auto[0] ); } else { $default_bank = 'pixabay'; } // Move old values to the new structure $optionstomove['image_block'][1] = array( 'image_location' => ( isset( $optionstomove['image_location'] ) ? $optionstomove['image_location'] : 'featured' ), 'image_custom_location_placement' => ( isset( $optionstomove['image_custom_location_placement'] ) ? $optionstomove['image_custom_location_placement'] : '' ), 'image_custom_location_position' => ( isset( $optionstomove['image_custom_location_position'] ) ? $optionstomove['image_custom_location_position'] : '' ), 'image_custom_location_tag' => ( isset( $optionstomove['image_custom_location_tag'] ) ? $optionstomove['image_custom_location_tag'] : '' ), 'image_custom_image_size' => ( isset( $optionstomove['image_custom_image_size'] ) ? $optionstomove['image_custom_image_size'] : '' ), 'api_chosen' => $default_bank, 'based_on' => ( isset( $optionstomove['based_on'] ) ? $optionstomove['based_on'] : 'title' ), 'title_selection' => ( isset( $optionstomove['title_selection'] ) ? $optionstomove['title_selection'] : 'full_title' ), 'title_length' => ( isset( $optionstomove['title_length'] ) ? $optionstomove['title_length'] : '' ), 'text_analyser_lang' => ( isset( $optionstomove['text_analyser_lang'] ) ? $optionstomove['text_analyser_lang'] : '' ), 'tags' => ( isset( $optionstomove['tags'] ) ? $optionstomove['tags'] : '' ), 'categories' => ( isset( $optionstomove['categories'] ) ? $optionstomove['categories'] : '' ), 'custom_field' => ( isset( $optionstomove['custom_field'] ) ? $optionstomove['custom_field'] : '' ), 'custom_request' => ( isset( $optionstomove['custom_request'] ) ? $optionstomove['custom_request'] : '' ), 'openai_extractor_apikey' => ( isset( $optionstomove['openai_extractor_apikey'] ) ? $optionstomove['openai_extractor_apikey'] : '' ), 'openai_number_of_keywords' => ( isset( $optionstomove['openai_number_of_keywords'] ) ? $optionstomove['openai_number_of_keywords'] : '' ), 'translation_EN' => ( isset( $optionstomove['translation_EN'] ) ? $optionstomove['translation_EN'] : '' ), 'selected_image' => ( isset( $optionstomove['selected_image'] ) ? $optionstomove['selected_image'] : 'first_result' ), ); // Remove old keys to prevent redundancy (optional) $keys_to_remove = array( 'image_location', 'image_custom_location_placement', 'image_custom_location_position', 'image_custom_location_tag', 'image_custom_image_size', 'based_on', 'title_selection', 'title_length', 'text_analyser_lang', 'tags', 'categories', 'custom_field', 'custom_request', 'openai_extractor_apikey', 'openai_number_of_keywords', 'translation_EN', 'selected_image' ); foreach ( $keys_to_remove as $key ) { unset($optionstomove[$key]); } // Save updated options update_option( 'ALLSI_plugin_main_settings', $optionstomove ); } elseif ( isset( $optionstomove['image_block'][1] ) && $optionstomove && !isset( $optionstomove['image_block'][1]['api_chosen'] ) ) { // Fix 6.0.1 : Add image bank in options if ( isset( $options_banks['api_chosen_auto'] ) ) { $ar_bank_auto = array($options_banks['api_chosen_auto']); $default_bank = reset( $ar_bank_auto[0] ); } else { $default_bank = 'pixabay'; } // Retrieve current options $current_options = get_option( 'ALLSI_plugin_main_settings', array() ); // Update only the necessary values $existing_block = isset( $current_options['image_block'][1] ) ? $current_options['image_block'][1] : array(); $current_options['image_block'][1] = array_merge( $existing_block, // Initialize as an empty array if not set array( 'api_chosen' => $default_bank, ) ); // Save the updated options update_option( 'ALLSI_plugin_main_settings', $current_options ); } else { } } /** * Sanitize main settings - Merge with existing values to preserve settings from other tabs * * When saving from Image Placement tab, we don't want to lose Post-Processing settings and vice versa. * This callback merges the new values with existing ones based on the _saving_tab field. * * @since 6.2.0 * @param array $input Raw input from settings form * @return array Merged settings */ public function ALLSI_sanitize_main_settings( $input ) { // Get existing settings $existing = get_option( 'ALLSI_plugin_main_settings', array() ); // If input is empty or not an array, return existing if ( empty( $input ) || ! is_array( $input ) ) { return $existing; } // First, sanitize all input values $input = $this->ALLSI_sanitize_main_settings_values( $input ); // Fields that belong to Post-Processing tab $post_processing_fields = array( 'image_filename', 'rewrite_featured', 'image_reuse', 'image_flip', 'image_crop', 'enable_alt', 'alt_from', 'translate_alt', 'translate_alt_lang', 'enable_caption', 'caption_from', ); // Fields that belong to Image Placement tab $image_placement_fields = array( 'image_block', 'image_custom_post_type', ); // Check for explicit tab indicator (added as hidden field in forms) $saving_tab = isset( $input['_saving_tab'] ) ? $input['_saving_tab'] : null; // Remove the _saving_tab field as it's not a real setting unset( $input['_saving_tab'] ); // Merge strategy based on which tab is being saved if ( $saving_tab === 'post_processing' ) { // Saving Post-Processing: preserve ALL Image Placement fields from existing foreach ( $image_placement_fields as $field ) { if ( isset( $existing[ $field ] ) ) { $input[ $field ] = $existing[ $field ]; } } } elseif ( $saving_tab === 'image_placement' ) { // Saving Image Placement: preserve ALL Post-Processing fields from existing foreach ( $post_processing_fields as $field ) { if ( isset( $existing[ $field ] ) ) { $input[ $field ] = $existing[ $field ]; } } } else { // Fallback: try to detect which tab based on fields present (for legacy/other forms) $saving_post_processing = false; $saving_image_placement = false; foreach ( $post_processing_fields as $field ) { if ( isset( $input[ $field ] ) ) { $saving_post_processing = true; break; } } foreach ( $image_placement_fields as $field ) { if ( isset( $input[ $field ] ) ) { $saving_image_placement = true; break; } } if ( $saving_post_processing && ! $saving_image_placement ) { foreach ( $image_placement_fields as $field ) { if ( isset( $existing[ $field ] ) && ! isset( $input[ $field ] ) ) { $input[ $field ] = $existing[ $field ]; } } } elseif ( $saving_image_placement && ! $saving_post_processing ) { foreach ( $post_processing_fields as $field ) { if ( isset( $existing[ $field ] ) && ! isset( $input[ $field ] ) ) { $input[ $field ] = $existing[ $field ]; } } } } // Also preserve any other fields that might exist but weren't in the form foreach ( $existing as $key => $value ) { if ( ! isset( $input[ $key ] ) ) { $input[ $key ] = $value; } } return $input; } /** * Sanitize main settings values recursively * * @since 6.2.0 * @param array $input Raw input array * @return array Sanitized array */ private function ALLSI_sanitize_main_settings_values( $input ) { if ( ! is_array( $input ) ) { return sanitize_text_field( $input ); } $sanitized = array(); foreach ( $input as $key => $value ) { $clean_key = sanitize_key( $key ); if ( is_array( $value ) ) { // Recursively sanitize nested arrays (like image_block) $sanitized[ $clean_key ] = $this->ALLSI_sanitize_main_settings_values( $value ); } else { // Sanitize based on field type switch ( $clean_key ) { case 'image_filename': case 'alt_from': case 'caption_from': case 'image_location': case 'based_on': case 'selected_image': case 'title_selection': case 'tags': case 'categories': case 'api_chosen': case 'api_chosen_2': case 'text_analyser_lang': case 'translate_alt_lang': case 'ai_prompt_style': case 'image_custom_location_placement': case 'image_custom_location_position': case 'image_custom_location_tag': case 'image_custom_image_size': case 'categories_level': $sanitized[ $clean_key ] = sanitize_text_field( $value ); break; case 'title_length': case 'openai_number_of_keywords': $sanitized[ $clean_key ] = absint( $value ); break; case 'rewrite_featured': case 'image_reuse': case 'image_flip': case 'image_crop': case 'enable_alt': case 'translate_alt': case 'enable_caption': case 'translation_en': // Boolean-like values $sanitized[ $clean_key ] = sanitize_text_field( $value ); break; case 'custom_field': case 'custom_request': case 'ai_prompt_custom_instructions': $sanitized[ $clean_key ] = sanitize_text_field( $value ); break; case 'openai_extractor_apikey': case 'ai_prompt_apikey': // API keys - sanitize but preserve $sanitized[ $clean_key ] = sanitize_text_field( $value ); break; case 'image_custom_post_type': // Array of post types if ( is_array( $value ) ) { $sanitized[ $clean_key ] = array_map( 'sanitize_key', $value ); } else { $sanitized[ $clean_key ] = sanitize_key( $value ); } break; default: $sanitized[ $clean_key ] = sanitize_text_field( $value ); break; } } } return $sanitized; } /** * Sanitize auto image settings. * * @since 6.2.0 * @param array $input Raw input from settings form. * @return array */ public function ALLSI_sanitize_auto_image_settings( $input ) { $defaults = $this->ALLSI_default_options_auto_image_settings( true ); if ( ! is_array( $input ) ) { return $defaults; } $sanitized = $defaults; $sanitized['enabled'] = ( isset( $input['enabled'] ) && 'enable' === sanitize_text_field( $input['enabled'] ) ) ? 'enable' : 'disable'; $post_types_input = array(); if ( isset( $input['post_types'] ) ) { $post_types_input = is_array( $input['post_types'] ) ? $input['post_types'] : array( $input['post_types'] ); } $post_types = array_values( array_unique( array_filter( array_map( 'sanitize_key', $post_types_input ) ) ) ); if ( ! empty( $post_types ) ) { $public_post_types = get_post_types( array( 'public' => true ), 'names' ); $post_types = array_values( array_intersect( $post_types, $public_post_types ) ); } $sanitized['post_types'] = $post_types; $term_ids_input = array(); if ( isset( $input['term_ids'] ) ) { $term_ids_input = is_array( $input['term_ids'] ) ? $input['term_ids'] : array( $input['term_ids'] ); } $sanitized['term_ids'] = array_values( array_unique( array_filter( array_map( 'absint', $term_ids_input ) ) ) ); $exclude_ids_raw = isset( $input['exclude_post_ids'] ) ? sanitize_text_field( $input['exclude_post_ids'] ) : ''; $exclude_ids = $this->ALLSI_auto_image_parse_ids( $exclude_ids_raw ); $sanitized['exclude_post_ids'] = ! empty( $exclude_ids ) ? implode( ',', $exclude_ids ) : ''; return $sanitized; } /** * Sanitize banks settings - Sanitize all fields and remove 'envato' if present (no longer working) * * @since 4.2.0 */ public function ALLSI_sanitize_banks_settings( $input ) { if ( ! is_array( $input ) ) { return array(); } $sanitized = array(); // Recursively sanitize the input array foreach ( $input as $key => $value ) { $clean_key = sanitize_key( $key ); if ( is_array( $value ) ) { // For nested arrays (like bank-specific settings) $sanitized[ $clean_key ] = $this->ALLSI_sanitize_banks_array_recursive( $value ); } else { // Sanitize based on expected field types $sanitized[ $clean_key ] = $this->ALLSI_sanitize_bank_field( $clean_key, $value ); } } // Remove 'envato' from api_chosen_auto if present if ( isset( $sanitized['api_chosen_auto'] ) && is_array( $sanitized['api_chosen_auto'] ) ) { unset( $sanitized['api_chosen_auto']['envato'] ); } // Remove 'envato' from api_chosen_manual if present if ( isset( $sanitized['api_chosen_manual'] ) && is_array( $sanitized['api_chosen_manual'] ) ) { unset( $sanitized['api_chosen_manual']['envato'] ); } // Clear api_chosen if it's 'envato' if ( isset( $sanitized['api_chosen'] ) && 'envato' === $sanitized['api_chosen'] ) { $sanitized['api_chosen'] = ''; } return $sanitized; } /** * Recursively sanitize banks array * * @since 6.2.0 * @param array $array Array to sanitize * @return array Sanitized array */ private function ALLSI_sanitize_banks_array_recursive( $array ) { if ( ! is_array( $array ) ) { return sanitize_text_field( $array ); } $sanitized = array(); foreach ( $array as $key => $value ) { $clean_key = sanitize_key( $key ); if ( is_array( $value ) ) { $sanitized[ $clean_key ] = $this->ALLSI_sanitize_banks_array_recursive( $value ); } else { $sanitized[ $clean_key ] = $this->ALLSI_sanitize_bank_field( $clean_key, $value ); } } return $sanitized; } /** * Sanitize individual bank field based on field name * * @since 6.2.0 * @param string $key Field key * @param mixed $value Field value * @return mixed Sanitized value */ private function ALLSI_sanitize_bank_field( $key, $value ) { // API keys, tokens, and account IDs - sanitize as text but preserve case if ( strpos( $key, 'apikey' ) !== false || strpos( $key, 'api_key' ) !== false || strpos( $key, 'token' ) !== false || strpos( $key, 'secret' ) !== false || strpos( $key, 'account_id' ) !== false || strpos( $key, 'cxid' ) !== false ) { return sanitize_text_field( $value ); } // URLs if ( strpos( $key, 'url' ) !== false || strpos( $key, 'endpoint' ) !== false ) { return esc_url_raw( $value ); } // Specific image size fields that should be text (like 1024x1024) if ( 'imgsize' === $key || 'image_size' === $key ) { return sanitize_text_field( $value ); } // Numeric fields (but exclude imgsize which is handled above) if ( strpos( $key, 'max_results' ) !== false || strpos( $key, 'count' ) !== false || strpos( $key, 'width' ) !== false || strpos( $key, 'height' ) !== false || strpos( $key, 'min_width' ) !== false || strpos( $key, 'min_height' ) !== false || strpos( $key, 'steps' ) !== false || strpos( $key, 'seed' ) !== false || strpos( $key, 'limit' ) !== false ) { return absint( $value ); } // Boolean-like fields if ( in_array( $value, array( 'true', 'false', '1', '0', 'yes', 'no', 'on', 'off' ), true ) ) { return sanitize_text_field( $value ); } // Default: sanitize as text field return sanitize_text_field( $value ); } /** * Sanitize cron settings and schedule/unschedule WordPress cron events * * @since 6.1.7 * @param array $input Raw input from settings form * @return array Sanitized settings */ public function ALLSI_sanitize_cron_settings( $input ) { if ( ! is_array( $input ) ) { return array(); } // Sanitize input values first $sanitized = array(); foreach ( $input as $key => $value ) { $clean_key = sanitize_key( $key ); switch ( $clean_key ) { case 'enable_cron': $allowed = array( 'enable', 'disable' ); $sanitized[ $clean_key ] = in_array( $value, $allowed, true ) ? $value : 'disable'; break; case 'cron_interval_value': $sanitized[ $clean_key ] = absint( $value ); break; case 'cron_interval_unit': $allowed = array( 'minutes', 'hours', 'days' ); $sanitized[ $clean_key ] = in_array( $value, $allowed, true ) ? $value : 'minutes'; break; case 'cron_post_types': if ( is_array( $value ) ) { $sanitized[ $clean_key ] = array_map( 'sanitize_key', $value ); } else { $sanitized[ $clean_key ] = array(); } break; default: $sanitized[ $clean_key ] = sanitize_text_field( $value ); break; } } // Clear existing scheduled event $timestamp = wp_next_scheduled( 'ALLSI_cron_image_generation' ); if ( $timestamp ) { wp_unschedule_event( $timestamp, 'ALLSI_cron_image_generation' ); } // If cron is enabled, schedule new event if ( isset( $sanitized['enable_cron'] ) && $sanitized['enable_cron'] === 'enable' ) { // Convert interval to seconds $interval_value = isset( $sanitized['cron_interval_value'] ) ? $sanitized['cron_interval_value'] : 5; $interval_unit = isset( $sanitized['cron_interval_unit'] ) ? $sanitized['cron_interval_unit'] : 'minutes'; $seconds = 0; switch ( $interval_unit ) { case 'minutes': $seconds = $interval_value * 60; break; case 'hours': $seconds = $interval_value * 3600; break; case 'days': $seconds = $interval_value * 86400; break; } // Create custom interval if needed $recurrence_key = 'ALLSI_cron_' . $interval_value . '_' . $interval_unit; // Add custom interval to WordPress cron schedules add_filter( 'cron_schedules', function( $schedules ) use ( $recurrence_key, $seconds, $interval_value, $interval_unit ) { $schedules[$recurrence_key] = array( 'interval' => $seconds, /* translators: 1: interval number, 2: interval unit (minutes, hours, days) */ 'display' => sprintf( __( 'Every %1$d %2$s', 'all-sources-images' ), $interval_value, $interval_unit ), ); return $schedules; } ); // Schedule the event if ( ! wp_next_scheduled( 'ALLSI_cron_image_generation' ) ) { wp_schedule_event( time(), $recurrence_key, 'ALLSI_cron_image_generation' ); } } return $sanitized; } /** * Sanitize block settings * * @since 6.2.0 * @param array $input Raw input from settings form * @return array Sanitized settings */ public function ALLSI_sanitize_block_settings( $input ) { if ( ! is_array( $input ) ) { return array(); } $sanitized = array(); foreach ( $input as $key => $value ) { if ( is_array( $value ) ) { $sanitized[ sanitize_key( $key ) ] = array_map( 'sanitize_text_field', $value ); } else { $sanitized[ sanitize_key( $key ) ] = sanitize_text_field( $value ); } } return $sanitized; } /** * Sanitize interval settings * * @since 6.2.0 * @param array $input Raw input from settings form * @return array Sanitized settings */ public function ALLSI_sanitize_interval_settings( $input ) { if ( ! is_array( $input ) ) { return array(); } $sanitized = array(); foreach ( $input as $key => $value ) { $sanitized[ sanitize_key( $key ) ] = is_numeric( $value ) ? absint( $value ) : sanitize_text_field( $value ); } return $sanitized; } /** * Sanitize rights/permissions settings * * @since 6.2.0 * @param array $input Raw input from settings form * @return array Sanitized settings */ public function ALLSI_sanitize_rights_settings( $input ) { if ( ! is_array( $input ) ) { return array(); } $sanitized = array(); foreach ( $input as $key => $value ) { if ( is_array( $value ) ) { // For role arrays, sanitize each role name $sanitized[ sanitize_key( $key ) ] = array_map( 'sanitize_key', $value ); } else { $sanitized[ sanitize_key( $key ) ] = sanitize_text_field( $value ); } } return $sanitized; } /** * Sanitize proxy settings * * @since 6.2.0 * @param array $input Raw input from settings form * @return array Sanitized settings */ public function ALLSI_sanitize_proxy_settings( $input ) { if ( ! is_array( $input ) ) { return array(); } $sanitized = array(); $allowed_proxy_modes = array( 'legacy', 'cloudflare', 'disabled' ); $allowed_enable_values = array( 'enable', 'disable' ); foreach ( $input as $key => $value ) { switch ( $key ) { case 'enable_proxy': $enable_value = sanitize_text_field( $value ); $sanitized['enable_proxy'] = in_array( $enable_value, $allowed_enable_values, true ) ? $enable_value : 'disable'; break; case 'proxy_mode': $mode_value = sanitize_text_field( $value ); $sanitized['proxy_mode'] = in_array( $mode_value, $allowed_proxy_modes, true ) ? $mode_value : 'legacy'; break; case 'proxy_address': $address = sanitize_text_field( $value ); $sanitized['proxy_address'] = preg_replace( '#^https?://#i', '', trim( $address ) ); break; case 'proxy_port': $port = absint( $value ); $sanitized['proxy_port'] = ( $port >= 1 && $port <= 65535 ) ? $port : 80; break; case 'proxy_username': $sanitized['proxy_username'] = sanitize_text_field( $value ); break; case 'proxy_password': $sanitized['proxy_password'] = sanitize_text_field( $value ); break; case 'cloudflare_worker_url': $sanitized['cloudflare_worker_url'] = esc_url_raw( $value ); break; case 'cloudflare_token': $sanitized['cloudflare_token'] = sanitize_text_field( $value ); break; } } return $sanitized; } /** * Sanitize logs settings * * @since 6.2.0 * @param array $input Raw input from settings form * @return array Sanitized settings */ public function ALLSI_sanitize_logs_settings( $input ) { if ( ! is_array( $input ) ) { return array(); } $sanitized = array(); foreach ( $input as $key => $value ) { switch ( $key ) { case 'log_level': $allowed_levels = array( 'debug', 'info', 'warning', 'error', 'none' ); $sanitized[ $key ] = in_array( $value, $allowed_levels, true ) ? $value : 'info'; break; case 'log_retention_days': $sanitized[ $key ] = absint( $value ); break; default: $sanitized[ sanitize_key( $key ) ] = sanitize_text_field( $value ); break; } } return $sanitized; } }