streams/include/feedutils.php

430 lines
13 KiB
PHP
Raw Permalink Normal View History

<?php
2021-12-03 03:01:39 +00:00
/**
* @file include/feedutils.php
* @brief Some functions to work with XML feeds.
*/
2022-02-16 04:08:28 +00:00
use Code\Lib\Img_filesize;
use Code\Extend\Hook;
2024-03-10 02:40:50 +00:00
use Code\Lib\Time;
2022-02-16 04:08:28 +00:00
use Code\Render\Theme;
2022-02-12 20:43:29 +00:00
2021-12-02 22:33:36 +00:00
/**
* @brief Return an Atom feed for channel.
*
* @see get_feed_for()
*
* @param array $channel
* @param array $params associative array which configures the feed
* @return string with an atom feed
*/
2021-12-03 03:01:39 +00:00
function get_public_feed($channel, $params)
{
if (! $params) {
$params = [];
}
$params['type'] = ((x($params, 'type')) ? $params['type'] : 'xml');
$params['begin'] = ((x($params, 'begin')) ? $params['begin'] : NULL_DATE);
2024-03-10 02:40:50 +00:00
$params['end'] = ((x($params, 'end')) ? $params['end'] : Time::convert('UTC', 'UTC', 'now'));
2021-12-03 03:01:39 +00:00
$params['start'] = ((x($params, 'start')) ? $params['start'] : 0);
$params['records'] = ((x($params, 'records')) ? $params['records'] : 40);
$params['direction'] = ((x($params, 'direction')) ? $params['direction'] : 'desc');
$params['pages'] = ((x($params, 'pages')) ? intval($params['pages']) : 0);
$params['top'] = ((x($params, 'top')) ? intval($params['top']) : 0);
$params['cat'] = ((x($params, 'cat')) ? $params['cat'] : '');
$params['compat'] = ((x($params, 'compat')) ? intval($params['compat']) : 0);
2024-04-07 19:31:52 +00:00
$params['noadd'] = ((x($params, 'noadd')) ? intval($params['noadd']) : 1);
2021-12-03 03:01:39 +00:00
switch ($params['type']) {
case 'json':
header("Content-type: application/atom+json");
break;
case 'xml':
default:
header("Content-type: application/atom+xml");
break;
}
return get_feed_for($channel, get_observer_hash(), $params);
}
/**
* @brief Create an atom feed for $channel from template.
*
* @param array $channel
* @param string $observer_hash xchan_hash from observer
* @param array $params
* @return string with an atom feed
*/
2021-12-03 03:01:39 +00:00
function get_feed_for($channel, $observer_hash, $params)
{
2021-12-03 03:01:39 +00:00
if (! $channel) {
http_status_exit(401);
}
2021-12-03 03:01:39 +00:00
// logger('params: ' . print_r($params,true));
2021-12-03 03:01:39 +00:00
$interactive = ((is_array($params) && array_key_exists('interactive', $params)) ? intval($params['interactive']) : 0);
2021-12-03 03:01:39 +00:00
if ($params['pages']) {
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);
}
}
}
2022-02-12 20:43:29 +00:00
$feed_template = Theme::get_template('atom_feed.tpl');
2021-12-03 03:01:39 +00:00
$atom = '';
2021-12-03 03:01:39 +00:00
$feed_author = '';
2022-11-20 06:44:13 +00:00
$atom .= replace_macros($feed_template, [
2022-02-16 04:08:28 +00:00
'$version' => xmlify(Code\Lib\System::get_project_version()),
2022-07-20 21:15:56 +00:00
'$generator' => xmlify(Code\Lib\System::get_project_name()),
'$generator_uri' => z_root(),
2021-12-03 03:01:39 +00:00
'$feed_id' => xmlify($channel['xchan_url']),
'$feed_title' => xmlify($channel['channel_name']),
2024-03-10 02:40:50 +00:00
'$feed_updated' => xmlify(Time::convert('UTC', 'UTC', 'now', ISO8601)),
2021-12-03 03:01:39 +00:00
'$author' => $feed_author,
'$profile_page' => xmlify($channel['xchan_url']),
2022-11-20 06:44:13 +00:00
]);
2021-12-03 03:01:39 +00:00
$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
*/
Hook::call('atom_feed_top', $x);
2021-12-03 03:01:39 +00:00
$atom = $x['xml'];
/**
* @hooks atom_feed
* A much simpler interface than atom_feed_top.
* * \e string - the feed after atom_feed_top hook
*/
Hook::call('atom_feed', $atom);
2021-12-03 03:01:39 +00:00
$items = items_fetch(
[
'wall' => '1',
'datequery' => $params['end'],
'datequery2' => $params['begin'],
'start' => intval($params['start']),
'records' => intval($params['records']),
'direction' => dbesc($params['direction']),
'pages' => $params['pages'],
'order' => dbesc('post'),
'top' => $params['top'],
'cat' => $params['cat'],
'compat' => $params['compat'],
2024-04-17 02:34:14 +00:00
'noadd' => $params['noadd']
2021-12-03 03:01:39 +00:00
],
$channel,
$observer_hash,
CLIENT_MODE_NORMAL,
App::$module
);
if ($items) {
$type = 'html';
foreach ($items as $item) {
if ($item['item_private']) {
continue;
}
2022-11-20 06:44:13 +00:00
$atom .= atom_entry($item, $type, null, $owner, true, '');
2021-12-03 03:01:39 +00:00
}
}
/**
* @hooks atom_feed_end
* \e string - The created XML feed as a string without closing tag
*/
Hook::call('atom_feed_end', $atom);
2021-12-03 03:01:39 +00:00
$atom .= '</feed>' . "\r\n";
return $atom;
}
/**
* @brief Return a XML tag with author information.
*
* @param string $tag The XML tag to create
* @param string $nick preferred username
* @param string $name displayed name of the author
* @param string $uri
* @param int $h image height
* @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 XML tag
*/
2021-12-03 03:01:39 +00:00
function atom_author($tag, $nick, $name, $uri, $h, $w, $type, $photo)
{
$o = '';
if (! $tag) {
return $o;
}
$nick = xmlify($nick);
$name = xmlify($name);
$uri = xmlify($uri);
$h = intval($h);
$w = intval($w);
$photo = xmlify($photo);
$o .= "<$tag>\r\n";
$o .= " <name>$name</name>\r\n";
$o .= " <uri>$uri</uri>\r\n";
$o .= ' <link rel="photo" type="' . $type . '" media:width="' . $w . '" media:height="' . $h . '" href="' . $photo . '" />' . "\r\n";
$o .= ' <link rel="avatar" type="' . $type . '" media:width="' . $w . '" media:height="' . $h . '" href="' . $photo . '" />' . "\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
*/
Hook::call('atom_author', $o);
2021-12-03 03:01:39 +00:00
$o .= "</$tag>\r\n";
return $o;
}
/**
* @brief Return an atom tag with author information from an xchan.
*
* @param string $tag
* @param array $xchan
* @return string
*/
2021-12-03 03:01:39 +00:00
function atom_render_author($tag, $xchan)
{
$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 .= " <name>$name</name>\r\n";
$o .= " <uri>$id</uri>\r\n";
$o .= ' <link rel="photo" type="' . $type . '" media:width="' . $w . '" media:height="' . $h . '" href="' . $photo . '" />' . "\r\n";
$o .= ' <link rel="avatar" type="' . $type . '" media:width="' . $w . '" media:height="' . $h . '" href="' . $photo . '" />' . "\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
*/
Hook::call('atom_render_author', $o);
2021-12-03 03:01:39 +00:00
$o .= "</$tag>\r\n";
return $o;
}
2021-12-03 03:01:39 +00:00
function compat_photos_list($s)
{
2021-12-03 03:01:39 +00:00
$ret = [];
2021-12-03 03:01:39 +00:00
$found = preg_match_all('/\[[zi]mg(.*?)\](.*?)\[/ism', $s, $matches, PREG_SET_ORDER);
2021-12-03 03:01:39 +00:00
if ($found) {
foreach ($matches as $match) {
$entry = [
'href' => $match[2],
'type' => guess_image_type($match[2])
];
$sizer = new Img_filesize($match[2]);
$size = $sizer->getSize();
if (intval($size)) {
$entry['length'] = intval($size);
}
2021-12-03 03:01:39 +00:00
$ret[] = $entry;
}
}
2021-12-03 03:01:39 +00:00
return $ret;
}
/**
* @brief Create an item for the Atom feed.
*
* @param array $item
* @param string $type
2022-11-20 06:44:13 +00:00
* @param array|null $author
* @param array $owner
* @param string $comment default false
* @param number $cid default 0
* @return void|string
2022-11-20 06:44:13 +00:00
*@see get_feed_for()
*
*/
2022-11-20 06:44:13 +00:00
function atom_entry($item, $type, $author, $owner, $comment = false, $cid = 0)
2021-12-03 03:01:39 +00:00
{
if (! $item['parent']) {
return;
}
if ($item['deleted']) {
2024-03-10 02:40:50 +00:00
return '<at:deleted-entry ref="' . xmlify($item['mid']) . '" when="' . xmlify(Time::convert('UTC', 'UTC', $item['edited'] . '+00:00', ISO8601)) . '" />' . "\r\n";
2021-12-03 03:01:39 +00:00
}
create_export_photo_body($item);
// provide separate summary and content unless compat is true; as summary represents a content-warning on some networks
$summary = $item['summary'];
$body = $item['body'];
$compat_photos = null;
$o = "\r\n\r\n<entry>\r\n";
if (is_array($author)) {
$o .= atom_render_author('author', $author);
} else {
$o .= atom_render_author('author', $item['author']);
}
if (($item['parent'] != $item['id']) || ($item['parent_mid'] !== $item['mid']) || (($item['thr_parent'] !== '') && ($item['thr_parent'] !== $item['mid']))) {
2022-10-22 00:28:02 +00:00
$parent_item = (($item['thr_parent']) ?: $item['parent_mid']);
2021-12-03 03:01:39 +00:00
$o .= '<thr:in-reply-to ref="' . xmlify($parent_item) . '" type="text/html" href="' . xmlify($item['plink']) . '" />' . "\r\n";
}
2022-07-20 21:34:13 +00:00
$o .= '<title>' . xmlify($item['title']) . '</title>' . "\r\n";
if ($summary) {
$o .= '<summary type="' . $type . '" >' . xmlify(prepare_text($summary, $item['mimetype'])) . '</summary>' . "\r\n";
}
$o .= '<content type="' . $type . '" >' . xmlify(prepare_text($body, $item['mimetype'])) . '</content>' . "\r\n";
2021-12-03 03:01:39 +00:00
$o .= '<id>' . xmlify($item['mid']) . '</id>' . "\r\n";
2024-03-10 02:40:50 +00:00
$o .= '<published>' . xmlify(Time::convert('UTC', 'UTC', $item['created'] . '+00:00', ISO8601)) . '</published>' . "\r\n";
$o .= '<updated>' . xmlify(Time::convert('UTC', 'UTC', $item['edited'] . '+00:00', ISO8601)) . '</updated>' . "\r\n";
2021-12-03 03:01:39 +00:00
$o .= '<link rel="alternate" type="text/html" href="' . xmlify($item['plink']) . '" />' . "\r\n";
if ($item['attach']) {
$enclosures = json_decode($item['attach'], true);
if ($enclosures) {
foreach ($enclosures as $enc) {
2022-05-05 20:17:09 +00:00
if (is_array($enc)) {
$o .= '<link rel="enclosure" '
. (isset($enc['href']) ? 'href="' . $enc['href'] . '" ' : '')
. (isset($enc['length']) ? 'length="' . $enc['length'] . '" ' : '')
. (isset($enc['type']) ? 'type="' . xmlify($enc['type']) . '" ' : '')
2022-05-05 20:17:09 +00:00
. ' />' . "\r\n";
}
2021-12-03 03:01:39 +00:00
}
}
}
if ($item['term']) {
foreach ($item['term'] as $term) {
$scheme = '';
$label = '';
switch ($term['ttype']) {
case TERM_HASHTAG:
2022-07-20 21:34:13 +00:00
$scheme = z_root() . '/term/hashtag';
2021-12-03 03:01:39 +00:00
$label = '#' . str_replace('"', '', $term['term']);
break;
case TERM_CATEGORY:
2022-07-20 21:34:13 +00:00
$scheme = z_root() . '/term/category';
2021-12-03 03:01:39 +00:00
$label = str_replace('"', '', $term['term']);
break;
default:
break;
}
if (! $scheme) {
continue;
}
$o .= '<category scheme="' . $scheme . '" term="' . str_replace('"', '', $term['term']) . '" label="' . $label . '" />' . "\r\n";
}
}
$o .= '</entry>' . "\r\n";
// build array to pass to hook
$x = [
'item' => $item,
'type' => $type,
'author' => $author,
'owner' => $owner,
'comment' => $comment,
'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
*/
Hook::call('atom_entry', $x);
2021-12-03 03:01:39 +00:00
return $x['entry'];
}
2019-07-11 01:43:20 +00:00
2021-12-03 03:01:39 +00:00
function get_mentions($item, $tags)
{
$o = '';
2019-07-11 01:43:20 +00:00
2021-12-03 03:01:39 +00:00
if (! count($tags)) {
return $o;
}
2019-07-11 01:43:20 +00:00
2021-12-03 03:01:39 +00:00
foreach ($tags as $x) {
if ($x['ttype'] == TERM_MENTION) {
$o .= "\t\t" . '<link rel="mentioned" href="' . $x['url'] . '" />' . "\r\n";
}
}
return $o;
2019-07-11 01:43:20 +00:00
}