/** * Handle the pull failure event. If the number of failures exceeds the maximum attempts set in the options, then disable the site. * * @param $site_id * @param $count */ public function handle_pull_failure_event($site_id, $count) { global $settings_manager; $site_id = (int) $site_id; $max_pull_attempts = (int) $settings_manager->get_setting('push_syndication_max_pull_attempts', 0); if (!$max_pull_attempts) { return; } if ($count >= $max_pull_attempts) { // Disable the site. update_post_meta($site_id, 'syn_site_enabled', false); // Reset the event counter. do_action('push_syndication_reset_event', 'pull_failure', $site_id); // Log what happened. Syndication_Logger::log_post_error($site_id, 'error', sprintf(__('Site disabled after %d pull failure(s).', 'push-syndication'), $count)); } }
/** * Fire up the Syndication plugin * * Note: Class Autoloading is in use */ public function __construct() { // Load our helper functions which autoload can't..load require_once SYNDICATION_PATH . 'includes/functions-helpers.php'; // Always load. new Cron(); // Settings helper. global $settings_manager; $settings_manager = new Syndication_Settings(); global $client_manager; $client_manager = new Client_Manager(); global $site_manager; $site_manager = new Site_Manager(); Syndication_Logger::init(); new Syndication_Event_Counter(); new Syndication_Site_Failure_Monitor(); new Syndication_Fail_Auto_Retry(); new Upgrade_Tasks(); new Legacy_Hooks(); // Bootstrap admin. new Admin\Settings_Screen(); new Admin\Site_List_Screen(); new Admin\Site_Edit_Screen($client_manager); new Admin\Post_Edit_Screen(); // Load the runner. new Syndication_Runner(); // Bootstrap individual built-in clients. new Clients\Test\Bootstrap(); new Clients\XML_Pull\Bootstrap(); new Clients\RSS_Pull\Bootstrap(); new Clients\XML_Push\Bootstrap(); new Clients\REST_Push\Bootstrap(); // Command line stuff. if (defined('WP_CLI') && WP_CLI) { require_once SYNDICATION_PATH . 'includes/class-syndication-cli-command.php'; \WP_CLI::add_command('syndication', 'Syndication_CLI_Command'); } // Hooks. add_action('init', [$this, 'init']); }
/** * Handle the pull failure event. If the number of failures exceeds the maximum attempts set in the options, then disable the site. * * @param $site_id * @param $count */ public function handle_pull_failure_event($site_id, $count) { global $settings_manager; $site_id = (int) $site_id; $max_pull_attempts = (int) $settings_manager->get_setting('push_syndication_max_pull_attempts', 0); if (!$max_pull_attempts) { return; } if ($count >= $max_pull_attempts) { // Disable the site. update_post_meta($site_id, 'syn_site_enabled', false); /** * Fires when the site pull failure count exceeds the maximum pull attempts. * * Triggers a reset of the event counter. * * @param int $site_id The id of the site that failed. */ do_action('push_syndication_reset_event', 'pull_failure', $site_id); // Log what happened. Syndication_Logger::log_post_error($site_id, 'error', sprintf(__('Site disabled after %d pull failure(s).', 'push-syndication'), $count)); } }
/** * Prepares data for the post level log events * @param string $event Type of event new/update/delete * @param mixed $result Result object of previous wp_insert_post action * @param mixed $post Post object or post_id * @param object $site Post object for the site doing the syndication * @param string $transport_type Post meta syn_transport_type for site * @param object $client Syndication_Client class */ private function log_post_event($event, $result, $post, $site, $transport_type, $client) { if (is_int($post)) { $post = get_post($post, ARRAY_A); } if (isset($post['postmeta']) && isset($post['postmeta']['is_update'])) { $log_time = $post['postmeta']['is_update']; } else { $log_time = null; } $extra = array('post' => $post, 'result' => $result, 'transpost_type' => $transport_type, 'client' => $client); if (false == $result || is_wp_error($result)) { if (is_wp_error($result)) { $message = $result->get_error_message(); } else { $message = 'fail'; } Syndication_Logger::log_post_error($site->ID, $status = __(esc_attr($event), 'push-syndication'), $message, $log_time, $extra); } else { $guid = isset($post['post_guid']) ? sanitize_text_field($post['post_guid']) : sanitize_text_field($post['guid']); $message = sprintf('%s,%d', $guid, intval($result)); Syndication_Logger::log_post_success($site->ID, $status = __(esc_attr($event), 'push-syndication'), $message, $log_time, $extra); } }
/** * Process a new post * * Insert the post into WP, then if successful, * insert the posts meta and terms. * * @param Types\Post $post The post for processing. * @param int $site_id The id of the site being processed. * @param obj $client The syndication client class instance. * * @return int $post_id The ID of the newly inserted post, or false if processing skipped. */ public function process_post(Types\Post $post, $site_id, $client) { global $settings_manager; // @todo hooks // @todo Validate the post. // @todo Bail if post exists and in-progress marker is set. // @todo Mark post as in-progress (if post exists). /** * Filter a post before it is processed for insert/update. * * @param Types\Post $post The post being processing. * * @todo Consider adding $site_id, $slient, $client_transport_type for context. */ $post = apply_filters('syn_before_insert_post', $post); $syndicated_guid = $post->post_data['post_guid']; /** * Filter the $post id retrieved when locating by guid. * * Enabled altering the post id that will be tied to a specific pulled post update. Return a post * id to force updated to happen to that post, return falsde to leave update behavior unchanged. * * @param string $syndicated_guid The post's guid. * @param Types\Post $post The post for processing. * @param int $site_id The id of the site being processed. */ $filtered_post_id = apply_filters('syn_pre_find_post_by_guid', false, $syndicated_guid, $post, $site_id); // Query for posts with a matching syndicated_guid $query_args = array('meta_key' => 'syn_post_guid', 'meta_value' => $syndicated_guid, 'post_status' => 'any', 'post_per_page' => 1); $existing_post_query = new \WP_Query($query_args); // Get the client transport type, passed to some hooks. $client_transport_type = get_post_meta($site_id, 'syn_transport_type', true); // If the post has already been consumed, update it; otherwise insert it. if ($existing_post_query->have_posts() || $filtered_post_id) { // Update existing posts? if ('on' !== $settings_manager->get_setting('update_pulled_posts')) { Syndication_Logger::log_post_info($site_id, $status = 'skip_update_pulled_posts', $message = sprintf(__('skipping post update per update_pulled_posts setting', 'push-syndication')), $log_time = null, $extra = array('post' => $post)); return false; } if (false === $filtered_post_id) { // Existing post, set the post ID for update. $existing_post_query->the_post(); $post->post_data['ID'] = get_the_ID(); } else { $post->post_data['ID'] = $syndicated_guid; } /** * Filter to short circuit the processing of a pulled post update (edit). * * Return true to short circuit the processing of this post update. * * @param bool $edit_shortcircuit Whether to short-circuit the updating of a post. * @param int $site_id The id of the site being processed. * @param string $transport_type The client transport type. * @param obj $client The syndication client class instance. */ $edit_shortcircuit = apply_filters('syn_pre_pull_edit_post_shortcircuit', false, $post, $site_id, $transport_type, $client); if (true === $pull_new_shortcircuit) { Syndication_Logger::log_post_info($site_id, $status = 'syn_pre_pull_edit_post_shortcircuit', $message = sprintf(__('skipping post per syn_pre_pull_edit_post_shortcircuit', 'push-syndication')), $log_time = null, $extra = array('post' => $post)); return false; } // Maintain the post's status. $post->post_data['post_status'] = get_post_status(); /** * Filter the post data, just before updating a post during pull post processing. * * Enables adjusting the data used to update a post. * * @param Types\Post $post The Post object containing the post update data. * @param int $site_id The id of the site being processed. * @param obj $client The syndication client class instance. */ $post = apply_filters('syn_pull_edit_post', $post, $site_id, $client); // Update the existing post. $post_id = wp_update_post($post->post_data, true); /** * Fires just after updating a post during pull post processing. * * @param int $post_id The result of `wp_update_post` (0 if update failed, otherwise post id). * @param Types\Post $post The Post object containing the post update data. * @param int $site_id The id of the site being processed. * @param string $transport_type The client transport type. * @param obj $client The syndication client class instance. */ do_action('syn_post_pull_edit_post', $post_id, $post, $site_id, $transport_type, $client); } else { /** * Filter to short circuit the processing of a pulled post insert. * * Return true to short circuit the processing of this post insert. * * @param bool $insert_shortcircuit Whether to short-circuit the inserting of a post. * @param int $site_id The id of the site being processed. * @param string $transport_type The client transport type. * @param obj $client The syndication client class instance. */ $insert_shortcircuit = apply_filters('syn_pre_pull_new_post_shortcircuit', false, $post, $site_id, $transport_type, $client); if (true === $pull_new_shortcircuit) { Syndication_Logger::log_post_info($site_id, $status = 'syn_pre_pull_edit_post_shortcircuit', $message = sprintf(__('skipping post per syn_pre_pull_new_post_shortcircuit', 'push-syndication')), $log_time = null, $extra = array('post' => $post)); return false; } // Include the syndicated_guid so we can update this post later. $post->post_meta['syn_post_guid'] = $post->post_data['post_guid']; /** * Filter the post data, just before inserting a new post during pull post processing. * * Enables adjusting the data used to insert a new post. * * @param Types\Post $post The Post object containing the post insert data. * @param int $site_id The id of the site being processed. * @param obj $client The syndication client class instance. */ $post = apply_filters('syn_pull_new_post', $post, $site_id, $client); // The post is new, insert it. $post_id = wp_insert_post($post->post_data, true); /** * Fires just after inserting a new post during pull post processing. * * @param int $post_id The result of `wp_update_post` (0 if update failed, otherwise post id). * @param Types\Post $post The Post object containing the post insert data. * @param int $site_id The id of the site being processed. * @param string $transport_type The client transport type. * @param obj $client The syndication client class instance. */ do_action('syn_post_pull_new_post', $post_id, $post, $site, $transport_type, $client); } wp_reset_postdata(); if (!is_wp_error_do_throw($post_id)) { $this->process_post_meta($post_id, $post->post_meta); $this->process_post_terms($post_id, $post->post_terms); } return $post_id; // @todo Mark post as done. }
/** * Handle a site pull failure event * * @param $site_id int The post id of the site we need to retry * @param $failed_attempts int The number of pull failures this site has experienced * * @return null */ public function handle_pull_failure_event($site_id = 0, $failed_attempts = 0) { global $settings_manager; $site_auto_retry_count = 0; $site_id = (int) $site_id; $failed_attempts = (int) $failed_attempts; $cleanup = false; // Fetch the allowable number of max pull attempts before the site is marked as 'disabled' $max_pull_attempts = (int) $settings_manager->get_setting('push_syndication_max_pull_attempts', 0); error_log('max times to retry: ' . $max_pull_attempts); // Bail if auto retry feature disabled if (!$max_pull_attempts) { return; } // Only proceed if we have a valid site id if (0 !== $site_id) { // Fetch the site post $site = get_post($site_id); // Fetch the site url $site_url = get_post_meta($site->ID, 'syn_feed_url', true); // Fetch the number of times we've tried to auto-retry $site_auto_retry_count = (int) get_post_meta($site_id, 'syn_failed_auto_retry_attempts', true); // Store the current time for repeated use below $time_now = time(); // Create a string time to be sent to the logger // Add 1 so our log items appear to occur a second later // and hence order better in the log viewer // without this, sometimes when the pull occurs quickly // these log items appear to occur at the same time as the failure $log_time = date('Y-m-d H:i:s', $time_now + 1); // Only proceed if we haven't hit the pull attempt ceiling if ($failed_attempts < $max_pull_attempts) { /** * Filter the auto retry limit. * * @param int $retry_limit The maximum number of times a client should auto_retry * when failing. Default is 3. */ $auto_retry_limit = apply_filters('pull_syndication_failure_auto_retry_limit', 3); // Are we still below the auto retry limit? if ($site_auto_retry_count <= $auto_retry_limit) { // Yes we are.. // Run in one minute by default /** * Filter the time to next retry when triggering an auto-retry. * * @param int $timestamp The time you want the auto-retry to occur. Default is one * minute from now ( time() + MINUTE_IN_SECONDS ). */ $auto_retry_interval = apply_filters('syndication_failure_auto_retry_interval', $time_now + MINUTE_IN_SECONDS); Syndication_Logger::log_post_info($site->ID, $status = 'start_auto_retry', $message = sprintf(__('Connection retry %d of %d to %s in %s..', 'push-syndication'), $site_auto_retry_count + 1, $auto_retry_limit, $site_url, human_time_diff($time_now, $auto_retry_interval)), '', $extra = array()); // Schedule a pull retry for one minute in the future wp_schedule_single_event($auto_retry_interval, 'syn_pull_content', array(array($site->ID))); // Increment our auto retry counter $site_auto_retry_count++; // And update the post meta auto retry count update_post_meta($site->ID, 'syn_failed_auto_retry_attempts', $site_auto_retry_count); } else { // Auto Retry limit met // Let's cleanup after ourselves $cleanup = true; } } else { // Retry attempt limit met // The site has been disabled, let's cleanup after ourselves $cleanup = true; } // Should we cleanup after ourselves? if ($cleanup) { // Remove the auto retry if there was one delete_post_meta($site->ID, 'syn_failed_auto_retry_attempts'); Syndication_Logger::log_post_error($site->ID, $status = 'end_auto_retry', $message = sprintf(__('Failed %d times to reconnect to %s', 'push-syndication'), ++$site_auto_retry_count, $site_url), $log_time, $extra = array()); } } }