/**
  * 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());
         }
     }
 }