Plugin Directory

Changeset 3380444


Ignore:
Timestamp:
10/18/2025 09:12:47 AM (3 months ago)
Author:
kotakdigitalcom
Message:

v1.0.7

Location:
kotaqx-poster/trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • kotaqx-poster/trunk/Readme.txt

    r3368850 r3380444  
    55Tested up to: 6.8
    66Requires PHP: 7.2
    7 Stable tag: 1.0.6
     7Stable tag: 1.0.7
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    105105== Changelog ==
    106106
     107= 1.0.7 =
     108* Improve: Republish platform handler code and add log event.
     109* Improve: Add plugin version to database, used to migrate plugin settings in future updates.
     110* Improve: License Info UI to provide clearer status display.
     111* Refactor: Internal code restructured for easier maintenance (no changes to functionality).
     112* Fix: Potential double execution on publish→update; added a 60-second cooldown to prevent duplicate reposts when `Repost on Update` is enabled.
     113* Fix: Custom delay of repost scheduling is not accurate.
     114
    107115= 1.0.6 =
    108116* Update: Add default settings if not present.
  • kotaqx-poster/trunk/admin/tab-license.php

    r3364882 r3380444  
    1212$license_key = isset($license_data['key']) ? Utils::censor_string($license_data['key'], true) : '';
    1313$license_status = isset($license_data['status']) ? $license_data['status'] : '';
    14 $license_exp = isset($license_data['exp']) ? $license_data['exp'] : '';
     14$license_exp = isset($license_data['exp']) ? trim((string) $license_data['exp']) : '';
    1515
    16 $is_valid = $license_status === 'valid';
     16$is_valid = ($license_status === 'valid');
     17$now_ts = time();
     18$is_lifetime = (strtolower((string)$license_exp) === 'lifetime');
     19$exp_dt = (!$is_lifetime && !empty($license_exp))
     20    ? DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $license_exp, new DateTimeZone('UTC'))
     21    : null;
    1722
    18 $button_text = $is_valid ? 'Deactivate' : 'Activate';
    19 $button_icon = $is_valid ? 'dashicons-no-alt' : 'dashicons-yes-alt';
    20 $button_class = $is_valid ? 'is-danger' : 'is-primary';
    21 $button_name = $is_valid ? 'deactivate_license' : 'activate_license';
     23$exp_ts = $exp_dt ? $exp_dt->getTimestamp() : false;
     24$is_expired = (!$is_lifetime && $exp_ts) ? ($exp_ts <= $now_ts) : false;
     25$expired_label = $exp_dt ? $exp_dt->format('Y-m-d H:i:s') : '—';
     26
     27// Final effective state: valid-but-expired should be treated as expired/inactive in UI
     28$effective_valid = $is_valid && !$is_expired;
     29
     30$button_text = $effective_valid ? 'Deactivate' : 'Activate';
     31$button_icon = $effective_valid ? 'dashicons-no-alt' : 'dashicons-yes-alt';
     32$button_class = $effective_valid ? 'is-danger' : 'is-primary';
     33$button_name = $effective_valid ? 'deactivate_license' : 'activate_license';
     34
     35// Status chip data
     36if ($is_lifetime) {
     37    $status_label = 'Lifetime';
     38    $status_icon = 'dashicons-infinity';
     39    $status_class = 'tag is-success is-light';
     40} elseif ($is_expired) {
     41    $status_label = 'Expired';
     42    $status_icon = 'dashicons-dismiss';
     43    $status_class = 'tag is-danger';
     44} elseif ($exp_ts) {
     45    $status_label = 'Active';
     46    $status_icon = 'dashicons-yes';
     47    $status_class = 'tag is-success';
     48} else {
     49    $status_label = $effective_valid ? 'Active' : 'Inactive';
     50    $status_icon = $effective_valid ? 'dashicons-yes' : 'dashicons-clock';
     51    $status_class = $effective_valid ? 'tag is-success' : 'tag is-warning is-light';
     52}
     53
     54// Expiration display text
     55$exp_display = '';
     56if ($is_lifetime) {
     57    $exp_display = 'Never (lifetime)';
     58} elseif ($exp_ts) {
     59    $exp_display = sprintf(
     60        '%s (on %s)',
     61        $is_expired
     62        ? 'Expired'
     63        : 'Expires',
     64        esc_html( $expired_label )
     65    );
     66}
     67
     68// Remaining time (human) when still active and not lifetime
     69$human_left = (!$is_lifetime && $exp_ts && !$is_expired)
     70    ? human_time_diff($now_ts, $exp_ts)
     71    : '';
    2272?>
    2373
     
    2777            <div class="card">
    2878                <header class="card-header">
    29                     <p class="card-header-title is-size-5 is-flex is-align-items-center is-justify-content-center has-text-warning">
     79                    <p class="card-header-title is-size-5 is-flex is-justify-content-center">
    3080                        <span class="dashicons dashicons-admin-network" style="margin-right: 8px;"></span>
    3181                        License Activation
    3282                    </p>
    3383                </header>
     84
     85                <?php if ($is_expired): ?>
     86                    <div class="notification is-danger m-4">
     87                        <p class="is-flex is-align-items-center mb-1">
     88                            <span class="dashicons dashicons-warning" style="margin-right:8px"></span>
     89                            <strong>Your license has expired.</strong>
     90                        </p>
     91                        <p>Click the button below to renew your license.</p>
     92                        <a class="button mt-5 is-warning is-flex is-align-items-center" href="https://kotakdigital.com/my-account/" target="_blank">
     93                            <span class="dashicons dashicons-admin-network" style="margin-right:6px"></span>
     94                            Renew / Manage License
     95                        </a>
     96                    </div>
     97                <?php endif; ?>
    3498
    3599                <div class="card-content">
     
    40104                                id="license_key"
    41105                                name="license_key"
    42                                 class="input"
     106                                class="input <?php echo $is_expired ? 'is-danger' : ''; ?>"
    43107                                placeholder="Enter your license key"
    44108                                value="<?php echo esc_attr($license_key); ?>"
    45                                 <?php echo esc_attr($license_status ? 'disabled' : ''); ?>
     109                                <?php echo esc_attr($effective_valid ? 'disabled' : ''); ?>
    46110                            >
    47111                            <span class="icon is-left">
     
    54118                    </div>
    55119
    56                     <div class="field is-grouped is-grouped-centered mt-4">
     120                    <?php if (!$is_lifetime): ?>
     121                        <div class="field is-grouped is-grouped-multiline">
     122                            <?php if ($exp_ts): ?>
     123                                <div class="control">
     124                                    <div class="tags has-addons">
     125                                        <span class="tag is-dark">Status</span>
     126                                        <span class="tag <?php echo $is_expired ? 'is-danger' : 'is-success'; ?>"><?php echo $is_expired ? 'Expired' : 'Active'; ?></span>
     127                                    </div>
     128                                </div>
     129                                <div class="control">
     130                                    <div class="tags has-addons">
     131                                        <span class="tag is-dark">Expiry</span>
     132                                        <span class="tag is-info"><?php echo esc_html($expired_label); ?></span>
     133                                    </div>
     134                                </div>
     135                                <?php if ($human_left && !$is_expired): ?>
     136                                    <div class="control">
     137                                        <div class="tags has-addons">
     138                                            <span class="tag is-dark">Time left</span>
     139                                            <span class="tag is-warning"><?php echo esc_html($human_left); ?></span>
     140                                        </div>
     141                                    </div>
     142                                <?php endif; ?>
     143                            <?php endif; ?>
     144                        </div>
     145                    <?php else: ?>
     146                        <div class="field is-grouped is-grouped-multiline">
     147                            <div class="control">
     148                                <div class="tags has-addons">
     149                                    <span class="tag is-dark">Status</span>
     150                                    <span class="tag is-success">Active</span>
     151                                </div>
     152                            </div>
     153                            <div class="control">
     154                                <div class="tags has-addons">
     155                                    <span class="tag is-dark">Expiry</span>
     156                                    <span class="tag is-info">Lifetime</span>
     157                                </div>
     158                            </div>
     159                        </div>
     160                    <?php endif; ?>
     161
     162                    <div class="field is-grouped is-grouped-centered mt-5">
    57163                        <div class="control">
    58164                            <button id="license-submit-btn"
    59165                                class="button <?php echo esc_attr($button_class); ?>"
     166                                name="<?php echo esc_attr($button_name); ?>"
    60167                                data-action="<?php echo esc_html(strtolower($button_text)); ?>"
    61168                                data-nonce="<?php echo wp_create_nonce('kotaqx_poster_handle_license'); ?>">
     
    67174                </div>
    68175
    69                 <?php if ($license_status): ?>
     176                <?php if (! $effective_valid): ?>
    70177                    <footer class="card-footer">
    71                         <div class="card-footer-item has-text-success">
    72                             <span class="dashicons dashicons-yes" style="margin-right: 6px;"></span>
    73                             License is active
    74                         </div>
     178                            <div class="card-footer-item has-text-warning">
     179                                <span class="dashicons dashicons-clock" style="margin-right: 6px;"></span>
     180                                License is inactive
     181                            </div>
    75182                    </footer>
    76183                <?php endif; ?>
     184               
    77185            </div>
    78186        </div>
  • kotaqx-poster/trunk/admin/tab-repost.php

    r3364882 r3380444  
    251251                </div>
    252252            </label>
    253             <p class="help">Repost content when a published post is updated (published → save).</p>
     253            <p class="help">Repost content when a published post is updated (published → save). 60-second cooldown to prevents duplicates.</p>
    254254        </div>
    255255    </div>
  • kotaqx-poster/trunk/includes/Ajax.php

    r3351682 r3380444  
    1212
    1313class Ajax {
     14    const LOGS = KOTAQX_POSTER_NAME . '_logs';
     15
    1416    private $functions;
    1517    private $kotaqx_poster;
     
    155157
    156158        global $wpdb;
    157         $table_name = $wpdb->prefix . 'kotaqx_poster_logs';
     159        $table_name = $wpdb->prefix . self::LOGS;
    158160
    159161        if ( ! preg_match('/^[A-Za-z0-9_]+$/', $table_name) ) {
     
    192194   
    193195        global $wpdb;
    194         $table_name = $wpdb->prefix . 'kotaqx_poster_logs';
     196        $table_name = $wpdb->prefix . self::LOGS;
    195197
    196198        if ( ! preg_match('/^[A-Za-z0-9_]+$/', $table_name) ) {
  • kotaqx-poster/trunk/includes/ProInstance.php

    r3351682 r3380444  
    2020     */
    2121    public static function get() {
    22         if (self::$instance === null) {
    23             if (class_exists('\KotakDigital\PosterPro\Kotaqx_Poster_Pro')) {
    24                 self::$instance = new \KotakDigital\PosterPro\Kotaqx_Poster_Pro();
    25             }
     22        if (self::$instance === null && class_exists('\KotakDigital\PosterPro\Kotaqx_Poster_Pro')) {
     23            self::$instance = \KotakDigital\PosterPro\Kotaqx_Poster_Pro::instance();
    2624        }
    2725        return self::$instance;
     
    3331     */
    3432    public static function is_active() {
    35         $transient = get_transient('kotaqx_poster_pro_active');
     33        $transient = get_transient(KOTAQX_POSTER_NAME . '_pro_active');
    3634
    3735        if ($transient === true) {
  • kotaqx-poster/trunk/includes/utils.php

    r3368850 r3380444  
    55
    66class Utils {
    7     // Option name for storing settings
    8     const SETTINGS = 'kotaqx_poster_settings';
     7    // Option name for storing settings
     8    const SETTINGS = KOTAQX_POSTER_NAME . '_settings';
     9    const LOGS = KOTAQX_POSTER_NAME . '_logs';
    910
    1011    // Load settings from the database
     
    5354            ) {
    5455                global $wpdb;
    55                 $table_name = $wpdb->prefix . 'kotaqx_poster_logs';
     56                $table_name = $wpdb->prefix . self::LOGS;
    5657
    5758                if ( ! preg_match('/^[A-Za-z0-9_]+$/', $table_name) ) {
     
    136137    public static function get_logs($limit = 25, $offset = 0) {
    137138        global $wpdb;
    138         $table_name = $wpdb->prefix . 'kotaqx_poster_logs';
     139        $table_name = $wpdb->prefix . self::LOGS;
    139140
    140141        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Prepared, Sanitized, & no cache needed
  • kotaqx-poster/trunk/kotaqx-poster.php

    r3368850 r3380444  
    55 * Plugin URI:        https://kotakdigital.com/downloads/kotaqx-poster/
    66 * Description:       Automatically recover missed schedule posts, and share published content to other platforms.
    7  * Version:           1.0.6
     7 * Version:           1.0.7
    88 * Author:            Kotak Digital
    99 * Author URI:        https://kotakdigital.com
     
    2020
    2121if (!defined('KOTAQX_POSTER_VERSION')) {
    22     define('KOTAQX_POSTER_VERSION', '1.0.6');
     22    define('KOTAQX_POSTER_VERSION', '1.0.7');
    2323}
    2424if (!defined('KOTAQX_POSTER_URL')) {
     
    3131    define('KOTAQX_POSTER_SLUG', 'kotaqx-poster');
    3232}
     33if (!defined('KOTAQX_POSTER_NAME')) {
     34    define('KOTAQX_POSTER_NAME', 'kotaqx_poster');
     35}
    3336if (!defined('KOTAQX_POSTER_SETTING')) {
    3437    define('KOTAQX_POSTER_SETTING', 'kotaqx-poster-settings');
    3538}
    3639
    37 // Some advanced features are available when Kotaqx Poster PRO is installed.
    38 // The free version works fine without it.
    39 // We conditionally check for Pro using defined()/function_exists()/class_exists() before calling anything from Pro.
    40 // This prevents fatal errors and keeps the free plugin fully functional on its own.
    4140if (!defined('KOTAQX_POSTER_PRO_DIR')) {
    4241    define('KOTAQX_POSTER_PRO_DIR', WP_PLUGIN_DIR . '/kotaqx-poster-pro/');
     
    9291    }
    9392
     93    /**
     94     * Load plugin features.
     95     */
    9496    private function load_features() {
    9597        require_once plugin_dir_path(__FILE__) . 'admin/settings.php';
    9698    }
    9799
     100    /**
     101     * Enqueue admin assets.
     102     *
     103     * @param string $hook The current admin page.
     104     */
    98105    public function admin_assets($hook) {
    99106        if ($hook !== 'toplevel_page_' . KOTAQX_POSTER_SETTING) {
     
    103110        wp_enqueue_script(
    104111            'kotaqx-poster-script',
    105             plugin_dir_url(__FILE__) . '/assets/js/script.min.js',
     112            KOTAQX_POSTER_URL. '/assets/js/script.min.js',
    106113            ['jquery'],
    107             filemtime(plugin_dir_path(__FILE__) . 'assets/js/script.min.js'),
     114            filemtime(KOTAQX_POSTER_DIR . 'assets/js/script.min.js'),
    108115            true
    109116        );
    110         wp_enqueue_script('sweetalert2', plugin_dir_url(__FILE__) . '/assets/js/sweetalert2.min.js', ['jquery'],  array('jquery'), '11.4.8', true);
    111         wp_enqueue_style('bulma-css', plugin_dir_url(__FILE__) . '/assets/css/bulma.min.css', [], '1.0.4');
    112         wp_enqueue_style('kotaqx-poster-style', plugin_dir_url(__FILE__) . '/assets/css/style.min.css', [], '1.0.0');
    113         wp_enqueue_style('sweetalert2-css', plugin_dir_url(__FILE__) . '/assets/css/sweetalert2.min.css', [], '11.4.8');
     117        wp_enqueue_script('sweetalert2', KOTAQX_POSTER_URL . '/assets/js/sweetalert2.min.js', ['jquery'],  array('jquery'), '11.4.8', true);
     118        wp_enqueue_style('bulma-css', KOTAQX_POSTER_URL. '/assets/css/bulma.min.css', [], '1.0.4');
     119        wp_enqueue_style('kotaqx-poster-style', KOTAQX_POSTER_URL . '/assets/css/style.min.css', [], '1.0.0');
     120        wp_enqueue_style('sweetalert2-css', KOTAQX_POSTER_URL . '/assets/css/sweetalert2.min.css', [], '11.4.8');
    114121
    115122        wp_localize_script('kotaqx-poster-script', 'kotaqx_poster_vars', [
     
    118125    }
    119126
     127    /**
     128     * Plugin activation callback.
     129     */
    120130    public function activate() {
    121131        global $wpdb;
     
    141151        }
    142152
     153        // Add plugin version
     154        if (!get_option('kotaqx_poster_version')) {
     155            // Set plugin version
     156            // Used to migrate plugin settings in future updates
     157            update_option('kotaqx_poster_version', KOTAQX_POSTER_VERSION);
     158        }
     159       
    143160        //Run the cron
    144161        $this->schedule_cron();
    145162    }
    146163
     164    /**
     165     * Plugin deactivation callback.
     166     */
    147167    public function deactivate() {
    148168        wp_clear_scheduled_hook('kotaqx_poster_schedule_cron');
    149169    }
    150170   
     171    /**
     172     * Handle manual trigger via AJAX.
     173     */
    151174    public function manual_trigger() {
    152175        if (!current_user_can('manage_options') || !check_ajax_referer('kotaqx_poster_manual_trigger', 'kotaqx_poster_manual_nonce', false)) {
     
    167190    }
    168191
     192    /**
     193     * Check and publish missed scheduled posts.
     194     *
     195     * @return int Number of posts published.
     196     */
    169197    public function check_and_publish_missed_scheduled_posts() {
    170198        global $wpdb;
     
    209237    }   
    210238
     239    /**
     240     * Add custom cron schedules.
     241     *
     242     * @param array $schedules Existing cron schedules.
     243     * @return array Modified cron schedules.
     244     */
    211245    public function add_cron_schedules($schedules) {
    212246        $options = Utils::load_settings();
     
    223257    }
    224258
     259    /**
     260     * Schedule the cron event if not already scheduled.
     261     */
    225262    public function schedule_cron() {
    226263        if (!wp_next_scheduled('kotaqx_poster_schedule_cron')) {
     
    229266    }
    230267
     268    /**
     269     * Handle republishing of posts when their status changes to 'publish'.
     270     *
     271     * @param string $new_status The new post status.
     272     * @param string $old_status The old post status.
     273     * @param WP_Post $post The post object.
     274     */
    231275    public function handle_republish_post($new_status, $old_status, $post) {
    232276        if ($new_status !== 'publish') {
    233             return; // Only handle when post is published
    234         }
     277            // Only handle when post is published
     278            return;
     279        }
     280
     281        $settings  = Utils::load_settings();
     282        $repost_on_update = isset($settings['repost_on_update']) ? $settings['repost_on_update'] : false;
     283        $is_update = ($old_status === 'publish' && $new_status === 'publish');
     284
     285        // Recheck for post status
     286        if ($is_update && !$repost_on_update) return;
    235287
    236288        $ID = $post->ID;
     
    238290
    239291        if (get_transient($lock_key)) {
    240             return; // Skip if already locked
     292            // Skip if already locked
     293            return;
    241294        }
    242295
     
    244297        set_transient($lock_key, true, 15);
    245298
    246         if (!wp_next_scheduled('kotaqx_poster_do_republish_event', [$ID])) {
     299        if (!wp_next_scheduled('kotaqx_poster_do_republish_event', [$ID, $new_status, $old_status])) {
    247300            // Schedule the event to run
    248             $settings = Utils::load_settings();
    249301            $repost_delay = isset($settings['schedule_repost_interval']) ? $settings['schedule_repost_interval'] : '0'; // Default to 'immediately'
     302            $delay_seconds = 0;
    250303
    251304            if ($repost_delay === 'custom') {
    252                 $custom_delay_value = isset($settings['schedule_repost_custom_delay_value']) ? intval($settings['schedule_repost_custom_delay_value']) : 1;
    253                 $custom_delay_unit = isset($settings['schedule_repost_custom_delay_unit']) ? $settings['schedule_repost_custom_delay_unit'] : 'minutes';
     305                $val = isset($settings['schedule_repost_custom_delay_value']) ? intval($settings['schedule_repost_custom_delay_value']) : 1;
     306                $unit = isset($settings['schedule_repost_custom_delay_unit']) ? $settings['schedule_repost_custom_delay_unit'] : 'minutes';
    254307
    255308                // Convert custom delay to seconds
    256                 switch ($custom_delay_unit) {
     309                switch ($unit) {
     310                    case 'minute':
    257311                    case 'minutes':
    258                         $repost_delay = $custom_delay_value * 60;
     312                        $delay_seconds = $val * 60;
    259313                        break;
     314                    case 'hour':
    260315                    case 'hours':
    261                         $repost_delay = $custom_delay_value * 3600;
     316                        $delay_seconds = $val * 3600;
    262317                        break;
     318                    case 'day':
    263319                    case 'days':
    264                         $repost_delay = $custom_delay_value * 86400;
     320                        $delay_seconds = $val * 86400;
    265321                        break;
    266322                    default:
    267                         $repost_delay = 0; // Fallback to immediate if unit is invalid
     323                        $delay_seconds = 0; // Fallback to 'immediately' if unit is invalid
    268324                        break;
    269325                }
    270             }
    271 
    272             $repost_delay = intval($repost_delay);
     326            } else {
     327                $delay_seconds = max(0, intval($repost_delay)) * 60;
     328            }
    273329
    274330            // Schedule with a delay 5s to ensure the post is fully saved
    275             wp_schedule_single_event(time() + ($repost_delay * 60) + 5, 'kotaqx_poster_do_republish_event', [$ID]);
    276         }
    277     }
    278 
    279     public function do_republish_event($ID) {
     331            wp_schedule_single_event(time() + $delay_seconds + 5, 'kotaqx_poster_do_republish_event', [$ID, $new_status, $old_status]);
     332        }
     333    }
     334
     335    /**
     336     * Execute the republishing event for a given post ID.
     337     *
     338     * @param int $ID The post ID.
     339     */
     340    public function do_republish_event($ID, $new_status = null, $old_status = null) {
     341        // Cooldown to prevent double execution
     342        $run_lock = 'kotaqx_poster_republish_run_' . $ID;
     343        if (get_transient($run_lock)) return;
     344        set_transient($run_lock, 1, 60); //60s cooldown
     345
    280346        $settings = Utils::load_settings();
    281347        $post = get_post($ID);
    282 
    283348        if (!$post) return;
     349
     350        $repost_on_update = !empty($settings['repost_on_update']);
     351        $repost_on_republish = !empty($settings['repost_on_republish']);
     352
     353        $is_publish_transition = ($new_status === 'publish');
     354        $was_published         = ($old_status === 'publish');
     355        $is_update             = ($is_publish_transition && $was_published); // publish -> publish
     356        $is_republish          = ($is_publish_transition && !$was_published); // non-publish -> publish
     357
     358        // If there is no new status, use the post_date check as a fallback:
     359        if ($new_status === null) {
     360            $is_update    = ($post->post_modified !== $post->post_date);
     361            $is_republish = !$is_update && ($post->post_status === 'publish');
     362        }
    284363
    285364        foreach ($this->platforms as $platform) {
     
    287366            $post_types = $settings[$platform . '_post_type'] ?? [];
    288367
    289             // Skip if the channel is not active or the post_type does not match
     368            // Skip if the platform is not active or the post_type does not match
    290369            if (!$enabled || !in_array($post->post_type, (array) $post_types)) {
    291370                continue;
     
    293372
    294373            $post_meta_key = "_{$platform}_post_sent";
    295             $post_sent = get_post_meta($ID, $post_meta_key, true);
    296 
    297             $repost_on_update = !empty($settings['repost_on_update']);
    298             $repost_on_republish = !empty($settings['repost_on_republish']);
    299 
    300             // If update, only resend if allowed
    301             if ($post_sent && !$repost_on_update && $post->post_modified !== $post->post_date) {
     374            $raw_meta      = get_post_meta($ID, $post_meta_key, true);
     375            $post_sent     = ($raw_meta === '1' || $raw_meta === 1 || $raw_meta === true);
     376
     377            // 1) If this is an UPDATE and the setting is off -> DO NOT send even if the meta doesn't exist yet
     378            if ($is_update && !$repost_on_update) {
     379                Utils::log_event(false, $ID, "[{$platform}] Error 1.");
    302380                continue;
    303381            }
    304382
    305             // If republish (draft > publish), reset post_sent to allow resending
    306             if ($post_sent && $repost_on_republish && $post->post_status === 'publish') {
     383            // 2) If this is REPUBLISH and setting off -> DO NOT send even if meta does not exist
     384            if ($is_republish && !$repost_on_republish) {
     385                Utils::log_event(false, $ID, "[{$platform}] Error 2.");
     386                continue;
     387            }
     388
     389            // 3) If reupdate is allowed and has already been sent, reset the flag to allow sending again.
     390            if ($is_update && $repost_on_update && $post_sent) {
    307391                update_post_meta($ID, $post_meta_key, false);
    308392            }
    309393
    310             // Send to channel
     394            // 4) If republishing is allowed and has already been sent, reset the flag to allow sending again.
     395            if ($is_republish && $repost_on_republish && $post_sent) {
     396                update_post_meta($ID, $post_meta_key, false);
     397            }
     398
     399            // Reposting
    311400            Handler::repost_to($platform, $ID);
    312401        }
    313402    }
    314403
     404    /**
     405     * Handle republishing by the scheduled event for specific platform republishing.
     406     * Currently used by Threads platform only.
     407     *
     408     * @param string $platform The platform identifier.
     409     * @param array $data Data related to the post to be republished.
     410     */
    315411    public static function handle_republish_platform($platform, $data) {
    316         $map = Handler::get_platform_map();
     412        // Get platforms class map
     413        $map = Handler::get_platform_map();
     414
     415        // Platform label and ID
     416        $platform_label = ucfirst(sanitize_key($platform));
     417        $ID = isset($data['ID']) ? (int) $data['ID'] : 0;
    317418
    318419        if (!isset($map[$platform])) {
    319             //Platform handler not found
     420            // Platform handler not found
     421            // Platform map not defined or platform name invalid
     422            Utils::log_event(false, $ID, "[{$platform_label}] Handler not found.");
    320423            return;
    321424        }
    322425
     426        // Get current platform class
    323427        $class = $map[$platform];
    324428
    325429        if (!class_exists($class)) {
    326             //Class $class not loaded
     430            // Class $class not loaded
     431            // Platform class is invalid
     432            Utils::log_event(false, $ID, "[{$platform_label}] Class not loaded.");
    327433            return;
    328434        }
     
    336442    }
    337443
     444    /**
     445     * Show admin notice if any platform token is expiring soon.
     446     */
    338447    public function maybe_show_token_expiry_notice() {
    339448        if (!current_user_can('manage_options')) return;
     
    361470    }
    362471
     472    /**
     473     * Send email notification if any platform token is expiring soon.
     474     */
    363475    public function maybe_send_token_expiry_email() {
    364476        $settings = Utils::load_settings();
Note: See TracChangeset for help on using the changeset viewer.