backoff_manager = Metasync_API_Backoff_Manager::get_instance(); $this->init_hooks(); } /** * Get singleton instance * * @return Metasync_API_Backoff_Notices */ public static function get_instance() { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } /** * Initialize WordPress hooks */ private function init_hooks() { // Display admin notices add_action('admin_notices', [$this, 'display_backoff_notices']); // Add AJAX handler for dismissing notices add_action('wp_ajax_metasync_dismiss_backoff_notice', [$this, 'ajax_dismiss_notice']); // Enqueue admin scripts add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_scripts']); } /** * Display backoff notices in admin */ public function display_backoff_notices() { // Only show on MetaSync admin pages if (!$this->is_metasync_admin_page()) { return; } $active_backoffs = $this->backoff_manager->get_all_active_backoffs(); if (empty($active_backoffs)) { return; } foreach ($active_backoffs as $backoff) { $this->render_backoff_notice($backoff); } } /** * Render individual backoff notice * * @param array $backoff Backoff state data. */ private function render_backoff_notice(array $backoff) { $endpoint = esc_html($backoff['endpoint']); $time_remaining = Metasync_API_Backoff_Manager::format_time_remaining($backoff['time_remaining']); $occurrence = $backoff['occurrence_count']; $response_code = $backoff['response_code']; $endpoint_hash = $backoff['endpoint_hash']; $notice_class = 'notice notice-warning is-dismissible metasync-backoff-notice'; $notice_id = 'metasync-backoff-' . esc_attr($endpoint_hash); $status_label = $response_code === 429 ? 'Rate Limited' : 'Service Unavailable'; $status_icon = '⏸️'; ?>

API Backoff Active:

Status: () Occurrence: /3 Time Remaining:

All requests to this endpoint are temporarily paused. The plugin will automatically resume requests when the backoff period expires. If this continues, the pause duration will increase to prevent further rate limiting.

id, 'metasync') !== false || (isset($_GET['page']) && strpos($_GET['page'], 'metasync') !== false); } /** * Enqueue admin scripts for notice handling * * @param string $hook Current admin page hook. */ public function enqueue_admin_scripts($hook) { if (!$this->is_metasync_admin_page()) { return; } // Enqueue inline script for auto-refresh and dismiss handling wp_add_inline_script('jquery', $this->get_notice_script(), 'after'); } /** * Get JavaScript for notice handling * * @return string JavaScript code. */ private function get_notice_script() { return " jQuery(document).ready(function($) { // Auto-refresh backoff notices every 30 seconds var refreshInterval = setInterval(function() { var backoffNotices = $('.metasync-backoff-notice'); if (backoffNotices.length > 0) { // Check if any notices are still active location.reload(); } else { // No more notices, stop refreshing clearInterval(refreshInterval); } }, 30000); // Handle notice dismissal $(document).on('click', '.metasync-backoff-notice .notice-dismiss', function() { var notice = $(this).closest('.metasync-backoff-notice'); var endpointHash = notice.data('endpoint-hash'); // Optionally send AJAX to log dismissal $.post(ajaxurl, { action: 'metasync_dismiss_backoff_notice', endpoint_hash: endpointHash, nonce: '" . wp_create_nonce('metasync_backoff_notice') . "' }); }); }); "; } /** * AJAX handler for dismissing notices */ public function ajax_dismiss_notice() { check_ajax_referer('metasync_backoff_notice', 'nonce'); $endpoint_hash = sanitize_text_field($_POST['endpoint_hash'] ?? ''); if (empty($endpoint_hash)) { wp_send_json_error(['message' => 'Invalid endpoint hash']); } // Log the dismissal (notice will reappear on page refresh if still active) error_log(sprintf( '[MetaSync API_BACKOFF_NOTICE_DISMISSED] User %d dismissed notice for endpoint: %s', get_current_user_id(), $endpoint_hash )); wp_send_json_success(['message' => 'Notice dismissed']); } /** * Display backoff status in admin bar (optional) * * @param WP_Admin_Bar $wp_admin_bar WordPress admin bar object. */ public function add_admin_bar_status($wp_admin_bar) { if (!current_user_can('manage_options')) { return; } $active_backoffs = $this->backoff_manager->get_all_active_backoffs(); if (empty($active_backoffs)) { return; } $count = count($active_backoffs); $wp_admin_bar->add_node([ 'id' => 'metasync-backoff-status', 'title' => sprintf( '⏸️ API Backoff (%d)', $count ), 'href' => admin_url('admin.php?page=' . Metasync_Admin::$page_slug), 'meta' => [ 'title' => sprintf( '%d API endpoint%s in backoff mode', $count, $count > 1 ? 's' : '' ), ], ]); // Add child nodes for each backoff foreach ($active_backoffs as $backoff) { $wp_admin_bar->add_node([ 'parent' => 'metasync-backoff-status', 'id' => 'metasync-backoff-' . $backoff['endpoint_hash'], 'title' => sprintf( '%s - %s remaining', esc_html($backoff['endpoint']), Metasync_API_Backoff_Manager::format_time_remaining($backoff['time_remaining']) ), ]); } } /** * Get notice HTML for manual display * * @param string $endpoint_hash Endpoint hash. * @return string Notice HTML. */ public function get_notice_html($endpoint_hash) { $backoff = $this->backoff_manager->get_backoff_state($endpoint_hash); if (!$backoff || $backoff['time_remaining'] <= 0) { return ''; } ob_start(); $backoff['endpoint_hash'] = $endpoint_hash; $this->render_backoff_notice($backoff); return ob_get_clean(); } }