plugin_file = $plugin_file; $this->version = $version; $this->api_url = trailingslashit($api_url); // Hook into WordPress update system add_filter('pre_set_site_transient_update_plugins', array($this, 'check_for_updates')); add_filter('plugins_api', array($this, 'plugin_info'), 10, 3); // Clear update cache after plugin is updated add_action('upgrader_process_complete', array($this, 'clear_update_cache'), 10, 2); // Add custom styling for plugin details popup add_action('admin_enqueue_scripts', array($this, 'enqueue_plugin_details_styles')); } /** * Check for plugin updates * * @param object $transient Update transient * @return object Modified transient */ public function check_for_updates($transient) { if (empty($transient->checked)) { return $transient; } // Get update info from IGNY8 API $update_info = $this->get_update_info(); if (!$update_info || !isset($update_info['latest_version'])) { return $transient; } $plugin_basename = plugin_basename($this->plugin_file); $latest_version = $update_info['latest_version']; // Only show update if latest version is actually newer than current if (version_compare($this->version, $latest_version, '<')) { $transient->response[$plugin_basename] = (object) array( 'slug' => $this->plugin_slug, 'new_version' => $latest_version, 'package' => $update_info['download_url'] ?? '', 'url' => $update_info['info_url'] ?? '', 'tested' => $update_info['tested'] ?? '', 'requires_php' => $update_info['requires_php'] ?? '', ); } else { // CRITICAL FIX: Explicitly mark as no update by removing from response // and adding to no_update to prevent false update notifications if (isset($transient->response[$plugin_basename])) { unset($transient->response[$plugin_basename]); } // Mark plugin as checked with no update available if (!isset($transient->no_update)) { $transient->no_update = array(); } $transient->no_update[$plugin_basename] = (object) array( 'slug' => $this->plugin_slug, 'plugin' => $plugin_basename, 'new_version' => $this->version, 'url' => $update_info['info_url'] ?? '', 'package' => '', ); } return $transient; } /** * Provide plugin information for update screen * * @param false|object|array $result The result object or array * @param string $action The type of information being requested * @param object $args Plugin API arguments * @return false|object Modified result */ public function plugin_info($result, $action, $args) { if ($action !== 'plugin_information') { return $result; } if (!isset($args->slug) || $args->slug !== $this->plugin_slug) { return $result; } // Get plugin info from IGNY8 API $info = $this->get_plugin_info(); if (!$info) { return $result; } return (object) array( 'name' => $info['name'] ?? 'IGNY8 WordPress Bridge', 'slug' => $this->plugin_slug, 'version' => $info['version'] ?? $this->version, 'author' => 'IGNY8', 'author_profile' => 'https://igny8.com', 'homepage' => 'https://igny8.com', 'sections' => array( 'description' => $info['description'] ?? '', 'changelog' => $info['changelog'] ?? '', ), 'download_link' => $info['download_url'] ?? '', 'tested' => $info['tested'] ?? '', 'requires_php' => $info['requires_php'] ?? '7.4', ); } /** * Clear update cache after plugin update completes * * Fixes the double-update notification bug by forcing WordPress * to re-check for updates with the new version number * * @param WP_Upgrader $upgrader WP_Upgrader instance * @param array $hook_extra Extra arguments passed to hooked filters */ public function clear_update_cache($upgrader, $hook_extra) { // Check if this is a plugin update if (!isset($hook_extra['type']) || $hook_extra['type'] !== 'plugin') { return; } // Check if our plugin was updated $plugin_basename = plugin_basename($this->plugin_file); $updated_plugins = array(); if (isset($hook_extra['plugin'])) { $updated_plugins[] = $hook_extra['plugin']; } elseif (isset($hook_extra['plugins'])) { $updated_plugins = $hook_extra['plugins']; } // If our plugin was updated, force WordPress to recheck updates if (in_array($plugin_basename, $updated_plugins, true)) { // Delete the update_plugins transient to force a fresh check delete_site_transient('update_plugins'); // Also clear any cached update info wp_cache_delete('igny8_update_check', 'igny8'); // Log the cache clear for debugging error_log('IGNY8: Cleared update cache after plugin update to version ' . $this->version); } } /** * Enqueue custom styles for plugin details popup */ public function enqueue_plugin_details_styles() { // No custom styles needed } /** * Get update information from IGNY8 API * * @return array|false Update info or false on failure */ private function get_update_info() { $url = $this->api_url . $this->plugin_slug . '/check-update/'; $response = wp_remote_get($url, array( 'timeout' => 10, 'headers' => array( 'X-IGNY8-Site-ID' => get_option('igny8_site_id'), 'X-IGNY8-API-Key' => get_option('igny8_api_key'), ), 'body' => array( 'current_version' => $this->version, ), )); if (is_wp_error($response)) { return false; } $code = wp_remote_retrieve_response_code($response); if ($code !== 200) { return false; } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); return $data ?: false; } /** * Get plugin information from IGNY8 API * * @return array|false Plugin info or false on failure */ private function get_plugin_info() { $url = $this->api_url . $this->plugin_slug . '/info/'; $response = wp_remote_get($url, array( 'timeout' => 10, )); if (is_wp_error($response)) { return false; } $code = wp_remote_retrieve_response_code($response); if ($code !== 200) { return false; } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); return $data ?: false; } }