/**
  * Retrieves a list of posts from a remote site.
  *
  * @param   int $site_id The ID of the site to get posts for
  * @return  array|bool   Array of posts on success, false on failure.
  */
 public function get_posts($site_id = 0)
 {
     // create $post with values from $this::node_to_post
     // create $post_meta with values from $this::node_to_meta
     //TODO: required fields for post
     //TODO: handle categories
     $abs_nodes = $item_nodes = $enc_nodes = $tax_nodes = $abs_post_fields = $abs_meta_data = $abs_tax_data = $posts = array();
     $site = get_post($site_id);
     $node_config = get_post_meta($site->ID, 'syn_node_config', true);
     $enc_field = get_post_meta($site->ID, 'syn_enc_field', true);
     /**
      * Filter the XML pull client feed url.
      *
      * @param string $feed_url The site's feed url.
      * @todo Consider adding $site_id for context.
      */
     $feed_url = apply_filters('syn_feed_url', get_post_meta($site->ID, 'syn_feed_url', true));
     $enclosures_as_strings = isset($node_config['enclosures_as_strings']) ? true : false;
     $id_field = get_post_meta($site_id, 'syn_id_field', true);
     $enc_is_photo = get_post_meta($site_id, 'syn_enc_is_photo', true);
     $new_posts = array();
     //TODO: add checkbox on feed config to allow enclosures to be saved as strings as SI does
     //TODO: add tags here and in feed set up UI
     foreach ($node_config['nodes'] as $key => $storage_locations) {
         foreach ($storage_locations as $storage_location) {
             $storage_location['xpath'] = $key;
             if ($storage_location['is_item']) {
                 $item_nodes[] = $storage_location;
             } else {
                 if ($storage_location['is_photo']) {
                     $enc_nodes[] = $storage_location;
                 } else {
                     if ($storage_location['is_tax'] && $storage_location['is_item']) {
                         $tax_nodes[] = $storage_location;
                     } else {
                         $abs_nodes[] = $storage_location;
                     }
                 }
             }
         }
     }
     // Fetch the XML feed
     $feed = $this->remote_get($feed_url);
     if (Syndication\is_wp_error_do_throw($feed)) {
         Syndication\Syndication_Logger::log_post_error($site->ID, $status = 'error', $message = sprintf(__('Could not reach feed at: %s | Error: %s', 'push-syndication'), $feed_url, $feed->get_error_message()), $log_time = null, $extra = array());
         /* This action is documented in includes/clients/rss-pull/class-pull-client.php */
         do_action('push_syndication_event', 'pull_failure', $site->ID);
         return array();
     } else {
         Syndication\Syndication_Logger::log_post_info($site->ID, $status = 'fetch_feed', $message = sprintf(__('fetched feed with %d bytes', 'push-syndication'), strlen($feed)), $log_time = null, $extra = array());
     }
     /**
      * @var SimpleXMLElement $xml
      */
     $xml = simplexml_load_string($feed, null, 0, $node_config['namespace'], false);
     if (false === $xml) {
         Syndication\Syndication_Logger::log_post_error($site->ID, $status = 'error', $message = sprintf(__('Failed to parse feed at: %s', 'push-syndication'), $feed_url), $log_time = null, $extra = array());
         /* This action is documented in includes/clients/rss-pull/class-pull-client.php */
         do_action('push_syndication_event', 'pull_failure', $site->ID);
         return array();
     }
     $abs_post_fields['enclosures_as_strings'] = $enclosures_as_strings;
     // TODO: handle constant strings in XML
     foreach ($abs_nodes as $abs_node) {
         $value_array = array();
         if ('string(' == substr($abs_node['xpath'], 0, 7)) {
             $value_array[0] = substr($abs_node['xpath'], 7, strlen($abs_node['xpath']) - 8);
         } else {
             $value_array = $xml->xpath(stripslashes($abs_node['xpath']));
         }
         if ($abs_node['is_meta']) {
             if (isset($value_array[0]) && !empty($value_array[0])) {
                 $abs_meta_data[$abs_node['field']] = (string) $value_array[0];
             }
         } else {
             if ($abs_node['is_tax']) {
                 if (isset($value_array[0]) && !empty($value_array[0])) {
                     $abs_tax_data[$abs_node['field']] = (string) $value_array[0];
                 }
             } else {
                 if (isset($value_array[0]) && !empty($value_array[0])) {
                     $abs_post_fields[$abs_node['field']] = (string) $value_array[0];
                 }
             }
         }
     }
     $items = $xml->xpath($node_config['post_root']);
     if (empty($items)) {
         Syndication\Syndication_Logger::log_post_error($site->ID, $status = 'error', $message = printf(__('No post nodes found using XPath "%s" in feed', 'push-syndication'), $node_config['post_root']), $log_time = $site->postmeta['is_update'], $extra = array());
         return array();
     } else {
         Syndication\Syndication_Logger::log_post_info($site->ID, $status = 'simplexml_load_string', $message = sprintf(__('parsed feed, received %d items', 'push-syndication'), count($items)), $log_time = null, $extra = array());
     }
     foreach ($items as $item_index => $item) {
         // @todo flush out how the post is actually created
         $new_post = new Types\Post();
         $meta_data = $tax_data = $value_array = array();
         $meta_data['is_update'] = current_time('mysql');
         $new_post->post_data['post_type'] = get_post_meta($site->ID, 'syn_default_post_type', true);
         //save photos as enclosures in meta
         if (isset($node_config['enc_parent']) && strlen($node_config['enc_parent']) && !empty($enc_nodes)) {
             $meta_data['enclosures'] = $this->get_enclosures($item->xpath($node_config['enc_parent']), $enc_nodes, $enc_is_photo);
             // This is wonky and messed up, @todo repair me please
             // the old implementation in 2.0 (in class-syndication-wp-xml-client.php:876)
             // used the following logic (named updated for refactor)
             // however, they purposefully only saved the last enclosure
             foreach ($meta_data['enclosures'] as $enclosure) {
                 if (defined('ENCLOSURES_AS_STRINGS') && constant('ENCLOSURES_AS_STRINGS')) {
                     $enclosure = implode("\n", $enclosure);
                 }
                 $new_post->post_meta['enc_field'] = $enclosure;
             }
         }
         foreach ($item_nodes as $save_location) {
             if ('string(' == substr($save_location['xpath'], 0, 7)) {
                 $value_array[0] = substr($save_location['xpath'], 7, strlen($save_location['xpath']) - 8);
             } else {
                 $value_array = $item->xpath(stripslashes($save_location['xpath']));
             }
             if (isset($save_location['is_meta']) && $save_location['is_meta']) {
                 //SimpleXMLElement::xpath returns either an array or false if an element isn't returned
                 //checking $value_array first avoids the warning we get if the field isn't found
                 if ($value_array && count($value_array) > 1) {
                     $value_array = array_map('strval', $value_array);
                     $meta_data[$save_location['field']] = $value_array;
                 } else {
                     if ($value_array) {
                         //return a string if $value_array contains only a single element
                         $meta_data[$save_location['field']] = (string) $value_array[0];
                     }
                 }
             } else {
                 if (isset($save_location['is_tax']) && $save_location['is_tax']) {
                     //for some taxonomies, multiple values may be supplied in the field
                     foreach ($value_array as $value) {
                         $tax_data[$save_location['field']] = (string) $value;
                     }
                 } else {
                     $new_post->post_data[$save_location['field']] = (string) $value_array[0];
                 }
             }
         }
         // get this into the tax data
         $new_post->post_data['post_category'] = $node_config['categories'];
         // get this into the meta data
         $meta_data['site_id'] = $site->ID;
         $meta_data = array_merge($meta_data, $abs_meta_data);
         $tax_data = array_merge($tax_data, $abs_tax_data);
         if (!empty($enc_field)) {
             $meta_data['enc_field'] = $enc_field;
         }
         if (!isset($meta_data['position'])) {
             $meta_data['position'] = $item_index;
         }
         if (!empty($meta_data)) {
             $new_post->post_meta = $meta_data;
         }
         if (!empty($tax_data)) {
             $new_post->post_terms = $tax_data;
         }
         if (!empty($meta_data[$id_field])) {
             $new_post->post_data['guid'] = $meta_data[$id_field];
         }
         $new_posts[] = $new_post;
     }
     Syndication\Syndication_Logger::log_post_info($site->ID, $status = 'posts_received', $message = sprintf(__('%d posts were prepared', 'push-syndication'), count($new_posts)), $log_time = null, $extra = array());
     /* This action is documented in includes/clients/rss-pull/class-pull-client.php */
     do_action('push_syndication_event', 'pull_success', $site->ID);
     return $new_posts;
 }
 /**
  *
  */
 public function prepare_items()
 {
     $columns = $this->get_columns();
     $hidden = array();
     $sortable = $this->get_sortable_columns();
     $this->_column_headers = array($columns, $hidden, $sortable);
     $log_id = isset($_REQUEST['log_id']) ? esc_attr($_REQUEST['log_id']) : null;
     $msg_type = null;
     $object_id = null;
     $object_type = 'post';
     $log_status = null;
     $date_start = null;
     $date_end = null;
     $message = null;
     $storage_type = 'object';
     $log_data = Syndication_Logger::instance()->get_messages($log_id, $msg_type, $object_id, $object_type, $log_status, $date_start, $date_end, $message, $storage_type);
     foreach ($log_data as $site_id => $log_items) {
         $this->prepared_data = array_merge($this->prepared_data, $log_items);
     }
     usort($this->prepared_data, array($this, 'usort_reorder'));
     $per_page = $this->get_items_per_page('per_page');
     $current_page = $this->get_pagenum();
     $total_items = count($this->prepared_data);
     $this->found_data = array_slice($this->prepared_data, ($current_page - 1) * $per_page, $per_page);
     // Populate min/max dates.
     if ($this->found_data) {
         $items_sorted_by_time = $this->found_data;
         usort($items_sorted_by_time, function ($a, $b) {
             return strtotime($a['time']) - strtotime($b['time']);
         });
         $this->_max_date = strtotime(end($items_sorted_by_time)['time']);
         $this->_min_date = strtotime(reset($items_sorted_by_time)['time']);
     }
     // Filter by month
     $requested_month = isset($_REQUEST['month']) ? esc_attr($_REQUEST['month']) : null;
     if ($requested_month) {
         $this->found_data = array_filter($this->found_data, function ($item) use($requested_month) {
             return date('Y-m', strtotime($item['time'])) === $requested_month;
         });
     }
     // Filter by type
     $requested_type = isset($_REQUEST['type']) ? esc_attr($_REQUEST['type']) : null;
     if ($requested_type) {
         $this->found_data = array_filter($this->found_data, function ($item) use($requested_type) {
             return $requested_type === $item['msg_type'];
         });
     }
     $this->set_pagination_args(array('total_items' => $total_items, 'per_page' => $per_page));
     $this->items = $this->found_data;
 }