diff options
Diffstat (limited to 'include/feedutils.php')
-rw-r--r-- | include/feedutils.php | 191 |
1 files changed, 129 insertions, 62 deletions
diff --git a/include/feedutils.php b/include/feedutils.php index 217da8188..4638ef66a 100644 --- a/include/feedutils.php +++ b/include/feedutils.php @@ -1,5 +1,8 @@ <?php - +/** + * @file include/feedutils.php + * @brief Some functions to work with XML feeds. + */ /** * @brief Return an Atom feed for channel. @@ -57,21 +60,39 @@ function get_public_feed($channel, $params) { * @param array $params * @return string with an atom feed */ - function get_feed_for($channel, $observer_hash, $params) { if(! $channel) http_status_exit(401); + + // logger('params: ' . print_r($params,true)); + + + $interactive = ((is_array($params) && array_key_exists('interactive',$params)) ? intval($params['interactive']) : 0); + + if($params['pages']) { - if(! perm_is_allowed($channel['channel_id'],$observer_hash,'view_pages')) - http_status_exit(403); - } else { - if(! perm_is_allowed($channel['channel_id'],$observer_hash,'view_stream')) - http_status_exit(403); + if(! perm_is_allowed($channel['channel_id'],$observer_hash,'view_pages')) { + if($interactive) { + return ''; + } + else { + http_status_exit(403); + } + } + } + else { + if(! perm_is_allowed($channel['channel_id'],$observer_hash,'view_stream')) { + if($interactive) { + return ''; + } + else { + http_status_exit(403); + } + } } - // logger('params: ' . print_r($params,true)); $feed_template = get_markup_template('atom_feed.tpl'); @@ -105,12 +126,28 @@ function get_feed_for($channel, $observer_hash, $params) { )); - $x = [ 'xml' => $atom, 'channel' => $channel, 'observer_hash' => $observer_hash, 'params' => $params ]; - call_hooks('atom_feed_top',$x); + $x = [ + 'xml' => $atom, + 'channel' => $channel, + 'observer_hash' => $observer_hash, + 'params' => $params + ]; + /** + * @hooks atom_feed_top + * * \e string \b xml - the generated feed and what will get returned from the hook + * * \e array \b channel + * * \e string \b observer_hash + * * \e array \b params + */ + call_hooks('atom_feed_top', $x); $atom = $x['xml']; - // a much simpler interface + /** + * @hooks atom_feed + * A much simpler interface than atom_feed_top. + * * \e string - the feed after atom_feed_top hook + */ call_hooks('atom_feed', $atom); $items = items_fetch( @@ -135,11 +172,14 @@ function get_feed_for($channel, $observer_hash, $params) { if($item['item_private']) continue; - /** @BUG $owner is undefined in this call */ $atom .= atom_entry($item, $type, null, $owner, true, '', $params['compat']); } } + /** + * @hooks atom_feed_end + * \e string - The created XML feed as a string without closing tag + */ call_hooks('atom_feed_end', $atom); $atom .= '</feed>' . "\r\n"; @@ -344,6 +384,7 @@ function get_atom_elements($feed, $item, &$author) { } } + // check for a yahoo media element (github etc.) if(! $author['author_photo']) { @@ -356,7 +397,6 @@ function get_atom_elements($feed, $item, &$author) { // No photo/profile-link on the item - look at the feed level - if((! (x($author,'author_link'))) || (! (x($author,'author_photo')))) { $rawauthor = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author'); if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) { @@ -396,7 +436,7 @@ function get_atom_elements($feed, $item, &$author) { // new style $ostatus_conversation = normalise_id(unxmlify($rawcnv[0]['attribs']['']['ref'])); if(! $ostatus_conversation) { - // old style + // old style $ostatus_conversation = normalise_id(unxmlify($rawcnv[0]['data'])); } if($ostatus_conversation) { @@ -406,7 +446,7 @@ function get_atom_elements($feed, $item, &$author) { } $ostatus_protocol = (($ostatus_conversation) ? true : false); - + $mastodon = (($item->get_item_tags('http://mastodon.social/schema/1.0','scope')) ? true : false); if($mastodon) { $ostatus_protocol = true; @@ -645,7 +685,7 @@ function get_atom_elements($feed, $item, &$author) { $res['attach'][] = array('href' => $link, 'length' => $len, 'type' => $type, 'title' => $title ); } } - + $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'object'); @@ -723,7 +763,6 @@ function get_atom_elements($feed, $item, &$author) { $res['target'] = $obj; } - if(array_key_exists('verb',$res) && $res['verb'] === ACTIVITY_SHARE && array_key_exists('obj_type',$res) && in_array($res['obj_type'], [ ACTIVITY_OBJ_NOTE, ACTIVITY_OBJ_COMMENT, ACTIVITY_OBJ_ACTIVITY ] )) { @@ -737,7 +776,13 @@ function get_atom_elements($feed, $item, &$author) { 'author' => $author, 'result' => $res ]; - + /** + * @hooks parse_atom + * * \e SimplePie \b feed - The original SimplePie feed + * * \e array \b item + * * \e array \b author + * * \e array \b result - the result array that will also get returned + */ call_hooks('parse_atom', $arr); logger('author: ' .print_r($arr['author'], true), LOGGER_DATA); @@ -782,7 +827,7 @@ function feed_get_reshare(&$res,$item) { $share['avatar'] = z_root() . '/' . get_default_profile_photo(80); if(! $share['profile']) $share['profile'] = z_root(); - + $child = $rawobj[0]['child']; @@ -809,7 +854,7 @@ function feed_get_reshare(&$res,$item) { $body = html2bbcode($body); } } - + $attach = $share['links']; if($attach) { @@ -845,16 +890,16 @@ function feed_get_reshare(&$res,$item) { } } } - + if((! $body) && ($share['alternate'])) { $body = $share['alternate']; - } + } - $res['body'] = "[share author='" . urlencode($share['author']) . + $res['body'] = "[share author='" . urlencode($share['author']) . "' profile='" . $share['profile'] . "' avatar='" . $share['avatar'] . "' link='" . $share['alternate'] . - "' posted='" . $share['created'] . + "' posted='" . $share['created'] . "' message_id='" . $share['message_id'] . "']"; $res['body'] .= $body; @@ -864,7 +909,6 @@ function feed_get_reshare(&$res,$item) { } - /** * @brief Encodes SimplePie_Item link arrays. * @@ -940,7 +984,7 @@ function process_feed_tombstones($feed,$importer,$contact,$pass) { * * @param string $xml * The (atom) feed to consume - RSS isn't as fully supported but may work for simple feeds. - * @param $importer + * @param array $importer * The contact_record (joined to user_record) of the local user who owns this * relationship. It is this person's stuff that is going to be updated. * @param[in,out] array $contact @@ -1103,7 +1147,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { // Update content if 'updated' changes if($r) { - if(activity_match($datarray['verb'],ACTIVITY_DELETE) + if(activity_match($datarray['verb'],ACTIVITY_DELETE) && $datarray['author_xchan'] === $r[0]['author_xchan']) { if(! intval($r[0]['item_deleted'])) { logger('deleting item ' . $r[0]['id'] . ' mid=' . $datarray['mid'], LOGGER_DEBUG); @@ -1147,6 +1191,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { $datarray['parent_mid'] = $pmid; } } + if(($item_parent_mid) && (! $pmid)) { logger('find_parent: matched in-reply-to: ' . $parent_mid, LOGGER_DEBUG); $pmid = $item_parent_mid[0]['parent_mid']; @@ -1172,7 +1217,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { dbesc($parent_mid), intval($importer['channel_id']) ); - + if($x) { $item_parent_mid = $x; $pmid = $x[0]['parent_mid']; @@ -1205,7 +1250,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { ); if($r) { $parent_item = $r[0]; - if(intval($parent_item['item_nocomment']) || $parent_item['comment_policy'] === 'none' + if(intval($parent_item['item_nocomment']) || $parent_item['comment_policy'] === 'none' || ($parent_item['comments_closed'] > NULL_DATE && $parent_item['comments_closed'] < datetime_convert())) { logger('comments disabled for post ' . $parent_item['mid']); continue; @@ -1215,7 +1260,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { $allowed = false; if($parent_item) { - if($parent_item['owner_xchan'] == $importer['channel_hash']) + if($parent_item['owner_xchan'] == $importer['channel_hash']) $allowed = perm_is_allowed($importer['channel_id'],$contact['xchan_hash'],'post_comments'); else $allowed = true; @@ -1230,16 +1275,17 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { // interactive feeds (such as OStatus) downstream to our followers // We do not want to set it for non-interactive feeds or conversations we do not own - if(array_key_exists('send_downstream',$importer) && intval($importer['send_downstream']) + if(array_key_exists('send_downstream',$importer) && intval($importer['send_downstream']) && ($parent_item['owner_xchan'] == $importer['channel_hash'])) { $send_downstream = true; } } else { if((! perm_is_allowed($importer['channel_id'],$contact['xchan_hash'],'send_stream')) && (! $importer['system'])) { - // @fixme check for and process ostatus autofriend - // otherwise - + /** + * @fixme check for and process ostatus autofriend + * otherwise ignore this author. + */ logger('Ignoring this author.'); continue; } @@ -1249,17 +1295,17 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { // immediate parent wasn't found. Turn into a top-level post if permissions allow // but save the thread_parent in case we need to refer to it later. - + if(! post_is_importable($datarray, $contact)) continue; + $datarray['parent_mid'] = $datarray['mid']; set_iconfig($datarray,'system','parent_mid',$parent_mid,true); } - // allow likes of comments - if($item_parent_mid && activity_match($datarray['verb'],ACTVITY_LIKE)) { + if($item_parent_mid && activity_match($datarray['verb'],ACTIVITY_LIKE)) { $datarray['thr_parent'] = $item_parent_mid[0]['parent_mid']; } @@ -1306,7 +1352,6 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { } - // if we have everything but a photo, provide the default profile photo if($author['author_name'] && $author['author_link'] && (! $author['author_photo'])) @@ -1364,7 +1409,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { // Update content if 'updated' changes if($r) { - if(activity_match($datarray['verb'],ACTIVITY_DELETE) + if(activity_match($datarray['verb'],ACTIVITY_DELETE) && $datarray['author_xchan'] === $r[0]['author_xchan']) { if(! intval($r[0]['item_deleted'])) { logger('deleting item ' . $r[0]['id'] . ' mid=' . $datarray['mid'], LOGGER_DEBUG); @@ -1426,12 +1471,12 @@ function feed_conversation_fetch($importer,$contact,$parent_link) { // GNU-Social flavoured feeds if(strpos($parent_link,'/notice/')) { $link = str_replace('/notice/','/api/statuses/show/',$parent_link) . '.atom'; - } + } // Mastodon flavoured feeds if(strpos($parent_link,'/users/') && strpos($parent_link,'/updates/')) { $link = $parent_link . '.atom'; - } + } if(! $link) return false; @@ -1446,21 +1491,21 @@ function feed_conversation_fetch($importer,$contact,$parent_link) { $data = $fetch['body']; // We will probably receive an atom 'entry' and not an atom 'feed'. Unfortunately - // our parser is a bit strict about compliance so we'll insert just enough of a feed - // tag to trick it into believing it's a compliant feed. + // our parser is a bit strict about compliance so we'll insert just enough of a feed + // tag to trick it into believing it's a compliant feed. if(! strstr($data,'<feed')) { - $data = str_replace('<entry ','<feed xmlns="http://www.w3.org/2005/Atom"><entry ',$data); + $data = str_replace('<entry ','<feed xmlns="http://www.w3.org/2005/Atom"><entry ',$data); $data .= '</feed>'; - } - + } + consume_feed($data,$importer,$contact,1); consume_feed($data,$importer,$contact,2); return true; - } + /** * @brief Normalise an id. * @@ -1479,7 +1524,7 @@ function normalise_id($id) { * * @param string $xml * The (atom) feed to consume - RSS isn't as fully supported but may work for simple feeds. - * @param $importer + * @param array $importer (unused) * The contact_record (joined to user_record) of the local user who owns this * relationship. It is this person's stuff that is going to be updated. */ @@ -1617,6 +1662,7 @@ function feed_meta($xml) { return $ret; } + /** * @brief Not yet implemented function to update feed item. * @@ -1627,6 +1673,7 @@ function update_feed_item($uid, $datarray) { item_store_update($datarray); } + /** * @brief Fetch the content of a feed and further consume it. * @@ -1659,11 +1706,10 @@ function handle_feed($uid, $abook_id, $url) { } } + /** * @brief Return a XML tag with author information. * - * @hooks \b atom_author Possibility to add further tags to returned XML string - * * \e string The created XML tag as a string without closing tag * @param string $tag The XML tag to create * @param string $nick preferred username * @param string $name displayed name of the author @@ -1672,7 +1718,7 @@ function handle_feed($uid, $abook_id, $url) { * @param int $w image width * @param string $type profile photo mime type * @param string $photo Fully qualified URL to a profile/avator photo - * @return string + * @return string XML tag */ function atom_author($tag, $nick, $name, $uri, $h, $w, $type, $photo) { $o = ''; @@ -1695,6 +1741,11 @@ function atom_author($tag, $nick, $name, $uri, $h, $w, $type, $photo) { $o .= ' <poco:preferredUsername>' . $nick . '</poco:preferredUsername>' . "\r\n"; $o .= ' <poco:displayName>' . $name . '</poco:displayName>' . "\r\n"; + /** + * @hooks atom_author + * Possibility to add further tags to returned XML string + * * \e string - The created XML tag as a string without closing tag + */ call_hooks('atom_author', $o); $o .= "</$tag>\r\n"; @@ -1703,17 +1754,23 @@ function atom_author($tag, $nick, $name, $uri, $h, $w, $type, $photo) { } -function atom_render_author($tag,$xchan) { +/** + * @brief Return an atom tag with author information from an xchan. + * + * @param string $tag + * @param array $xchan + * @return string + */ +function atom_render_author($tag, $xchan) { - - $nick = xmlify(substr($xchan['xchan_addr'],0,strpos($xchan['xchan_addr'],'@'))); + $nick = xmlify(substr($xchan['xchan_addr'], 0, strpos($xchan['xchan_addr'], '@'))); $id = xmlify($xchan['xchan_url']); $name = xmlify($xchan['xchan_name']); $photo = xmlify($xchan['xchan_photo_l']); $type = xmlify($xchan['xchan_photo_mimetype']); $w = $h = 300; - $o .= "<$tag>\r\n"; + $o = "<$tag>\r\n"; $o .= " <as:object-type>http://activitystrea.ms/schema/1.0/person</as:object-type>\r\n"; $o .= " <id>$id</id>\r\n"; $o .= " <name>$nick</name>\r\n"; @@ -1724,13 +1781,16 @@ function atom_render_author($tag,$xchan) { $o .= ' <poco:preferredUsername>' . $nick . '</poco:preferredUsername>' . "\r\n"; $o .= ' <poco:displayName>' . $name . '</poco:displayName>' . "\r\n"; + /** + * @hooks atom_render_author + * Possibility to add further tags to returned XML string. + * * \e string The created XML tag as a string without closing tag + */ call_hooks('atom_render_author', $o); $o .= "</$tag>\r\n"; return $o; - - } function compat_photos_list($s) { @@ -1740,7 +1800,7 @@ function compat_photos_list($s) { $found = preg_match_all('/\[[zi]mg(.*?)\](.*?)\[/ism',$s,$matches,PREG_SET_ORDER); if($found) { - foreach($matches as $match) { + foreach($matches as $match) { $ret[] = [ 'href' => $match[2], 'length' => 0, @@ -1754,7 +1814,6 @@ function compat_photos_list($s) { } - /** * @brief Create an item for the Atom feed. * @@ -1766,11 +1825,11 @@ function compat_photos_list($s) { * @param array $owner * @param string $comment default false * @param number $cid default 0 + * @param boolean $compat default false * @return void|string */ function atom_entry($item, $type, $author, $owner, $comment = false, $cid = 0, $compat = false) { - if(! $item['parent']) return; @@ -1922,9 +1981,17 @@ function atom_entry($item, $type, $author, $owner, $comment = false, $cid = 0, $ 'abook_id' => $cid, 'entry' => $o ]; - + /** + * @hooks atom_entry + * * \e array \b item + * * \e string \b type + * * \e array \b author + * * \e array \b owner + * * \e string \b comment + * * \e number \b abook_id + * * \e string \b entry - The generated entry and what will get returned + */ call_hooks('atom_entry', $x); return $x['entry']; } - |