Commit f369008b authored by Malcolm Blaney's avatar Malcolm Blaney

Reader module now looks for multiple h-feeds on a page when doing

feed discovery. SimplePie Parser can now return the correct h-feed
when a fragment identifier is provided in the url. Fixed a problem
with the session token not being set on feed pages when NotModified
returned true in page.php.
parent 68d40c09
Pipeline #37193609 passed with stage
in 1 minute and 18 seconds
......@@ -584,6 +584,7 @@ class Reader extends Base {
// get_all_discoverd_feeds doesn't work if caching is used.
$feed->set_cache_duration(0);
$feed->set_autodiscovery_cache_duration(0);
$error = 'Feed not found.';
if (!$feed->init() || $error = $feed->error()) {
$us_xml_url = trim($us_xml_url, ' /');
......@@ -591,42 +592,66 @@ class Reader extends Base {
return ['error' => $error];
}
// get_all_discovered_feeds returns an array of SimplePie_File objects.
$all_feeds = $feed->get_all_discovered_feeds();
// The auto parameter is set to true for indieauth users so that they are
// automatically subscribed to their own feeds, without showing options.
if (count($all_feeds) <= 1 || $auto) {
// SimplePie will update the feed_url when the url given was
// previously used for autodiscovery, or if there were redirects.
$us_xml_url = $feed->subscribe_url();
}
else {
$discovered = '<p>Multiple feeds were found on this page.<br>' .
'Please select the ones you would like to subscribe to:</p>';
for ($i = 0; $i < count($all_feeds); $i++) {
// Select the first feed by default.
$checked = $i === 0 ? ' checked="checked"' : '';
$discovered_id = 'reader-discovered-feed-' . $i;
$discovered_url = $all_feeds[$i]->url;
$short_url = strlen($discovered_url) <= 50 ?
$discovered_url : substr($discovered_url, 0, 47) . '...';
$discovered_link = '<a href="' . $discovered_url . '">' . $short_url .
'</a>';
$discovered_title = $this->FeedTitle($all_feeds[$i]->body);
$display = $discovered_title === '' ? $discovered_link :
$discovered_title . ' <span class="reader-discovered-feed-url">[' .
$discovered_link . ']</span>';
$discovered .= '<div class="form-spacing">' .
'<input type="checkbox" id="' . $discovered_id . '"' .
'value="' . $discovered_url . '"' . $checked . '>' .
'<label for="' . $discovered_id . '">' . $display . '</label>' .
'</div>';
}
$discovered .= '<button class="reader-discovered-cancel">cancel</button>'.
'<button class="reader-discovered-add">add</button>';
return ['discovered' => $discovered];
// Also don't do feed discovery when a fragment identifier has been added
// to the url as this represents a specific feed on the page.
if (!$auto && strpos($us_xml_url, '#') === false) {
// If microformats were found on the page then there could be more than
// one h-feed, so show all of them during discovery.
$feed_author = '';
$h_feed_list = [];
$mf = Mf2\parse($feed->get_raw_data(), $us_xml_url);
foreach ($mf['items'] as $mf_item) {
if (!isset($mf_item['type'])) continue;
if (in_array('h-feed', $mf_item['type'])) {
if (isset($mf_item['properties']['name'][0])) {
$h_feed_list[] = $mf_item['properties']['name'][0];
}
else if (isset($mf_item['properties']['uid'][0])) {
$h_feed_list[] = $mf_item['properties']['uid'][0];
}
else if (!in_array('', $h_feed_list)) {
// Assume an h-feed without a name is the main feed on the page,
// and push it onto the front of the list.
array_unshift($h_feed_list, '');
}
}
// Also look for h-feed on children.
if (!isset($mf_item['children'][0]['type'])) continue;
foreach ($mf_item['children'] as $child) {
if (in_array('h-feed', $child['type'])) {
// The parent of this h-feed may be an h-card, so use the name as
// the feed author if a name isn't given for the h-feed.
if (in_array('h-card', $mf_item['type'])) {
$feed_author = $mf_item['properties']['name'][0];
}
if (isset($child['properties']['name'][0])) {
$h_feed_list[] = $child['properties']['name'][0];
}
else if (isset($child['properties']['uid'][0])) {
$h_feed_list[] = $child['properties']['uid'][0];
}
else if (!in_array('', $h_feed_list)) {
array_unshift($h_feed_list, '');
}
}
}
}
// get_all_discovered_feeds() returns an array of SimplePie_File objects.
$all_feeds = $feed->get_all_discovered_feeds();
if (count($all_feeds) > 1 || count($h_feed_list) > 1) {
return $this->DiscoveredFeeds($us_xml_url, $feed_author, $h_feed_list,
$all_feeds);
}
}
// SimplePie will update the feed_url when the url given was
// previously used for autodiscovery, or if there were redirects.
$us_xml_url = $feed->subscribe_url();
$mysqli = connect_db();
$owner = $this->SwitchOwner();
$us_xml_url = trim($us_xml_url, ' /');
......@@ -849,6 +874,71 @@ class Reader extends Base {
$mysqli->close();
}
private function DiscoveredFeeds($xml_url, $author,
$h_feed_list, $all_feeds) {
$discovered = '<p>Multiple feeds were found on this page.<br>' .
'Please select the ones you would like to subscribe to:</p>';
$i = 0;
$j = 0;
for ($i; $i < count($h_feed_list); $i++) {
// Select the first feed by default, can also skip the first entry in
// $all_feeds because that is the page this h-feed was found on.
$checked = '';
if ($i === 0) {
$j = 1;
$checked = ' checked="checked"';
}
// The id is just to link the label to the checkbox.
$discovered_id = 'reader-discovered-feed-' . $i;
$name = $h_feed_list[$i];
$short_url = $xml_url;
if ($name !== '') $short_url .= '#' . $name;
$short_url = strlen($short_url) <= 50 ? $short_url :
substr($short_url, 0, 47) . '...';
$discovered_url = $xml_url . '#' . urlencode($name);
$discovered_link = '<a href="' . $discovered_url . '">' . $short_url .
'</a>';
// Use the author's name if provided and the current feed doesn't have
// an explicit name. Otherwise give it a generic label.
if ($name === '') {
$discovered_title = $author === '' ? 'Main Feed' : $author;
}
else {
$discovered_title = $name;
}
$display = $discovered_title === '' ? $discovered_link :
$discovered_title . ' <span class="reader-discovered-feed-url">[' .
$discovered_link . ']</span>';
$discovered .= '<div class="form-spacing">' .
'<input type="checkbox" id="' . $discovered_id. '" ' .
'value="' . $discovered_url . '"' . $checked . '>' .
'<label for="' . $discovered_id . '">' . $display . '</label>' .
'</div>';
}
for ($j; $j < count($all_feeds); $j++) {
// Select the first feed by default (if not checked by an h-feed).
$checked = $j === 0 ? ' checked="checked"' : '';
$discovered_id = 'reader-discovered-feed-' . ($i + $j);
$discovered_url = $all_feeds[$j]->url;
$short_url = strlen($discovered_url) <= 50 ?
$discovered_url : substr($discovered_url, 0, 47) . '...';
$discovered_link = '<a href="' . $discovered_url . '">' . $short_url .
'</a>';
$discovered_title = $this->FeedTitle($all_feeds[$j]->body);
$display = $discovered_title === '' ? $discovered_link :
$discovered_title . ' <span class="reader-discovered-feed-url">[' .
$discovered_link . ']</span>';
$discovered .= '<div class="form-spacing">' .
'<input type="checkbox" id="' . $discovered_id . '" ' .
'value="' . $discovered_url . '"' . $checked . '>' .
'<label for="' . $discovered_id . '">' . $display . '</label>' .
'</div>';
}
$discovered .= '<button class="reader-discovered-cancel">cancel' .
'</button><button class="reader-discovered-add">add</button>';
return ['discovered' => $discovered];
}
private function FeedList($id = NULL) {
$us_list = [];
$mysqli = connect_db();
......@@ -901,9 +991,9 @@ class Reader extends Base {
while ($reader = $result->fetch_assoc()) {
$url = $reader['xml_url'];
$short_url = strlen($url) <= 50 ? $url : substr($url, 0, 47) . '...';
$title = htmlspecialchars($reader['feed_title']);
$title = $reader['feed_title'];
$added = $us_xml_url === $url ? ' added' : '';
$link = '<a href="' . $url . '">' . $short_url . '</a>';
$link = '<a href="' . $url . '">' . urldecode($short_url) . '</a>';
$content .= '<div class="feed-item">' .
'<button class="remove-feed-item">remove</button> ' .
'<span class="feed-text' . $added . '">';
......@@ -1964,9 +2054,18 @@ class Reader extends Base {
$us_title = '';
}
else if (strlen($us_title) > 140) {
// This assumes that long titles are probably the same as the content,
// but the comparison failed. In this case show a shortened version.
$us_title = substr($us_title, 0, 100) . '...';
// For long titles check if there's any content, if not assume p-name
// was just used instead of e-content and use the title as content.
if ($us_content === '') {
$us_content = $us_title;
$us_title = '';
}
else {
// Otherwise assume that long titles are probably the same as the
// content, but the comparison failed. In this case show a shortened
// version of the title.
$us_title = substr($us_title, 0, 100) . '...';
}
}
}
......
......@@ -20,6 +20,9 @@ session_start();
function session_expired() {
// All javascript xhr should be able to log errors.
if (!isset($_SESSION['token'])) {
// May have an expired session because page.php does 'not modified' checks
// on feed pages. When this happens reload is used to skip the check.
$_SESSION['reload'] = true;
echo json_encode(['error' => 'Session expired: reloading page.']);
return true;
}
......
......@@ -475,12 +475,40 @@ class SimplePie_Parser
$author_cache = array();
$items = array();
$entries = array();
$mf = Mf2\parse($data, $url);
$h_feed = array();
$feed_id = NULL;
if (preg_match('/#([^#]*)$/', $url, $match)) {
$feed_id = urldecode($match[1]);
}
$mf = Mf2\parse($data, $url);
foreach ($mf['items'] as $mf_item) {
if (!isset($mf_item['type'])) continue;
if (in_array('h-feed', $mf_item['type'])) {
if (isset($feed_id)) {
if (isset($mf_item['properties']['name'][0])) {
if ($feed_id === $mf_item['properties']['name'][0]) {
$h_feed = $mf_item;
break;
}
}
else if (isset($mf_item['properties']['uid'][0])) {
if ($feed_id === $mf_item['properties']['uid'][0]) {
$h_feed = $mf_item;
break;
}
}
else if ($feed_id === '') {
// This last case relies on both name and uid properties not being
// set above and feed_id being empty means it's a match.
$h_feed = $mf_item;
break;
}
// If none of the three cases above match then need to continue,
// because feed_id is set so no other cases are allowed to match.
continue;
}
$h_feed = $mf_item;
break;
}
......@@ -509,25 +537,47 @@ class SimplePie_Parser
$entries = $mf['items'];
break;
}
if (!isset($mf_item['children'][0]['type'])) continue;
// If not one of the simpler formats above, look for h-feed, h-entry or
// h-cite in the children of each top level item.
if (isset($mf_item['children'][0]['type'])) {
if (in_array('h-feed', $mf_item['children'][0]['type'])) {
$h_feed = $mf_item['children'][0];
foreach ($mf_item['children'] as $child) {
if (in_array('h-feed', $child['type'])) {
// The parent of this h-feed may be an h-card, so use it as the
// feed author.
if (in_array('h-card', $mf_item['type'])) $feed_author = $mf_item;
break;
if (isset($feed_id)) {
if (isset($child['properties']['name'][0])) {
if ($feed_id === $child['properties']['name'][0]) {
$h_feed = $child;
break 2;
}
}
else if (isset($child['properties']['uid'][0])) {
if ($feed_id === $child['properties']['uid'][0]) {
$h_feed = $child;
break 2;
}
}
else if ($feed_id === '') {
$h_feed = $child;
break 2;
}
continue;
}
$h_feed = $child;
break 2;
}
if (in_array('h-entry', $mf_item['children'][0]['type']) ||
in_array('h-cite', $mf_item['children'][0]['type'])) {
if (in_array('h-entry', $child['type']) ||
in_array('h-cite', $child['type'])) {
$entries = $mf_item['children'];
// The parent of this h-entry may also be an h-card.
if (in_array('h-card', $mf_item['type'])) $feed_author = $mf_item;
break;
break 2;
}
}
}
// Also set the feed title and store author from the h-feed if available.
if ($feed_title === '' && isset($h_feed['properties']['name'][0])) {
$feed_title = $h_feed['properties']['name'][0];
......@@ -574,8 +624,8 @@ class SimplePie_Parser
$author = $author_cache[$author];
}
else {
$mf = Mf2\fetch($author);
foreach ($mf['items'] as $hcard) {
$mf_author = Mf2\fetch($author);
foreach ($mf_author['items'] as $hcard) {
// Only interested in an h-card by itself in this case.
if (!in_array('h-card', $hcard['type'])) {
continue;
......
......@@ -244,6 +244,11 @@ class Page {
}
private function NotModified($username, $page, $action) {
if (isset($_SESSION['reload']) && $_SESSION['reload'] === true) {
unset($_SESSION['reload']);
return false;
}
$timestamp = 0;
$mysqli = connect_db();
$query = 'SELECT timestamp FROM page_updates WHERE ' .
......@@ -251,7 +256,7 @@ class Page {
'action = "' . $action . '"';
if ($result = $mysqli->query($query)) {
if ($page_updates = $result->fetch_assoc()) {
$timestamp = $page_updates['timestamp'];
$timestamp = (int)$page_updates['timestamp'];
}
$result->close();
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment