Plugin Directory

Changeset 3452203


Ignore:
Timestamp:
02/02/2026 03:06:20 PM (3 weeks ago)
Author:
gkanters
Message:

Avoid fatal error when Elemtor is active

Location:
ai-translate/tags/2.2.4
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • ai-translate/tags/2.2.4/ai-translate.php

    r3451583 r3452203  
    233233}
    234234
    235 // Ensure slug map table exists early in init
    236 add_action('init', function () {
    237     \AITranslate\AI_Slugs::install_table();
    238 }, 1);
    239 
    240 // Schedule cache metadata sync cron job
    241 add_action('ai_translate_sync_cache_metadata', function () {
    242     \AITranslate\AI_Cache_Meta::sync_from_filesystem();
    243 });
    244 
    245 if (!wp_next_scheduled('ai_translate_sync_cache_metadata')) {
    246     wp_schedule_event(time(), 'hourly', 'ai_translate_sync_cache_metadata');
    247 }
    248 
    249 
    250 
    251235// Add original-style language-prefixed rewrite rules using 'lang' query var to ensure WP resolves pages via pagename
    252236// Priority 999: runs AFTER custom post types are registered (default priority 10)
    253 add_action('init', function () {
     237function ai_translate_register_rewrite_rules()
     238{
    254239    $settings = get_option('ai_translate_settings', array());
    255240    $enabled = isset($settings['enabled_languages']) && is_array($settings['enabled_languages']) ? $settings['enabled_languages'] : array();
     
    261246    }
    262247    $langs = array_filter(array_map('sanitize_key', $langs));
    263     if (empty($langs)) return;
     248    if (empty($langs)) {
     249        return;
     250    }
    264251    $regex = '(' . implode('|', array_map(function ($l) {
    265252        return preg_quote($l, '/');
     
    267254    add_rewrite_rule('^' . $regex . '/?$', 'index.php?lang=$matches[1]', 'top');
    268255   
    269     // Add rewrite rules for all public custom post types (generiek)
    270256    $public_post_types = get_post_types(array('public' => true, '_builtin' => false), 'objects');
    271257    foreach ($public_post_types as $post_type) {
     
    273259            $slug = $post_type->rewrite['slug'];
    274260            $type_name = $post_type->name;
    275             // Add explicit rule for this CPT: /{lang}/{cpt-slug}/{post-name}
    276261            add_rewrite_rule(
    277262                '^' . $regex . '/(?!wp-admin|wp-login\.php)(' . preg_quote($slug, '/') . ')/([^/]+)/?$',
     
    282267    }
    283268   
    284     // Generic rule for hierarchical paths - try to resolve without language prefix first
    285269    add_rewrite_rule('^' . $regex . '/(?!wp-admin|wp-login\.php)(.+)$', 'index.php?ai_translate_path=$matches[2]&lang=$matches[1]', 'top');
    286     // Pagination for posts index: /{lang}/page/{n}/ (added last so it sits on top due to 'top')
    287270    add_rewrite_rule('^' . $regex . '/page/([0-9]+)/?$', 'index.php?lang=$matches[1]&paged=$matches[2]', 'top');
    288 }, 999); // Priority 999: runs AFTER custom post types are registered (default priority 10)
     271}
     272
     273add_action('init', 'ai_translate_register_rewrite_rules', 999); // Priority 999: runs AFTER custom post types are registered (default priority 10)
     274
     275// Ensure slug map table exists early in init
     276add_action('init', function () {
     277    \AITranslate\AI_Slugs::install_table();
     278}, 1);
     279
     280// Schedule cache metadata sync cron job
     281add_action('ai_translate_sync_cache_metadata', function () {
     282    \AITranslate\AI_Cache_Meta::sync_from_filesystem();
     283});
     284
     285if (!wp_next_scheduled('ai_translate_sync_cache_metadata')) {
     286    wp_schedule_event(time(), 'hourly', 'ai_translate_sync_cache_metadata');
     287}
     288
     289
     290
     291// Ensure slug map table exists early in init
     292add_action('init', function () {
     293    \AITranslate\AI_Slugs::install_table();
     294}, 1);
     295
     296// Schedule cache metadata sync cron job
     297add_action('ai_translate_sync_cache_metadata', function () {
     298    \AITranslate\AI_Cache_Meta::sync_from_filesystem();
     299});
     300
     301if (!wp_next_scheduled('ai_translate_sync_cache_metadata')) {
     302    wp_schedule_event(time(), 'hourly', 'ai_translate_sync_cache_metadata');
     303}
    289304
    290305add_filter('query_vars', function ($vars) {
     
    421436register_activation_hook(__FILE__, function () {
    422437    // Ensure rules are registered before flushing
    423     do_action('init');
     438    ai_translate_register_rewrite_rules();
    424439
    425440    // Automatically set permalinks to 'post-name' if they're currently 'plain'
     
    560575 * Handles language detection and redirection according to the 4 language switcher rules.
    561576 */
     577// Check for _wp_old_slug redirects before other plugin logic (priority 1).
     578// Request path is checked first: if it matches an old slug in DB, redirect. Do not rely on is_404
     579// because themes/plugins may serve the homepage instead of 404 for unknown slugs.
     580add_action('template_redirect', function () {
     581    if (!isset($_SERVER['REQUEST_URI'])) {
     582        return;
     583    }
     584    $request_uri = (string) $_SERVER['REQUEST_URI'];
     585    $request_path = parse_url($request_uri, PHP_URL_PATH);
     586    if (!is_string($request_path) || $request_path === '' || $request_path === '/') {
     587        return;
     588    }
     589    // Skip if URL has language prefix (let AI Translate handle those)
     590    if (preg_match('#^/([a-z]{2})(?:/|$)#i', $request_path)) {
     591        return;
     592    }
     593    // Single-segment path (e.g. /programmeren-met-een-ai-agen/) – might be an old slug
     594    $path_parts = array_filter(explode('/', trim($request_path, '/')));
     595    if (empty($path_parts)) {
     596        return;
     597    }
     598    $slug = end($path_parts);
     599    global $wpdb;
     600    $post_id = $wpdb->get_var($wpdb->prepare(
     601        "SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = '_wp_old_slug' AND meta_value = %s LIMIT 1",
     602        $slug
     603    ));
     604    if (!$post_id) {
     605        return;
     606    }
     607    $post = get_post($post_id);
     608    if (!$post || $post->post_status !== 'publish') {
     609        return;
     610    }
     611    $permalink = get_permalink($post);
     612    if ($permalink) {
     613        wp_safe_redirect($permalink, 301);
     614        exit;
     615    }
     616}, 1);
     617
    562618add_action('template_redirect', function () {
    563619    // Handle ai_translate_path query var for custom URL resolution
     
    13911447
    13921448/**
     1449 * Resolve _wp_old_slug in parse_request and redirect to canonical URL before 404 is set.
     1450 * Priority 0 so this runs before any 404 redirect plugin (which would send 404s to homepage).
     1451 */
     1452add_action('parse_request', function ($wp) {
     1453    if (is_admin() || wp_doing_ajax() || ai_translate_is_xml_request()) {
     1454        return;
     1455    }
     1456    $reqUriRaw = isset($_SERVER['REQUEST_URI']) ? (string) $_SERVER['REQUEST_URI'] : '';
     1457    if (strpos($reqUriRaw, '%25') !== false) {
     1458        $reqUriRaw = urldecode($reqUriRaw);
     1459    }
     1460    $request_path = wp_parse_url($reqUriRaw, PHP_URL_PATH) ?: '/';
     1461    if (!is_string($request_path) || $request_path === '' || $request_path === '/') {
     1462        return;
     1463    }
     1464    if (preg_match('#^/([a-z]{2})(?:/|$)#i', $request_path)) {
     1465        return;
     1466    }
     1467    $path_parts = array_filter(explode('/', trim($request_path, '/')));
     1468    if (empty($path_parts)) {
     1469        return;
     1470    }
     1471    $slug = end($path_parts);
     1472    global $wpdb;
     1473    $post_id = $wpdb->get_var($wpdb->prepare(
     1474        "SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = '_wp_old_slug' AND meta_value = %s LIMIT 1",
     1475        $slug
     1476    ));
     1477    if (!$post_id) {
     1478        return;
     1479    }
     1480    $post = get_post($post_id);
     1481    if (!$post || $post->post_status !== 'publish') {
     1482        return;
     1483    }
     1484    $permalink = get_permalink($post);
     1485    if ($permalink) {
     1486        wp_safe_redirect($permalink, 301);
     1487        exit;
     1488    }
     1489}, 0);
     1490
     1491/**
    13931492 * Ensure language-only URLs (e.g., /en/, /de/) route to the site's front page.
    13941493 */
  • ai-translate/tags/2.2.4/includes/admin-page.php

    r3451583 r3452203  
    55// Load the core class if it doesn't exist yet.
    66// Since this file is in the "includes" folder, the core class is at the same level.
    7 if (! class_exists('AI_Translate_Core')) {
     7if (! \class_exists('AI_Translate_Core')) {
    88    $core_class_file = plugin_dir_path(__FILE__) . 'class-ai-translate-core.php';
    99    if (file_exists($core_class_file)) {
     
    306306
    307307add_action('wp_ajax_ai_translate_delete_cache_file', __NAMESPACE__ . '\\ajax_delete_cache_file');
     308
     309/**
     310 * Return route_id used for cache key when warming cache. Must match class-ai-ob current_route_id() for the same URL.
     311 *
     312 * @param int $post_id Post ID (0 = homepage)
     313 * @return string route_id e.g. 'post:123' or 'path:' . md5('/')
     314 */
     315function warm_cache_route_id($post_id)
     316{
     317    if ($post_id === 0) {
     318        $front_page_id = (int) get_option('page_on_front');
     319        return ($front_page_id > 0) ? ('post:' . $front_page_id) : ('path:' . md5('/'));
     320    }
     321    return 'post:' . $post_id;
     322}
    308323
    309324/**
     
    417432            usleep(500000); // 0.5 second
    418433           
    419             // Use correct route_id format (post:ID for posts, path:md5(/) for homepage)
    420             if ($post_id === 0) {
    421                 $route_id = 'path:' . md5('/');
    422             } else {
    423                 $route_id = 'post:' . $post_id;
    424             }
     434            $route_id = warm_cache_route_id($post_id);
    425435            $cache_key = \AITranslate\AI_Cache::key($lang_code, $route_id, '');
    426436            $cache_file = \AITranslate\AI_Cache::get_file_path($cache_key);
     
    474484        } elseif ($http_code === 301 || $http_code === 302) {
    475485            // Redirect - might be normal, but we'll mark as success if cache exists
    476             $route_id = 'post:' . $post_id;
     486            $route_id = warm_cache_route_id($post_id);
    477487            $cache_key = \AITranslate\AI_Cache::key($lang_code, $route_id, '');
    478488            $cache_file = \AITranslate\AI_Cache::get_file_path($cache_key);
     
    737747    // This ensures the database is in sync with the filesystem
    738748    if ($warmed > 0) {
    739         // Use correct route_id format (post:ID for posts, path:md5(/) for homepage)
    740         if ($post_id === 0) {
    741             $route_id = 'path:' . md5('/');
    742         } else {
    743             $route_id = 'post:' . $post_id;
    744         }
     749        $route_id = warm_cache_route_id($post_id);
    745750        $enabled = \AITranslate\AI_Lang::enabled();
    746751        $detectable = \AITranslate\AI_Lang::detectable();
  • ai-translate/tags/2.2.4/includes/class-ai-batch.php

    r3451601 r3452203  
    289289                    $choices = is_array($data) ? ($data['choices'] ?? []) : [];
    290290                    if (!$choices || !isset($choices[0]['message']['content'])) {
    291                         error_log('[AI-Batch] Response ' . $idx . ' FAILED: no choices/content. Raw: ' . substr($content, 0, 500));
    292291                        $failedIndexes[] = (int)$idx;
    293292                        continue;
    294293                    }
    295294                    $msg = (string)$choices[0]['message']['content'];
    296                     error_log('[AI-Batch] Response ' . $idx . ' message: ' . substr($msg, 0, 300));
    297295                    if (trim($msg) === '') {
    298                         error_log('[AI-Batch] Response ' . $idx . ' FAILED: empty message');
    299296                        $failedIndexes[] = (int)$idx;
    300297                        continue;
  • ai-translate/tags/2.2.4/includes/class-ai-cache-meta.php

    r3449890 r3452203  
    148148        global $wpdb;
    149149
     150        if (get_option('ai_translate_cache_meta_indexes_applied', false)) {
     151            return;
     152        }
     153
    150154        $table_name = self::get_table_name();
    151155
     
    172176
    173177        foreach ($indexes_to_check as $index_name => $index_definition) {
    174             if (!in_array($index_name, $existing_indexes, true)) {
    175                 // Index doesn't exist, create it
    176                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.SchemaChange
    177                 $wpdb->query($wpdb->prepare(
    178                     "ALTER TABLE %i ADD %1s",
    179                     $table_name,
    180                     $index_definition
    181                 ));
    182             }
    183         }
     178            if (in_array($index_name, $existing_indexes, true)) {
     179                continue;
     180            }
     181
     182            // Guard against race conditions / duplicate key errors by double-checking the schema.
     183            $schema_index_exists = $wpdb->get_var($wpdb->prepare(
     184                "SELECT COUNT(1) FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema = %s AND table_name = %s AND index_name = %s",
     185                $wpdb->dbname,
     186                str_replace($wpdb->prefix, '', $table_name),
     187                $index_name
     188            ));
     189            if ((int) $schema_index_exists > 0) {
     190                continue;
     191            }
     192
     193            // Index doesn't exist, create it
     194            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.SchemaChange
     195            $result = $wpdb->query($wpdb->prepare(
     196                "ALTER TABLE %i ADD %1s",
     197                $table_name,
     198                $index_definition
     199            ));
     200
     201            if ($result === false && stripos($wpdb->last_error, 'Duplicate key name') !== false) {
     202                $wpdb->flush();
     203            }
     204        }
     205
     206        update_option('ai_translate_cache_meta_indexes_applied', true);
    184207    }
    185208
  • ai-translate/tags/2.2.4/includes/class-ai-seo.php

    r3450395 r3452203  
    840840        }
    841841
     842        $front_page_id = (int) get_option('page_on_front');
     843        if ($front_page_id > 0 && (int) $post_id === $front_page_id) {
     844            if (strtolower($lang) === strtolower($default)) {
     845                return home_url('/');
     846            }
     847            return home_url('/' . $lang . '/');
     848        }
     849
    842850        if ((int) $post_id === 0) {
    843851            if (strtolower($lang) === strtolower($default)) {
  • ai-translate/tags/2.2.4/includes/class-ai-translate-core.php

    r3451583 r3452203  
    7878
    7979        if ($provider_key === '') {
    80             throw new \Exception('Provider ontbreekt');
     80            throw new \Exception('Provider missing');
    8181        }
    8282        if ($api_key === '') {
    83             throw new \Exception('API key ontbreekt');
     83            throw new \Exception('API key missing');
    8484        }
    8585
     
    8989        }
    9090        if ($base === '') {
    91             throw new \Exception('API URL ontbreekt');
     91            throw new \Exception('API URL missing');
    9292        }
    9393
     
    123123        $data = json_decode($body, true);
    124124        if (!is_array($data)) {
    125             throw new \Exception('Ongeldig antwoord van API');
     125            throw new \Exception('Unknown response from API');
    126126        }
    127127
Note: See TracChangeset for help on using the changeset viewer.