Commit 0873fb82 authored by lechuck's avatar lechuck Committed by lucha

[auto] Plugin: feedwordpress 2016.0420

parent 22ede96b
......@@ -356,23 +356,36 @@ function clone_http_test_args_keyvalue_prototype () {
<td><a href="#http-test-args" onclick="return clone_http_test_args_keyvalue_prototype();">+ Add</a></td>
</tr>
</table>
</td>
</tr>
<tr>
<th>XPath:</th>
<td><div><input type="text" name="http_test_xpath" value="" placeholder="xpath-like query" /></div>
<div><p>Leave blank to test HTTP, fill in to test a query.</p></div>
</td>
</tr>
<?php if (isset($page->test_html['http_test'])) : ?>
<tr>
<th scope="row">RESULTS:</th>
<td>
<div>URL: <code><?php print esc_html($page->test_html['url']); ?></code></div>
<div style="position: relative">
<div style="width: 100%; overflow: scroll; background-color: #eed">
<pre style="white-space: pre-wrap; white-space: -moz-pre-wrap; white-space: -o-pre-wrap;"><?php print $page->test_html['http_test']; ?></pre>
</div>
</div>
<?php endif; ?>
</td>
</tr>
<?php endif; ?>
</table>
<?php
} /* FeedWordPressDiagnosticsPage::tests_box () */
var $test_html;
static function do_http_test ($post) {
private $test_html;
public function do_http_test ($post) {
if (isset($post['http_test_url']) and isset($post['http_test_method'])) :
$url = $post['http_test_url'];
......@@ -410,6 +423,7 @@ function clone_http_test_args_keyvalue_prototype () {
break;
endswitch;
$this->test_html['url'] = $url;
$this->test_html['http_test'] = esc_html(MyPHP::val($out));
endif;
} /* FeedWordPressDiagnosticsPage::do_http_test () */
......
......@@ -629,7 +629,7 @@ function fwp_xpathtest_ok (response, result_id, destination) {
if (response.results instanceof Array) {
for (var i = 0; i < response.results.length; i++) {
resultsHtml += '<li>result['+i.toString()+'] = <code>'+response.results[i]+'</code></li>';
resultsHtml += '<li>result['+(i+1).toString()+'] = <code>'+response.results[i]+'</code></li>';
}
} else {
resultsHtml += '<li>result = <code>' + response.results + '</code></li>';
......
......@@ -3,7 +3,7 @@
Plugin Name: FeedWordPress
Plugin URI: http://feedwordpress.radgeek.com/
Description: simple and flexible Atom/RSS syndication for WordPress
Version: 2015.0514
Version: 2016.0420
Author: Charles Johnson
Author URI: http://radgeek.com/
License: GPL
......@@ -11,7 +11,7 @@ License: GPL
/**
* @package FeedWordPress
* @version 2015.0514
* @version 2016.0420
*/
# This uses code derived from:
......@@ -32,7 +32,7 @@ License: GPL
# -- Don't change these unless you know what you're doing...
define ('FEEDWORDPRESS_VERSION', '2015.0514');
define ('FEEDWORDPRESS_VERSION', '2016.0420');
define ('FEEDWORDPRESS_AUTHOR_CONTACT', 'http://radgeek.com/contact');
if (!defined('FEEDWORDPRESS_BLEG')) :
......
......@@ -3,8 +3,8 @@ Contributors: Charles Johnson
Donate link: http://feedwordpress.radgeek.com/
Tags: syndication, aggregation, feed, atom, rss
Requires at least: 3.0
Tested up to: 4.2.2
Stable tag: 2015.0514
Tested up to: 4.5.2
Stable tag: 2016.0420
FeedWordPress syndicates content from feeds you choose into your WordPress weblog.
......@@ -20,7 +20,7 @@ appears as a series of special posts in your WordPress posts database. If you
syndicate several feeds then you can use WordPress's posts database and
templating engine as the back-end of an aggregation ("planet") website. It was
developed, originally, because I needed a more flexible replacement for
[Planet][] to use at [Feminist Blogs][].
[Planet][] to use at Feminist Blogs, an aggregator site that I used to administer.
[Planet]: http://www.planetplanet.org/
[Feminist Blogs]: http://feministblogs.org/
......@@ -94,6 +94,33 @@ outs, see the documentation at the [FeedWordPress project homepage][].
== Changelog ==
= 2016.0420 =
* WORDPRESS COMPATIBILITY: Tested with new versions of WordPress up to 4.5.
* FILTERS AND ADD-ONS: Allow filters and add-ons to filter terms and taxonomy
(categories, tags, custom taxonomies, etc.) more thoroughly and more
fine-grainedly using syndicated_post_terms_match, syndicated_post_terms_match_{taxonomy},
syndicated_post_terms_unfamiliar, syndicated_post_terms_mapping,
syndicated_item_feed_terms, and syndicated_item_preset_terms filters.
* FILTERS AND ADD-ONS: Globals $fwp_channel and $fwp_feedmeta REMOVED.
These global variables, originally introduced to allow filters access to
information about the source feed in `syndicated_item` filters were
deprecated 6+ years ago. If you have any filters or add-ons which still
depend on these global variables, you've been using obsolete techniques
and you should see about fixing them to access data about the source feed
using the SyndicatedPost::link element instead. For documentation, see
the FeedWordPress documentation wiki at
<http://feedwordpress.radgeek.com/wiki/syndicatedpost> and
<http://feedwordpress.radgeek.com/wiki/syndicatedlink>.
* BUGFIX: Syndication > Diagnostics HTTP diagnostic test widget was broken due to
a dumb error on my part. Now fixed.
* SMALL CODING CHANGES: Lots of small changes to code organization, incorporation
of some PHP 5.x coding conventions, etc.
= 2015.0514 =
* IMPORTANT SECURITY UPDATE: This version includes two important fixes for
......
......@@ -59,6 +59,9 @@ class SyndicatedPostTerm {
protected function search () {
// Initialize
$found = null;
// Either this is a numbered term code, which supplies the ID
// and the taxonomy explicitly (e.g.: {category#2}; in which
// case we have set $this->tax to a unit array containing only
......@@ -90,7 +93,7 @@ class SyndicatedPostTerm {
'CHECKED familiarity of term '
.json_encode($this->term)
.' across '.json_encode($this->tax)
. ' with result: '.json_encode($record)
. ' with result: '.json_encode($found)
);
return $this->exists;
......
<?php
/**
* class SyndicatedPostXPathQuery: implements an XPath-like syntax used to query
* arbitrary elements within the syndicated item.
*
*/
class SyndicatedPostXPathQuery {
private $path;
private $parsedPath;
private $feed_type;
private $xmlns;
private $urlHash = array();
/**
* SyndicatedPostXPathQuery::__construct
*
* @param array $args
* @uses wp_parse_args
*
*/
public function __construct ($args = array()) {
if (is_string($args)) :
$args = array("path" => $args);
endif;
$args = wp_parse_args($args, array(
"path" => "",
));
$this->setPath($args['path']);
} /* SyndicatedPostXPathQuery::__construct() */
/**
* SyndicatedPostXPathQuery::getPath
*
* @param array $args
* @return mixed
*/
public function getPath ($args = array()) {
$args = wp_parse_args($args, array(
"parsed" => false,
));
return ($args['parsed'] ? $this->parsedPath : $this->path);
} /* SyndicatedPostXPathQuery::getPath () */
/**
* SyndicatedPostXPathQuery::setPath
*
* @param string $path
*/
public function setPath ($path) {
$this->urlHash = array();
$this->path = $path;
// Allow {url} notation for namespaces. URLs will contain : and /, so...
preg_match_all('/{([^}]+)}/', $path, $match, PREG_SET_ORDER);
foreach ($match as $ref) :
$this->urlHash[md5($ref[1])] = $ref[1];
endforeach;
foreach ($this->urlHash as $hash => $url) :
$path = str_replace('{'.$url.'}', '{#'.$hash.'}', $path);
endforeach;
$path = $this->parsePath(/*cur=*/ $path, /*orig=*/ $path);
$this->parsedPath = $path;
} /* SyndicatedPostXPathQuery::setPath() */
/**
* SyndicatedPostXPathQuery::snipSlug
*
* @return string
*/
protected function snipSlug ($path, $start, $n) {
$slug = substr($path, $start, ($n-$start));
if (strlen($slug) > 0) :
if (preg_match('/{#([^}]+)}/', $slug, $ref)) :
if (isset($this->urlHash[$ref[1]])) :
$slug = str_replace(
'{#'.$ref[1].'}',
'{'.$this->urlHash[$ref[1]].'}',
$slug
);
endif;
endif;
endif;
return $slug;
} /* SyndicatedPostXPathQuery::snipSlug () */
/**
* SyndicatedPostXPathQuery::parsePath ()
*
* @param mixed $path
* @param mixed $rootPath
* @return array|object
*/
public function parsePath ($path, $rootPath) {
if (is_array($path)) :
// This looks like it's already been parsed.
$pp = $path;
else :
$pp = array();
// Okay let's parse this thing.
$n = 0; $start = 0; $state = 'slug';
while ($state != '$') :
switch ($state) :
case 'slash' :
$slug = $this->snipSlug($path, $start, $n);
if (strlen($slug) > 0) :
$pp[] = $slug;
endif;
$n++;
// don't include the slash in our next slug
$start = $n;
$state = (($n < strlen($path)) ? 'slug' : '$');
break;
case 'brackets' :
// first, snip off what we've consumed so far
$slug = $this->snipSlug($path, $start, $n);
if (strlen($slug) > 0) :
$pp[] = $slug;
endif;
// now, chase the ]
$depth = 1;
$n++; $start = $n;
// find the end of the [square-bracketed] expression
while ($depth > 0 and $n != '') :
$tok = ((strlen($path) > $n) ? $path[$n] : '');
switch ($tok) :
case '' :
// ERROR STATE: syntax error
$depth = -1;
$state = 'syntax-error';
break;
case '[' :
$depth++;
break;
case ']' :
$depth--;
break;
default :
// NOOP
endswitch;
$n++;
endwhile;
if ($state != 'syntax-error') :
$bracketed = substr($path, $start, ($n-$start)-1);
// recursive parsing
$oFilter = new stdClass;
$oFilter->verb = 'has';
$oFilter->query = $this->parsePath($bracketed, $rootPath);
$pp[] = $oFilter;
$start = $n;
$state = 'slash-expected';
endif;
break;
case 'slash-expected' :
$tok = ((strlen($path) > $n) ? $path[$n] : '');
if ($tok == '/' or $tok == '') :
$state = 'slash';
else :
$state = 'syntax-error';
endif;
break;
case 'syntax-error' :
$pp = new WP_Error('xpath', __("Syntax error", "feedwordpress"));
$state = '$';
break;
case 'slug' :
default :
$tok = ((strlen($path) > $n) ? $path[$n] : '');
switch ($tok) :
case '' :
case '/' :
$state = 'slash';
break;
case '[' :
$state = 'brackets';
break;
default :
$n++;
endswitch;
endswitch;
endwhile;
endif;
return $pp;
} /* SyndicatedPostXPathQuery::parsePath() */
/**
* SyndicatedPostXPathQuery::match
*
* @param string $path
* @return array
*/
public function match ($r = array()) {
$path = $this->parsedPath;
$r = wp_parse_args($r, array(
"type" => SIMPLEPIE_TYPE_ATOM_10,
"xmlns" => array(),
"map" => array(),
"context" => array(),
"parent" => array(),
"format" => "string",
));
$this->feed_type = $r['type'];
$this->xmlns = $r['xmlns'];
// Start out with a get_item_tags query.
$node = '';
while (strlen($node)==0 and !is_null($node)) :
$node = array_shift($path);
endwhile;
if (is_string($node) and isset($r['map'][$node])) :
$data = $r['map'][$node];
$node = array_shift($path);
else :
$data = $r['map']['/'];
endif;
$matches = $data;
while (!is_null($node)) :
if (is_object($node) OR strlen($node) > 0) :
list($axis, $element) = $this->xpath_name_and_axis($node);
if ('self'==$axis) :
if (is_object($element) and property_exists($element, 'verb')) :
$subq = new self(array("path" => $element->query));
$result = $subq->match(array(
"type" => $r['type'],
"xmlns" => $r['xmlns'],
"map" => array(
"/" => $matches,
),
"context" => $matches,
"parent" => $r['parent'],
"format" => "object",
));
// when format = 'object' we should get back
// a sparse array of arrays, with indices = indices
// from the input array, each element = an array of
// one or more matching elements
if ($element->verb = 'has' and is_array($result)) :
$results = array();
foreach (array_keys($result) as $a) :
$results[$a] = $matches[$a];
endforeach;
$matches = $results;
$data = $matches;
endif;
elseif (is_numeric($node)) :
// according to W3C, sequence starts at position 1, not 0
// so subtract 1 to line up with PHP array starting at 0
$idx = intval($element) - 1;
if (isset($matches[$idx])) :
$data = array($idx => $matches[$idx]);
else :
$data = array();
endif;
$matches = array($idx => $data);
endif;
else :
$matches = array();
foreach ($data as $idx => $datum) :
if (!is_string($datum) and isset($datum[$axis])) :
foreach ($datum[$axis] as $ns => $elements) :
if (isset($elements[$element])) :
// Potential match.
// Check namespace.
if (is_string($elements[$element])) : // Attribute
$addenda = array($elements[$element]);
$contexts = array($datum);
// Element
else :
$addenda = $elements[$element];
$contexts = $elements[$element];
endif;
foreach ($addenda as $index => $addendum) :
$context = $contexts[$index];
$namespaces = $this->xpath_possible_namespaces($node, $context);
if (in_array($ns, $namespaces)) :
$matches[] = $addendum;
endif;
endforeach;
endif;
endforeach;
endif;
endforeach;
$data = $matches;
endif;
endif;
$node = array_shift($path);
endwhile;
$matches = array();
foreach ($data as $idx => $datum) :
if ($r['format'] == 'string') :
if (is_string($datum)) :
$matches[] = $datum;
elseif (isset($datum['data'])) :
$matches[] = $datum['data'];
endif;
else :
$matches[$idx] = $datum;
endif;
endforeach;
return $matches;
} /* SyndicatedPostXPathQuery::match() */
public function xpath_default_namespace () {
// Get the default namespace.
$type = $this->feed_type;
if ($type & SIMPLEPIE_TYPE_ATOM_10) :
$defaultNS = SIMPLEPIE_NAMESPACE_ATOM_10;
elseif ($type & SIMPLEPIE_TYPE_ATOM_03) :
$defaultNS = SIMPLEPIE_NAMESPACE_ATOM_03;
elseif ($type & SIMPLEPIE_TYPE_RSS_090) :
$defaultNS = SIMPLEPIE_NAMESPACE_RSS_090;
elseif ($type & SIMPLEPIE_TYPE_RSS_10) :
$defaultNS = SIMPLEPIE_NAMESPACE_RSS_10;
elseif ($type & SIMPLEPIE_TYPE_RSS_20) :
$defaultNS = SIMPLEPIE_NAMESPACE_RSS_20;
else :
$defaultNS = SIMPLEPIE_NAMESPACE_RSS_20;
endif;
return $defaultNS;
} /* SyndicatedPostXPathQuery::xpath_default_namespace() */
public function xpath_name_and_axis ($node) {
$ns = NULL; $element = NULL;
$axis = 'child'; // "In effect, `child` is the default axis."
if (is_object($node) and property_exists($node, 'verb')):
if ('has'==$node->verb) :
$axis = 'self';
endif;
elseif (strpos($node, '::') !== false) :
list($axis, $node) = explode("::", $node, 2);
if ($axis=='attribute') :
$axis = 'attribs'; // map from W3C to SimplePie's idiosyncratic notation
endif;
elseif (substr($node, 0, 1)=='@') :
$axis = 'attribs'; $node = substr($node, 1);
elseif (is_numeric($node)) :
$axis = 'self';
elseif (substr($node, 0, 1)=='/') :
// FIXME: properly, we should check for // and if we have it,
// treat that as short for /descendent-or-self::node()/
$axis = 'child'; $node = substr($node, 1);
else :
// NOOP
endif;
if (is_string($node) and preg_match('/^{([^}]*)}(.*)$/', $node, $ref)) :
$element = $ref[2];
elseif (is_string($node) and strpos($node, ':') !== FALSE) :
list($xmlns, $element) = explode(':', $node, 2);
else :
$element = $node;
endif;
return array($axis, $element);
} /* SyndicatedPostXPathQuery::xpath_name_and_axis () */
public function xpath_possible_namespaces ($node, $datum = array()) {
$ns = NULL; $element = NULL;
if (substr($node, 0, 1)=='@') :
$attr = '@'; $node = substr($node, 1);
else :
$attr = '';
endif;
if (preg_match('/^{([^}]*)}(.*)$/', $node, $ref)) :
$ns = array($ref[1]);
elseif (strpos($node, ':') !== FALSE) :
list($xmlns, $element) = explode(':', $node, 2);
if (isset($this->xmlns['reverse'][$xmlns])) :
$ns = $this->xmlns['reverse'][$xmlns];
else :
$ns = array($xmlns);
endif;
// Fucking SimplePie. For attributes in default xmlns.
$defaultNS = $this->xpath_default_namespace();
if (isset($this->xmlns['forward'][$defaultNS])
and ($xmlns==$this->xmlns['forward'][$defaultNS])) :
$ns[] = '';
endif;
if (isset($datum['xmlns'])) :
if (isset($datum['xmlns'][$xmlns])) :
$ns[] = $datum['xmlns'][$xmlns];
endif;
endif;
else :
// Often in SimplePie, the default namespace gets stored
// as an empty string rather than a URL.
$ns = array($this->xpath_default_namespace(), '');
endif;
return array_unique($ns);
} /* SyndicatedPostXPathQuery::xpath_possible_namespaces() */