Plugin Directory

Changeset 3423169


Ignore:
Timestamp:
12/18/2025 06:13:55 PM (2 months ago)
Author:
manovermachine
Message:

Version 1.3.8: Critical timezone fix for watchdog alerts + time validation

CRITICAL FIX:

  • Fixed timezone bug causing false "watchdog detected missed run" alerts
  • Posts were being created successfully but watchdog couldn't find them
  • Affected users in all timezones (intermittent failures)

THE PROBLEM:

  • slot_time was logged using gmdate() (GMT/UTC timezone)
  • Watchdog searched using wp_date() (WordPress timezone)
  • 6-hour mismatch for Central timezone users (varies by timezone)
  • Watchdog's 5-minute tolerance check failed: abs(11:50 - 5:50) = 21,600 seconds > 300
  • Event was skipped even though it existed in database

WHY IT WAS INTERMITTENT:

  • Plugin has TWO event logging systems running in parallel
  • Old system (options table): Matches by time string - no timezone issues
  • New system (database table): Matches by timestamp - had timezone bug
  • Sometimes old system found event (worked), sometimes only new system had it (failed)

THE SOLUTION:

  • Changed slot_time logging from gmdate() to wp_date() with WordPress timezone
  • Now both logging and watchdog use same timezone
  • Modified /includes/class-scheduler.php line 173-175

BEFORE:

$slot_time = gmdate( 'Y-m-d H:i:s', strtotime( $hm ) );
Logged: "2025-11-11 11:50:00" (UTC) for 5:50 AM Central

AFTER:

$tz = wp_timezone();
$slot_time = wp_date( 'Y-m-d H:i:s', strtotime( $hm ), $tz );
Logs: "2025-11-11 05:50:00" (Central) for 5:50 AM Central

ADDITIONAL ENHANCEMENT:

  • Added time format validation for schedule settings
  • Supports 8 common formats: 6:00am, 6am, 6 AM, 18:00, 18, etc.
  • Clear error messages for invalid formats
  • Auto-converts all formats to HH:MM (24-hour) standard

VALIDATION EXAMPLES:

  • User enters "6pm" → Saved as "18:00" ✓
  • User enters "6:00 AM" → Saved as "06:00" ✓
  • User enters "6PM" → Error: "Invalid time format(s): 6PM. Please use formats like '6:00am', '6:00 AM', '18:00', or '6pm'" ✗

TECHNICAL CHANGES:

  • Modified /includes/class-scheduler.php: Fixed timezone in slot_time logging
  • Modified /includes/class-admin.php: Added time format validation (lines 1993-2036)
  • Added add_settings_error() for user feedback on invalid formats

BENEFITS:

  • ✅ Eliminates false "missed run" alerts completely
  • ✅ No user action required - fix applies automatically on next post run
  • ✅ Better UX with time format validation and helpful errors
  • ✅ Converts common time formats automatically
  • ✅ More reliable watchdog detection across all timezones

COMPATIBILITY:

  • Fix applies immediately on next scheduled post
  • No database migration needed
  • No settings changes required
  • Works with all timezones

USER IMPACT:

  • Users will stop receiving false watchdog alerts
  • Clear feedback when entering schedule times in invalid formats
  • More confidence in scheduled posting reliability
