function posts_request($sql, &$query) { if ($query->get('fields') == '_synfresh') { FeedWordPress::diagnostic('feed_items:freshness:sql', "SQL: " . $sql); } return $sql; }
public function attach_image($url, $to, $args = array()) { $attach_id = NULL; $p = wp_parse_args($args, array("crop" => NULL, "resize" => NULL)); # Fetch the URI $headers['Connection'] = 'close'; $headers['Referer'] = get_permalink($to); if (is_callable(array('FeedWordPress', 'fetch_timeout'))) { $timeout = FeedWordPress::fetch_timeout(); } elseif (defined('FEEDWORDPRESS_FETCH_TIME_OUT')) { $timeout = FEEDWORDPRESS_FETCH_TIME_OUT; } elseif (defined('FEEDWORDPRESS_FETCH_TIMEOUT_DEFAULT')) { $timeout = FEEDWORDPRESS_FETCH_TIMEOUT_DEFAULT; } else { $timeout = 60; } FeedWordPress::diagnostic('sicem:capture:http', "HTTP »» GET [{$url}]"); $params = apply_filters('sicem_remote_request_params', array('headers' => $headers, 'timeout' => $timeout), $url); $http = apply_filters('sicem_remote_request', NULL, $url, $params); if (is_null($http)) { $http = wp_remote_request($url, $params); } $imgBits = new SicWebImage($url, $params, $http); if ($imgBits->is_ok() and $imgBits->is_image()) { // Check whether our size filters or MIME constraints filter it out $imagesize = $imgBits->size(); if (!is_null($imagesize)) { $minWidth = isset($args['min width']) ? $args['min width'] : 0; $minHeight = isset($args['min height']) ? $args['min height'] : 0; if ($imagesize[0] < $minWidth or $imagesize[1] < $minHeight or !$this->allowedtype($imagesize['mime'], $args)) { FeedWordPress::diagnostic('sicem:capture:reject', "Image [{$url}] rejected. " . ($imagesize[0] < $minWidth ? 'width: ' . $imagesize[0] . ' < ' . $minWidth . '. ' : '') . ($imagesize[1] < $minHeight ? 'height: ' . $imagesize[1] . ' < ' . $minHeight . '. ' : '') . (!$this->allowedtype($imagesize['mime'], $args) ? 'type [' . $imagesize['mime'] . ']: whitelist [' . implode('|', $args['whitelist']) . '] blacklist [' . implode('|', $args['blacklist']) . '].' : '')); $imgBits->set_image(NULL, NULL); } } // Apply (if applicable) crop and resize settings $imgBits->constrain($p['crop'], $p['resize']); if ($imgBits->is_image()) { $attach_id = $imgBits->upload($to); } else { $attach_id = -1; // Filtered } } else { // Got a WP_Error object back instead of a HTTP GET reply if (is_wp_error($http)) { $error_message = preg_replace('/\\s+/', " ", "WP_Error: " . implode(" / ", $http->get_error_messages())); // Got a HTTP GET reply other than 200 OK. } elseif (is_array($http) and isset($http['response'])) { $code = $http['response']['code']; $mesg = $http['response']['message']; $pcode = preg_replace('/\\s+/', '\\s+', preg_quote($code)); $pmesg = preg_replace('/\\s+/', '\\s+', preg_quote($mesg)); $pattern = ":<([^>]+)> \\s* ({$pcode}\\s*)? {$pmesg} \\s*</\\1>:ix"; $stripped_body = strip_tags(preg_replace($pattern, '', $http['body'])); $len = 66; $error_message = preg_replace('/\\s+/', " ", "{$code} {$mesg}: " . substr($stripped_body, 0, $len) . (strlen($stripped_body) > $len ? "…" : '')); // Well, who knows what the hell is going on, really? } else { $error_message = preg_replace('/\\s+/', " ", FeedWordPress::val($http)); } // Send it to the diagnostix module. FeedWordPress::diagnostic('sicem:capture:error', "Failed GET [{$url}] «« " . $error_message); } return $attach_id; }
function process_retirements ($delta) { global $post; $q = new WP_Query(array( 'fields' => '_synfrom', 'post_status__not' => 'fwpretired', 'ignore_sticky_posts' => true, 'meta_key' => '_feedwordpress_retire_me_'.$this->id, 'meta_value' => '1', )); if ($q->have_posts()) : foreach ($q->posts as $p) : $old_status = $p->post_status; FeedWordPress::diagnostic('syndicated_posts', 'Retiring existing post # '.$p->ID.' "'.$p->post_title.'" due to absence from a non-incremental feed.'); set_post_field('post_status', 'fwpretired', $p->ID); wp_transition_post_status('fwpretired', $old_status, $p); delete_post_meta($p->ID, '_feedwordpress_retire_me_'.$this->id); endforeach; endif; return $delta; }
function feed_error($error, $old, $link) { $wpError = $error['object']; $url = $link->uri(); $mesgs = $wpError->get_error_messages(); foreach ($mesgs as $mesg) { $mesg = esc_html($mesg); FeedWordPress::diagnostic('updated_feeds:errors', "Feed Error: [{$url}] update returned error: {$mesg}"); $hours = get_option('feedwordpress_diagnostics_persistent_errors_hours', 2); $span = $error['ts'] - $error['since']; if ($span >= $hours * 60 * 60) { $since = date('r', $error['since']); $mostRecent = date('r', $error['ts']); FeedWordPress::diagnostic('updated_feeds:errors:persistent', "Feed Update Error: [{$url}] returning errors" . " since {$since}:<br/><code>{$mesg}</code>", $url, $error['since'], $error['ts']); } } }
public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false) { global $feedwordpress; global $wp_version; $source = NULL; if ($feedwordpress->subscribed($url)) { $source = $feedwordpress->subscription($url); } $this->url = $url; $this->timeout = $timeout; $this->redirects = $redirects; $this->headers = $headers; $this->useragent = $useragent; $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE; global $wpdb; global $fwp_credentials; if (preg_match('/^http(s)?:\\/\\//i', $url)) { $args = array('timeout' => $this->timeout, 'redirection' => $this->redirects); if (!empty($this->headers)) { $args['headers'] = $this->headers; } // Use default FWP user agent unless custom has been specified if (SIMPLEPIE_USERAGENT != $this->useragent) { $args['user-agent'] = $this->useragent; } else { $args['user-agent'] = apply_filters('feedwordpress_user_agent', 'FeedWordPress/' . FEEDWORDPRESS_VERSION . ' (aggregator:feedwordpress; WordPress/' . $wp_version . ' + ' . SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION . '; Allow like Gecko; +http://feedwordpress.radgeek.com/) at ' . feedwordpress_display_url(get_bloginfo('url')), $this); } // This is ugly as hell, but communicating up and down the chain // in any other way is difficult. if (!is_null($fwp_credentials)) { $args['authentication'] = $fwp_credentials['authentication']; $args['username'] = $fwp_credentials['username']; $args['password'] = $fwp_credentials['password']; } elseif ($source instanceof SyndicatedLink) { $args['authentication'] = $source->authentication_method(); $args['username'] = $source->username(); $args['password'] = $source->password(); } FeedWordPress::diagnostic('updated_feeds:http', "HTTP [{$url}] ⇜ " . esc_html(MyPHP::val($args))); $res = wp_remote_request($url, $args); FeedWordPress::diagnostic('updated_feeds:http', "HTTP [{$url}] ⇝ " . esc_html(MyPHP::val($res))); if (is_wp_error($res)) { $this->error = 'WP HTTP Error: ' . $res->get_error_message(); $this->success = false; } else { $this->headers = wp_remote_retrieve_headers($res); $this->body = wp_remote_retrieve_body($res); $this->status_code = wp_remote_retrieve_response_code($res); } if ($source instanceof SyndicatedLink) { $source->update_setting('link/filesize', strlen($this->body)); $source->update_setting('link/http status', $this->status_code); $source->save_settings(true); } // Do not allow schemes other than http(s)? for the time being. // They are unlikely to be used; and unrestricted use of schemes // allows for user to use an unrestricted file:/// scheme, which // may result in exploits by WordPress users against the web // hosting environment. } else { $this->error = 'FeedWordPress only allows http or https URLs'; $this->success = false; } // SimplePie makes a strongly typed check against integers with // this, but WordPress puts a string in. Which causes caching // to break and fall on its ass when SimplePie is getting a 304, // but doesn't realize it because this member is "304" instead. $this->status_code = (int) $this->status_code; }
function debug_out_feedwordpress_footer() { if (FeedWordPress::diagnostic_on('memory_usage')) { if (function_exists('memory_get_usage')) { FeedWordPress::diagnostic('memory_usage', "Memory: Current usage: " . debug_out_human_readable_bytes(memory_get_usage())); } if (function_exists('memory_get_peak_usage')) { FeedWordPress::diagnostic('memory_usage', "Memory: Peak usage: " . debug_out_human_readable_bytes(memory_get_peak_usage())); } } }
/** * category_ids: look up (and create) category ids from a list of * categories * * @param array $cats * @param string $unfamiliar_category * @param array|null $taxonomies * @return array */ function category_ids($post, $cats, $unfamiliar_category = 'create', $taxonomies = NULL, $params = array()) { $singleton = isset($params['singleton']) ? $params['singleton'] : true; $allowFilters = isset($params['filters']) ? $params['filters'] : false; $catTax = 'category'; if (is_null($taxonomies)) { $taxonomies = array('category'); } // We need to normalize whitespace because (1) trailing // whitespace can cause PHP and MySQL not to see eye to eye on // VARCHAR comparisons for some versions of MySQL (cf. // <http://dev.mysql.com/doc/mysql/en/char.html>), and (2) // because I doubt most people want to make a semantic // distinction between 'Computers' and 'Computers ' $cats = array_map('trim', $cats); $terms = array(); foreach ($taxonomies as $tax) { $terms[$tax] = array(); } foreach ($cats as $cat_name) { if (strlen(trim($cat_name)) < 1) { continue; } $oTerm = new SyndicatedPostTerm($cat_name, $taxonomies, $post); if ($oTerm->is_familiar()) { $tax = $oTerm->taxonomy(); if (!isset($terms[$tax])) { $terms[$tax] = array(); } $terms[$tax][] = $oTerm->id(); } else { if ('tag' == $unfamiliar_category) { $unfamiliar_category = 'create:post_tag'; } if (preg_match('/^create(:(.*))?$/i', $unfamiliar_category, $ref)) { $tax = $catTax; // Default if (isset($ref[2]) and strlen($ref[2]) > 2) { $tax = $ref[2]; } $inserted = $oTerm->insert($tax); if (!is_null($inserted)) { if (!isset($terms[$tax])) { $terms[$tax] = array(); } $terms[$tax][] = $inserted; } else { } // !is_null($inserted) } // preg_match(...) } /* ($oTerm->is_familiar()) */ } $filtersOn = $allowFilters; if ($allowFilters) { $filters = array_filter($this->setting('match/filter', 'match_filter', array()), 'remove_dummy_zero'); $filtersOn = ($filtersOn and is_array($filters) and count($filters) > 0); } // Check for filter conditions foreach ($terms as $tax => $term_ids) { if ($filtersOn and count($term_ids) == 0 and in_array($tax, $filters)) { $terms = NULL; // Drop the post break; } else { $terms[$tax] = array_unique($term_ids); } } if ($singleton and count($terms) == 1) { // If we only searched one, just return the term IDs $terms = end($terms); } FeedWordPress::diagnostic('syndicated_posts:categories', 'Category: MAPPED term names ' . json_encode($cats) . ' to IDs: ' . json_encode($terms)); return $terms; }
/** * SICWebImage::upload () * * @return int The numeric ID of the attachment created in the WordPress * media gallery as a result of uploading this image. -1 if upload failed */ function upload($attach_to) { $attach_id = NULL; # Now send the image to the upload directory $up = wp_upload_bits($this->local_filename(), $this->mimetype(), $this->data()); if ($up and !$up['error']) { # Now do the attachment $attach = array('post_mime_type' => $this->mimetype(), 'post_title' => $this->post_title(), 'post_content' => '', 'post_status' => 'inherit', 'guid' => self::guid($this->url())); $attach_id = wp_insert_attachment($attach, $up['file'], $attach_to); require_once ABSPATH . 'wp-admin' . '/includes/image.php'; $attach_data = wp_generate_attachment_metadata($attach_id, $up['file']); wp_update_attachment_metadata($attach_id, $attach_data); } else { if (is_array($up) and isset($up['error'])) { $error_message = $up['error']; } else { $error_message = preg_replace('/\\s+/', " ", FeedWordPress::val($up)); } FeedWordPress::diagnostic('sicem:capture:error', "Failed image storage [{$url}]: {$error_message}"); } return $attach_id; }
function add_rss_meta($new_status, $old_status, $post) { FeedWordPress::diagnostic('syndicated_posts:meta_data', 'Adding post meta-data: {' . implode(", ", array_keys($this->post['meta'])) . '}'); // not saving the post we're processing; bail. if ($post->ID != $this->wp_id()) { return; } if (is_array($this->post) and isset($this->post['meta']) and is_array($this->post['meta'])) { $postId = $post->ID; // Aggregated posts should NOT send out pingbacks. // WordPress 2.1-2.2 claim you can tell them not to // using $post_pingback, but they don't listen, so we // make sure here. $result = delete_post_meta($postId, '_pingme'); foreach ($this->post['meta'] as $key => $values) { // If this is an update, clear out the old // values to avoid duplication. $result = delete_post_meta($postId, $key); // Allow for either a single value or an array if (!is_array($values)) { $values = array($values); } foreach ($values as $value) { FeedWordPress::diagnostic('syndicated_posts:meta_data', "Adding post meta-datum to post [{$postId}]: [{$key}] = " . FeedWordPress::val($value, true)); add_post_meta($postId, $key, $value, false); } } } }
static function admin_init() { // WordPress 3.5+ compat: the WP devs are in the midst of removing Links from the WordPress core. Eventually we'll have to deal // with the possible disappearance of the wp_links table as a whole; but in the meantime, we just need to turn on the interface // element to avoid problems with user capabilities that presume the presence of the Links Manager in the admin UI. if (!intval(get_option('link_manager_enabled', false))) { update_option('link_manager_enabled', true); } if (defined('FEEDWORDPRESS_PREPARE_TO_ZAP') and FEEDWORDPRESS_PREPARE_TO_ZAP) { $post_id = FEEDWORDPRESS_PREPARE_TO_ZAP; $sendback = wp_get_referer(); if (!$sendback or strpos($sendback, 'post.php') !== false or strpos($sendback, 'post-new.php') !== false) { if ('attachment' == $post_type) { $sendback = admin_url('upload.php'); } else { $sendback = admin_url('edit.php'); $sendback .= !empty($post_type) ? '?post_type=' . $post_type : ''; } } else { $sendback = esc_url(remove_query_arg(array('trashed', 'untrashed', 'deleted', 'zapped', 'unzapped', 'ids'), $sendback)); } // Make sure we have a post corresponding to this ID. $post = $post_type = $post_type_object = null; if ($post_id) { $post = get_post($post_id); } if ($post) { $post_type = $post->post_type; } $p = get_post($post_id); if (!$post) { wp_die(__('The item you are trying to zap no longer exists.')); } if (!current_user_can('delete_post', $post_id)) { wp_die(__('You are not allowed to zap this item.')); } if ($user_id = wp_check_post_lock($post_id)) { $user = get_userdata($user_id); wp_die(sprintf(__('You cannot retire this item. %s is currently editing.'), $user->display_name)); } if (MyPHP::request('fwp_post_delete') == 'zap') { FeedWordPress::diagnostic('syndicated_posts', 'Zapping existing post # ' . $p->ID . ' "' . $p->post_title . '" due to user request.'); $old_status = $post->post_status; set_post_field('post_status', 'fwpzapped', $post_id); wp_transition_post_status('fwpzapped', $old_status, $post); # Set up the post to have its content blanked on # next update if you do not undo the zapping. add_post_meta($post_id, '_feedwordpress_zapped_blank_me', 1, true); add_post_meta($post_id, '_feedwordpress_zapped_blank_old_status', $old_status, true); wp_redirect(esc_url_raw(add_query_arg(array('zapped' => 1, 'ids' => $post_id), $sendback))); } else { $old_status = get_post_meta($post_id, '_feedwordpress_zapped_blank_old_status', true); set_post_field('post_status', $old_status, $post_id); wp_transition_post_status($old_status, 'fwpzapped', $post); # O.K., make sure this post does not get blanked delete_post_meta($post_id, '_feedwordpress_zapped_blank_me'); delete_post_meta($post_id, '_feedwordpress_zapped_blank_old_status'); wp_redirect(esc_url_raw(add_query_arg(array('unzapped' => 1, 'ids' => $post_id), $sendback))); } // Intercept, don't pass on. exit; } }
function process_expirations($delta) { global $post; global $wpdb; $q = new WP_Query(array('post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => get_option('fwplpbd_expiration_chunk', 25), 'meta_key' => '_syndication_expiration_date', 'meta_value' => time(), 'meta_compare' => '<=')); while ($q->have_posts()) { $q->the_post(); $expiration = get_post_meta($post->ID, '_syndication_expiration_date', true); if ((int) $expiration and (int) $expiration <= time()) { $action = get_post_meta($post->ID, '_syndication_expiration_action', true); FeedWordPress::diagnostic('expiration', 'Post [' . $post->ID . '] "' . esc_html($post->post_title) . '" expired as of ' . date('r', (int) $expiration) . ' and will now be ' . ('trash' == $action ? 'trashed' : ('nuke' == $action ? 'deleted permanently' : 'hidden'))); switch ($action) { case 'trash': case 'nuke': $feed = get_syndication_feed_object($post->ID); $thumbId = get_post_thumbnail_id($post->ID); wp_delete_post($post->ID, 'nuke' == $action); // Check to see whether any other posts // use this as a Featured Image. If not // then zap it. if ("nuke" == $feed->setting('post expiration thumbnail', 'post_expiration_thumbnail', "keep")) { if (strlen($thumbId) > 0) { $qrows = $wpdb->get_results($wpdb->prepare("\n\t\t\t\t\t\t\tSELECT meta_value FROM {$wpdb->postmeta}\n\t\t\t\t\t\t\tWHERE meta_key = '_thumbnail_id'\n\t\t\t\t\t\t\tAND meta_value = '%d'\n\t\t\t\t\t\t\tAND post_id <> '%d'\n\t\t\t\t\t\t\t", $thumbId, $post->ID)); if (count($qrows) < 1) { FeedWordPress::diagnostic('expiration', 'The expired post [' . $post->ID . '] had an attached Featured Image, which is not used as the Featured Image for any other post. We will now clean up and the image will be ' . ('trash' == $action ? 'trashed' : ('nuke' == $action ? 'deleted permanently' : 'hidden'))); wp_delete_attachment($thumbId, 'nuke' == $action); } else { FeedWordPress::diagnostic('expiration', 'The expired post [' . $post->ID . '] had an attached Featured Image, but it CANNOT be deleted right now because at least one other post uses the same image as its Featured Image.'); } } } break; case 'hide': case 'redirect': default: $old_status = $post->post_status; set_post_field('post_status', 'expired', $post->ID); wp_transition_post_status('expired', $old_status, $post); break; } } } }
function poll($crash_ts = NULL) { global $wpdb; $url = $this->uri(array('add_params' => true)); FeedWordPress::diagnostic('updated_feeds', 'Polling feed [' . $url . ']'); $timeout = $this->setting('fetch timeout', 'feedwordpress_fetch_timeout', FEEDWORDPRESS_FETCH_TIMEOUT_DEFAULT); $this->simplepie = apply_filters('syndicated_feed', FeedWordPress::fetch($url, array('timeout' => $timeout)), $this); // Filter compatibility mode if (is_wp_error($this->simplepie)) { $this->magpie = $this->simplepie; } else { $this->magpie = new MagpieFromSimplePie($this->simplepie, NULL); } $new_count = NULL; $resume = FeedWordPress::affirmative($this->settings, 'update/unfinished'); if ($resume) { // pick up where we left off $processed = array_map('trim', explode("\n", $this->settings['update/processed'])); } else { // begin at the beginning $processed = array(); } if (is_wp_error($this->simplepie)) { $new_count = $this->simplepie; // Error; establish an error setting. $theError = array(); $theError['ts'] = time(); $theError['since'] = time(); $theError['object'] = $this->simplepie; $oldError = $this->setting('update/error', NULL, NULL); if (is_string($oldError)) { $oldError = unserialize($oldError); } if (!is_null($oldError)) { // Copy over the in-error-since timestamp $theError['since'] = $oldError['since']; // If this is a repeat error, then we should take // a step back before we try to fetch it again. $this->settings['update/last'] = time(); $this->settings['update/ttl'] = $this->automatic_ttl(); $this->settings['update/ttl'] = apply_filters('syndicated_feed_ttl', $this->settings['update/ttl'], $this); $this->settings['update/ttl'] = apply_filters('syndicated_feed_ttl_from_error', $this->settings['update/ttl'], $this); $this->settings['update/timed'] = 'automatically'; } do_action('syndicated_feed_error', $theError, $oldError, $this); $this->settings['update/error'] = serialize($theError); $this->save_settings(true); } elseif (is_object($this->simplepie)) { // Success; clear out error setting, if any. if (isset($this->settings['update/error'])) { unset($this->settings['update/error']); } $new_count = array('new' => 0, 'updated' => 0); # -- Update Link metadata live from feed $channel = $this->magpie->channel; if (!isset($channel['id'])) { $channel['id'] = $this->link->link_rss; } $update = array(); if (!$this->hardcode('url') and isset($channel['link'])) { $update[] = "link_url = '" . $wpdb->escape($channel['link']) . "'"; } if (!$this->hardcode('name') and isset($channel['title'])) { $update[] = "link_name = '" . $wpdb->escape($channel['title']) . "'"; } if (!$this->hardcode('description')) { if (isset($channel['tagline'])) { $update[] = "link_description = '" . $wpdb->escape($channel['tagline']) . "'"; } elseif (isset($channel['description'])) { $update[] = "link_description = '" . $wpdb->escape($channel['description']) . "'"; } } $this->settings = array_merge($this->settings, $this->flatten_array($channel)); $this->settings['update/last'] = time(); list($ttl, $xml) = $this->ttl(true); if (!is_null($ttl)) { $this->settings['update/ttl'] = $ttl; $this->settings['update/xml'] = $xml; $this->settings['update/timed'] = 'feed'; } else { $ttl = $this->automatic_ttl(); $this->settings['update/ttl'] = $ttl; $this->settings['update/xml'] = NULL; $this->settings['update/timed'] = 'automatically'; } $this->settings['update/fudge'] = rand(0, $ttl / 3) * 60; $this->settings['update/ttl'] = apply_filters('syndicated_feed_ttl', $this->setting('update/ttl'), $this); if (!$this->setting('update/hold') != 'ping') { $this->settings['update/hold'] = 'scheduled'; } $this->settings['update/unfinished'] = 'yes'; $update[] = "link_notes = '" . $wpdb->escape($this->settings_to_notes()) . "'"; $update_set = implode(',', $update); // Update the properties of the link from the feed information $result = $wpdb->query("\n\t\t\t\tUPDATE {$wpdb->links}\n\t\t\t\tSET {$update_set}\n\t\t\t\tWHERE link_id='{$this->id}'\n\t\t\t"); do_action('update_syndicated_feed', $this->id, $this); # -- Add new posts from feed and update any updated posts $crashed = false; $posts = apply_filters('syndicated_feed_items', $this->simplepie->get_items(), &$this); $this->magpie->originals = $posts; if (is_array($posts)) { foreach ($posts as $key => $item) { $post = new SyndicatedPost($item, $this); if (!$resume or !in_array(trim($post->guid()), $processed)) { $processed[] = $post->guid(); if (!$post->filtered()) { $new = $post->store(); if ($new !== false) { $new_count[$new]++; } } if (!is_null($crash_ts) and time() > $crash_ts) { $crashed = true; break; } } unset($post); } } $suffix = $crashed ? 'crashed' : 'completed'; do_action('update_syndicated_feed_items', $this->id, $this); do_action("update_syndicated_feed_items_{$suffix}", $this->id, $this); // Copy back any changes to feed settings made in the course of updating (e.g. new author rules) $to_notes = $this->settings; $this->settings['update/processed'] = $processed; if (!$crashed) { $this->settings['update/unfinished'] = 'no'; } $update_set = "link_notes = '" . $wpdb->escape($this->settings_to_notes()) . "'"; // Update the properties of the link from the feed information $result = $wpdb->query("\n\t\t\tUPDATE {$wpdb->links}\n\t\t\tSET {$update_set}\n\t\t\tWHERE link_id='{$this->id}'\n\t\t\t"); do_action("update_syndicated_feed_completed", $this->id, $this); } // All done; let's clean up. $this->magpie = NULL; // Avoid circular-reference memory leak in PHP < 5.3. // Cf. <http://simplepie.org/wiki/faq/i_m_getting_memory_leaks> if (method_exists($this->simplepie, '__destruct')) { $this->simplepie->__destruct(); } $this->simplepie = NULL; return $new_count; }
function poll($crash_ts = NULL) { FeedWordPress::diagnostic('updated_feeds', 'Polling feed <' . $this->link->link_rss . '>'); $this->simplepie = apply_filters('syndicated_feed', FeedWordPress::fetch($this->link->link_rss), $this); // Filter compatibility mode if (is_wp_error($this->simplepie)) { $this->magpie = $this->simplepie; } else { $this->magpie = new MagpieFromSimplePie($this->simplepie); } $new_count = NULL; $resume = FeedWordPress::affirmative($this->settings, 'update/unfinished'); if ($resume) { // pick up where we left off $processed = array_map('trim', explode("\n", $this->settings['update/processed'])); } else { // begin at the beginning $processed = array(); } if (is_wp_error($this->simplepie)) { $new_count = $this->simplepie; // Error; establish an error setting. $theError = array(); $theError['ts'] = time(); $theError['since'] = time(); $theError['object'] = $this->simplepie; $oldError = $this->setting('update/error', NULL, NULL); if (is_string($oldError)) { $oldError = unserialize($oldError); } if (!is_null($oldError)) { // Copy over the in-error-since timestamp $theError['since'] = $oldError['since']; // If this is a repeat error, then we should take // a step back before we try to fetch it again. $this->settings['update/last'] = time(); $this->settings['update/ttl'] = $this->automatic_ttl(); $this->settings['update/ttl'] = apply_filters('syndicated_feed_ttl', $this->settings['update/ttl'], $this); $this->settings['update/ttl'] = apply_filters('syndicated_feed_ttl_from_error', $this->settings['update/ttl'], $this); $this->settings['update/timed'] = 'automatically'; } $this->settings['update/error'] = serialize($theError); $this->save_settings(true); } elseif (is_object($this->simplepie)) { // Grab our bookmark we're going to be updating $link = get_bookmark($this->id, ARRAY_A); if (is_array($link) && !empty($link)) { // Success; clear out error setting, if any. if (isset($this->settings['update/error'])) { unset($this->settings['update/error']); } $new_count = array('new' => 0, 'updated' => 0); # -- Update Link metadata live from feed $channel = $this->magpie->channel; if (!isset($channel['id'])) { $channel['id'] = $this->link->link_rss; } if (!$this->hardcode('url') and isset($channel['link'])) { $link['link_url'] = $channel['link']; } if (!$this->hardcode('name') and isset($channel['title'])) { $link['link_name'] = $channel['title']; } if (!$this->hardcode('description')) { if (isset($channel['tagline'])) { $link['link_description'] = $channel['tagline']; } elseif (isset($channel['description'])) { $link['link_description'] = $channel['description']; } } $this->settings = array_merge($this->settings, $this->flatten_array($channel)); $this->settings['update/last'] = time(); $ttl = $this->ttl(); if (!is_null($ttl)) { $this->settings['update/ttl'] = $ttl; $this->settings['update/timed'] = 'feed'; } else { $this->settings['update/ttl'] = $this->automatic_ttl(); $this->settings['update/timed'] = 'automatically'; } $this->settings['update/ttl'] = apply_filters('syndicated_feed_ttl', $this->settings['update/ttl'], $this); if (!isset($this->settings['update/hold']) or $this->settings['update/hold'] != 'ping') { $this->settings['update/hold'] = 'scheduled'; } $this->settings['update/unfinished'] = 'yes'; $link['link_notes'] = $this->settings_to_notes(); // Update the properties of the link from the feed information $result = wp_update_link($link); do_action('update_syndicated_feed', $this->id, $this); # -- Add new posts from feed and update any updated posts $crashed = false; $posts = apply_filters('syndicated_feed_items', $this->magpie->originals, $this); if (is_array($posts)) { foreach ($posts as $key => $original) { $item = $this->magpie->items[$key]; $post = new SyndicatedPost(array('simplepie' => $original, 'magpie' => $item), $this); if (!$resume or !in_array(trim($post->guid()), $processed)) { $processed[] = $post->guid(); if (!$post->filtered()) { $new = $post->store(); if ($new !== false) { $new_count[$new]++; } } if (!is_null($crash_ts) and time() > $crash_ts) { $crashed = true; break; } } unset($post); } } $suffix = $crashed ? 'crashed' : 'completed'; do_action('update_syndicated_feed_items', $this->id, $this); do_action("update_syndicated_feed_items_{$suffix}", $this->id, $this); // Copy back any changes to feed settings made in the course of updating (e.g. new author rules) $to_notes = $this->settings; $this->settings['update/processed'] = $processed; if (!$crashed) { $this->settings['update/unfinished'] = 'no'; } $link['link_notes'] = $this->settings_to_notes(); // Update the properties of the link from the feed information $result = wp_update_link($link); do_action("update_syndicated_feed_completed", $this->id, $this); } } return $new_count; }
function __construct ($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false) { global $feedwordpress; global $wp_version; $source = NULL; if ($feedwordpress->subscribed($url)) : $source = $feedwordpress->subscription($url); endif; $this->url = $url; $this->timeout = $timeout; $this->redirects = $redirects; $this->headers = $headers; $this->useragent = $useragent; $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE; global $wpdb; global $fwp_credentials; if ( preg_match('/^http(s)?:\/\//i', $url) ) { $args = array( 'timeout' => $this->timeout, 'redirection' => $this->redirects); if ( !empty($this->headers) ) $args['headers'] = $this->headers; // Use default FWP user agent unless custom has been specified if ( SIMPLEPIE_USERAGENT != $this->useragent ) : $args['user-agent'] = $this->useragent; else : $args['user-agent'] = apply_filters('feedwordpress_user_agent', 'FeedWordPress/'.FEEDWORDPRESS_VERSION .' (aggregator:feedwordpress; WordPress/'.$wp_version .' + '.SIMPLEPIE_NAME.'/'.SIMPLEPIE_VERSION .'; Allow like Gecko; +http://feedwordpress.radgeek.com/) at ' . feedwordpress_display_url(get_bloginfo('url')), $this ); endif; // This is ugly as hell, but communicating up and down the chain // in any other way is difficult. if (!is_null($fwp_credentials)) : $args['authentication'] = $fwp_credentials['authentication']; $args['username'] = $fwp_credentials['username']; $args['password'] = $fwp_credentials['password']; elseif ($source InstanceOf SyndicatedLink) : $args['authentication'] = $source->authentication_method(); $args['username'] = $source->username(); $args['password'] = $source->password(); endif; FeedWordPress::diagnostic('updated_feeds:http', "HTTP [$url] ⇜ ".esc_html(FeedWordPress::val($args))); $res = wp_remote_request($url, $args); FeedWordPress::diagnostic('updated_feeds:http', "HTTP [$url] ⇝ ".esc_html(FeedWordPress::val($res))); if ( is_wp_error($res) ) { $this->error = 'WP HTTP Error: ' . $res->get_error_message(); $this->success = false; } else { $this->headers = wp_remote_retrieve_headers( $res ); $this->body = wp_remote_retrieve_body( $res ); $this->status_code = wp_remote_retrieve_response_code( $res ); } if ($source InstanceOf SyndicatedLink) : $source->update_setting('link/filesize', strlen($this->body)); $source->update_setting('link/http status', $this->status_code); $source->save_settings(/*reload=*/ true); endif; } else { if ( ! $this->body = file_get_contents($url) ) { $this->error = 'file_get_contents could not read the file'; $this->success = false; } } // SimplePie makes a strongly typed check against integers with // this, but WordPress puts a string in. Which causes caching // to break and fall on its ass when SimplePie is getting a 304, // but doesn't realize it because this member is "304" instead. $this->status_code = (int) $this->status_code; }
public function insert($tax = NULL) { $ret = NULL; if (is_null($tax)) { if (count($this->tax) > 0) { $tax = $this->tax[0]; } } if (!$this->is_forbidden_in($tax)) { $aTerm = wp_insert_term($this->term, $tax); if (is_wp_error($aTerm)) { // If debug mode is ON, this will halt us here. FeedWordPress::noncritical_bug('term insertion problem', array('term' => $this->term, 'result' => $aTerm, 'post' => $post, 'this' => $this), __LINE__, __FILE__); // Otherwise, we'll continue & return NULL... } else { $this->exists = $aTerm; $this->exists_in = $tax; $ret = $this->id(); } FeedWordPress::diagnostic('syndicated_posts:categories', 'CREATED unfamiliar ' . $tax . ': ' . json_encode($this->term) . ' with result: ' . json_encode($aTerm)); } else { FeedWordPress::diagnostic('syndicated_posts:categories', 'Category: DID NOT CREATE unfamiliar ' . $tax . ': ' . json_encode($this->term) . ':' . ' that ' . $tax . ' name is filtered out.'); } return $ret; }
/** * SyndicatedPost::add_rss_meta: adds interesting meta-data to each entry * using the space for custom keys. The set of keys and values to add is * specified by the keys and values of $post['meta']. This is used to * store anything that the WordPress user might want to access from a * template concerning the post's original source that isn't provided * for by standard WP meta-data (i.e., any interesting data about the * syndicated post other than author, title, timestamp, categories, and * guid). It's also used to hook into WordPress's support for * enclosures. * * @param string $new_status Unused action parameter. * @param string $old_status Unused action parameter. * @param object $post The database record for the post just inserted. */ function add_rss_meta($new_status, $old_status, $post) { global $wpdb; if ($new_status != 'inherit') { // Bail if we are creating a revision. FeedWordPress::diagnostic('syndicated_posts:meta_data', 'Adding post meta-data: {' . implode(", ", array_keys($this->post['meta'])) . '}'); if (is_array($this->post) and isset($this->post['meta']) and is_array($this->post['meta'])) { $postId = $post->ID; // Aggregated posts should NOT send out pingbacks. // WordPress 2.1-2.2 claim you can tell them not to // using $post_pingback, but they don't listen, so we // make sure here. $result = $wpdb->query("\n\t\t\t\tDELETE FROM {$wpdb->postmeta}\n\t\t\t\tWHERE post_id='{$postId}' AND meta_key='_pingme'\n\t\t\t\t"); foreach ($this->post['meta'] as $key => $values) { $eKey = esc_sql($key); // If this is an update, clear out the old // values to avoid duplication. $result = $wpdb->query("\n\t\t\t\t\tDELETE FROM {$wpdb->postmeta}\n\t\t\t\t\tWHERE post_id='{$postId}' AND meta_key='{$eKey}'\n\t\t\t\t\t"); // Allow for either a single value or an array if (!is_array($values)) { $values = array($values); } foreach ($values as $value) { FeedWordPress::diagnostic('syndicated_posts:meta_data', "Adding post meta-datum to post [{$postId}]: [{$key}] = " . MyPHP::val($value, true)); add_post_meta($postId, $key, $value, false); } } } } }
function processRule($word, $actions, $mp) { if ($word == '-') { if ($mp->matches > 0) { $word = '/$^/'; // never matched } else { $word = '/.*/'; // always matches } } $comparisonFields = 'text'; $patterns = NULL; if (preg_match("^(category|text)::(.*)\$", $word, $ref)) { $comparisonFields = $ref[1]; switch ($comparisonFields) { case 'category': // Exact & complete matches only, but ignore case and space $patterns = array(array("pattern" => '^\\s*' . preg_replace("/\\s+/", "\\s+", preg_quote(strtolower(trim($ref[2])))) . '\\s*$', "mods" => "i")); break; case 'text': $word = $ref[2]; default: // Pass the buck down to the next if-then structure } } if (is_null($patterns)) { if (preg_match("^\\s*/(.*)/\\s*([a-z]*)\\s*\$i", $word, $ref)) { // PCRE literal expression $patterns = array(array("pattern" => $ref[1], "mods" => $ref[2])); } else { // List of keywords to search for. $words = preg_split('/\\s+/', $word, -1, PREG_SPLIT_NO_EMPTY); $patterns = array(); foreach ($words as $w) { if (preg_match("^\\s*/(.*)/\\s*([a-z]*)\\s*\$i", $w, $ref)) { $patterns[] = array("pattern" => $ref[1], "mods" => $ref[2]); } else { $patterns[] = array("pattern" => '\\b' . preg_quote($w) . '\\b', "mods" => 'i'); } } } } $diagRegexes = array(); foreach ($patterns as $regex) { $diagRegexes[] = '/' . $regex['pattern'] . '/' . $regex['mods']; } $iGuid = esc_html($mp->post->guid()); $iWord = esc_html($word); $iRegexes = esc_html(implode(' + ', $diagRegexes)); $iMethod = esc_html($comparisonFields); FeedWordPress::diagnostic('keyword_filters:scan', "Scanning item [{$iGuid}] for {$iMethod} keyword {$iWord} (PCRE: {$iRegexes})"); $matched = $mp->match($patterns, $comparisonFields); if ($matched) { $mp->matches = $mp->matches + 1; FeedWordPress::diagnostic('keyword_filters:match', 'Matched item [' . esc_html($mp->post->guid()) . '] against keyword "' . esc_html($word) . '". Applying: ' . esc_html(implode(",", $actions))); foreach ($actions as $action) { $bits = explode(":", $action, 2); $verb = $bits[0]; if (isset($bits[1])) { $param = $bits[1]; } switch ($verb) { case 'filter': if (is_null($this->filtered)) { $this->filtered = true; } break; case 'include': $this->filtered = false; break; case 'category': case 'post_tag': $terms = array_map('trim', explode(",", $param)); $this->terms[$verb] = array_merge($this->terms[$verb], $terms); } } } }