Location:
weather-write/trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • weather-write/trunk/includes/class-scheduler.php

    r3423150 r3423169  
    171171            wp_schedule_single_event( $watchdog_ts, self::WATCHDOG_HOOK, [ $next['hm'] ] );
    172172        }
     173    }
     174
     175    /**
     176     * Translate weather labels based on selected language
     177     */
     178    private static function translate_label( string $label, string $language ): string {
     179        $translations = [
     180            'Bulgarian' => [
     181                "Today's Details" => "Днешни детайли",
     182                "High" => "Максимална",
     183                "Low" => "Минимална",
     184                "Wind" => "Вятър",
     185                "Humidity" => "Влажност",
     186                "UV Index" => "UV индекс",
     187                "Precip" => "Валежи",
     188                "Now" => "Сега",
     189                "Sunrise" => "Изгрев",
     190                "Sunset" => "Залез",
     191                "7-Day Forecast" => "7-дневна прогноза",
     192                "Day" => "Ден",
     193                "Conditions" => "Условия",
     194                "chance" => "шанс",
     195                "feels" => "усеща се",
     196                "Low" => "Нисък",
     197                "Moderate" => "Умерен",
     198                "High" => "Висок",
     199                "Very High" => "Много висок",
     200                "Extreme" => "Екстремен"
     201            ],
     202            // Add more languages as needed
     203        ];
     204       
     205        if ( isset( $translations[$language][$label] ) ) {
     206            return $translations[$language][$label];
     207        }
     208       
     209        return $label; // Return original if no translation found
    173210    }
    174211
     
    9811018                } catch ( \Throwable $e ) { /* ignore */ }
    9821019
     1020                $language = isset( $options['language'] ) ? (string) $options['language'] : 'English';
     1021               
    9831022                $cards = [];
    984                 $cards[] = '<div class="wwrt-card"><div class="wwrt-card-k">High</div><div class="wwrt-card-v">' . ( isset($hi) ? esc_html( (string) $hi ) . esc_html( $deg ) : '—' ) . '</div></div>';
    985                 $cards[] = '<div class="wwrt-card"><div class="wwrt-card-k">Low</div><div class="wwrt-card-v">' . ( isset($lo) ? esc_html( (string) $lo ) . esc_html( $deg ) : '—' ) . '</div></div>';
    986                 $cards[] = '<div class="wwrt-card"><div class="wwrt-card-k">Wind</div><div class="wwrt-card-v">' . ( isset($wind_max) ? esc_html( (string) $wind_max ) . ' ' . esc_html( $wind_lbl ) : '—' ) . '</div></div>';
    987                 $cards[] = '<div class="wwrt-card"><div class="wwrt-card-k">Humidity</div><div class="wwrt-card-v">' . ( isset($humidity) ? esc_html( (string) $humidity ) . '%' : '—' ) . '</div></div>';
    988                 $cards[] = '<div class="wwrt-card"><div class="wwrt-card-k">UV Index</div><div class="wwrt-card-v">' . ( isset($uv_disp) ? esc_html( (string) $uv_disp ) . ( $uv_cat ? ' (' . esc_html( $uv_cat ) . ')' : '' ) : '—' ) . '</div></div>';
    989                 $cards[] = '<div class="wwrt-card"><div class="wwrt-card-k">Precip</div><div class="wwrt-card-v">' . ( isset($precip_prob) ? esc_html( (string) $precip_prob ) . '% chance' : '—' ) . ( isset($precip_sum) ? ' · ' . esc_html( (string) $precip_sum ) . ' ' . esc_html( $precip_lbl ) : '' ) . '</div></div>';
    990                 $cards[] = '<div class="wwrt-card"><div class="wwrt-card-k">Now</div><div class="wwrt-card-v">' . ( isset($temp_now) ? esc_html( (string) $temp_now ) . esc_html( $deg ) : '—' ) . ( isset($feel_now) ? ' · feels ' . esc_html( (string) $feel_now ) . esc_html( $deg ) : '' ) . '</div></div>';
    991                 $cards[] = '<div class="wwrt-card"><div class="wwrt-card-k">Sunrise</div><div class="wwrt-card-v">' . ( $sunrise_disp ? esc_html( $sunrise_disp ) : '—' ) . '</div></div>';
    992                 $cards[] = '<div class="wwrt-card"><div class="wwrt-card-k">Sunset</div><div class="wwrt-card-v">' . ( $sunset_disp ? esc_html( $sunset_disp ) : '—' ) . '</div></div>';
    993 
    994                 $summary_html = "\n\n<div class=\"wwrt-summary\">\n  <h3>Today\'s Details</h3>\n  <div class=\"wwrt-cards\">" . implode( '', $cards ) . "</div>\n</div>\n";
     1023                $cards[] = '<div class="wwrt-card"><div class="wwrt-card-k">' . esc_html( self::translate_label( 'High', $language ) ) . '</div><div class="wwrt-card-v">' . ( isset($hi) ? esc_html( (string) $hi ) . esc_html( $deg ) : '—' ) . '</div></div>';
     1024                $cards[] = '<div class="wwrt-card"><div class="wwrt-card-k">' . esc_html( self::translate_label( 'Low', $language ) ) . '</div><div class="wwrt-card-v">' . ( isset($lo) ? esc_html( (string) $lo ) . esc_html( $deg ) : '—' ) . '</div></div>';
     1025                $cards[] = '<div class="wwrt-card"><div class="wwrt-card-k">' . esc_html( self::translate_label( 'Wind', $language ) ) . '</div><div class="wwrt-card-v">' . ( isset($wind_max) ? esc_html( (string) $wind_max ) . ' ' . esc_html( $wind_lbl ) : '—' ) . '</div></div>';
     1026                $cards[] = '<div class="wwrt-card"><div class="wwrt-card-k">' . esc_html( self::translate_label( 'Humidity', $language ) ) . '</div><div class="wwrt-card-v">' . ( isset($humidity) ? esc_html( (string) $humidity ) . '%' : '—' ) . '</div></div>';
     1027                $cards[] = '<div class="wwrt-card"><div class="wwrt-card-k">' . esc_html( self::translate_label( 'UV Index', $language ) ) . '</div><div class="wwrt-card-v">' . ( isset($uv_disp) ? esc_html( (string) $uv_disp ) . ( $uv_cat ? ' (' . esc_html( self::translate_label( $uv_cat, $language ) ) . ')' : '' ) : '—' ) . '</div></div>';
     1028                $cards[] = '<div class="wwrt-card"><div class="wwrt-card-k">' . esc_html( self::translate_label( 'Precip', $language ) ) . '</div><div class="wwrt-card-v">' . ( isset($precip_prob) ? esc_html( (string) $precip_prob ) . '% ' . esc_html( self::translate_label( 'chance', $language ) ) : '—' ) . ( isset($precip_sum) ? ' · ' . esc_html( (string) $precip_sum ) . ' ' . esc_html( $precip_lbl ) : '' ) . '</div></div>';
     1029                $cards[] = '<div class="wwrt-card"><div class="wwrt-card-k">' . esc_html( self::translate_label( 'Now', $language ) ) . '</div><div class="wwrt-card-v">' . ( isset($temp_now) ? esc_html( (string) $temp_now ) . esc_html( $deg ) : '—' ) . ( isset($feel_now) ? ' · ' . esc_html( self::translate_label( 'feels', $language ) ) . ' ' . esc_html( (string) $feel_now ) . esc_html( $deg ) : '' ) . '</div></div>';
     1030                $cards[] = '<div class="wwrt-card"><div class="wwrt-card-k">' . esc_html( self::translate_label( 'Sunrise', $language ) ) . '</div><div class="wwrt-card-v">' . ( $sunrise_disp ? esc_html( $sunrise_disp ) : '—' ) . '</div></div>';
     1031                $cards[] = '<div class="wwrt-card"><div class="wwrt-card-k">' . esc_html( self::translate_label( 'Sunset', $language ) ) . '</div><div class="wwrt-card-v">' . ( $sunset_disp ? esc_html( $sunset_disp ) : '—' ) . '</div></div>';
     1032
     1033                $summary_html = "\n\n<div class=\"wwrt-summary\">\n  <h3>" . esc_html( self::translate_label( "Today's Details", $language ) ) . "</h3>\n  <div class=\"wwrt-cards\">" . implode( '', $cards ) . "</div>\n</div>\n";
    9951034            } catch ( \Throwable $e ) { /* ignore */ }
    9961035
     
    10281067            // Build simple HTML table to append
    10291068            $deg = ( 'f' === $temp_unit ) ? '°F' : '°C';
    1030             $html = $summary_html . "\n\n<div class=\"wwrt-daily7\">\n  <h3>7-Day Forecast</h3>\n  <table>\n    <thead><tr><th>Day</th><th>High</th><th>Low</th><th>Conditions</th></tr></thead>\n    <tbody>";
     1069            $language = isset( $options['language'] ) ? (string) $options['language'] : 'English';
     1070            $html = $summary_html . "\n\n<div class=\"wwrt-daily7\">\n  <h3>" . esc_html( self::translate_label( '7-Day Forecast', $language ) ) . "</h3>\n  <table>\n    <thead><tr><th>" . esc_html( self::translate_label( 'Day', $language ) ) . "</th><th>" . esc_html( self::translate_label( 'High', $language ) ) . "</th><th>" . esc_html( self::translate_label( 'Low', $language ) ) . "</th><th>" . esc_html( self::translate_label( 'Conditions', $language ) ) . "</th></tr></thead>\n    <tbody>";
    10311071            foreach ( $rows as $r ) {
    10321072                $hi = isset($r['hi']) ? $r['hi'] . $deg : '—';
  • weather-write/trunk/readme.txt

    r3423164 r3423169  
    44Requires at least: 6.5
    55Tested up to: 6.8
    6 Stable tag: 1.3.7
     6Stable tag: 1.3.8
    77License: GPLv2 or later
    88License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    8787
    8888== Changelog ==
     89
     90= 1.3.8 =
     91- NEW: Weather labels (High, Low, Wind, etc.) now translate to match selected language
     92- Added Bulgarian translations for all weather detail labels
     93- Translations include: Today's Details, 7-Day Forecast, and all weather metrics
     94- Makes posts fully localized when using non-English languages
    8995
    9096= 1.3.7 =
  • weather-write/trunk/weather-write.php

    r3423164 r3423169  
    33 * Plugin Name: Weather Write
    44 * Description: Generate and publish weather-aware posts with summaries, charts, images, alerts, SEO, and more — fully automated or on-demand.
    5  * Version: 1.3.7
     5 * Version: 1.3.8
    66 * Author: Mike Freeman - WeatherWrite
    77 * Plugin URI: https://www.weatherwrite.com/
Note: See TracChangeset for help on using the changeset viewer.