fedi-tumblr/modules/timeline.php
2020-04-22 20:25:00 -05:00

369 lines
20 KiB
PHP

<?php
/* this file will fetch and render the posts of a timeline based
on the options of the user_settings cookie and the url query.
a "timeline" may be something like TWKN or just a single post
*/
$thread = timeline(array_merge($tl, $user_settings));
/*
if the timeline requested is the first part of an user profile it will render first
the user profile header, i may move this to it's own place in the future but
for now it's here.
*/
if (isset($_GET['user']) && (!isset($_GET['next']) && !isset($_GET['since']))) {
list($info, $rel) = user_info($_GET['user']);
$template = themes("get","templates/profileheader.txt");
$profile['mainColor'] = averageColor($info['avatar']);
$profile['header'] = $info['header_static'];
/* display user buttons (block, mute, list, etc) for loged in users only */
$profile['top'] = ($logedin ? "<span id='" . $info['id'] . "' class='profileButton profileMenu' style='position:relative;'>&#xe811;
<div class='menu' style='width:80px; position:absolute; background-color:yellow; top:25px; display:none;'>
<div id='" . $info['id'] . "' class='profileButton " . ($rel[0]['muting'] ? "un" : "") . "mute' style='width:100%; border-radius:0px;'>" . ($rel[0]['muting'] ? "&#xe81a; Unmute" : "&#xe81b; Mute") . "</div>
<div id='" . $info['id'] . "' class='profileButton " . ($rel[0]['blocking'] ? "un" : "") . "block' style='width:100%; border-radius:0px;'>" . ($rel[0]['blocking'] ? "&#xe80d Unblock" : "&#xe813; Block") . "</div>
</div>
</span>" : "");
if ($logedin && $rel[0]['following']) {
$profile['top'] .= "<span id='" . $info['id'] . "' class='profileButton listmenu' style='position:relative;'>Lists &#xe80b;
<div class='menu' style='width:120px; position:absolute; background-color:yellow; top:25px; left:00px; display:none;'><ul>";
$userlist = api_get("accounts/" . $info['id'] . "/lists");
$listids = array();
foreach ($userlist as $lst) {
$listids[] = $lst['id'];
}
$lists = api_get("/lists");
foreach ($lists as $list) {
$profile['top'] .= "<a href='?action=addtolist&user=" . $info['id'] . "&list=" . $list['id'] . "' id='" . $info['id'] . "' list='" . $list['id'] . "' class='" . (in_array($list['id'], $listids) ? 'rutl' : 'autl') . "' onClick='return false;'><li>" . $list['title'] . "</li></a>";
}
$profile['top'] .= "<a href='?page=lists' class='ldr' onClick='return false;'><li>Manage Lists</li></a>";
$profile['top'] .= "
</ul></div>
</span>";
}
if ($logedin) {
if ($rel[0]['following']) {
$label = "&#xe80c; Following";
$class = "unfollow";
}
else {
if ($info['locked']) {
if ($rel[0]['requested']) {
$label = "&#xe806; Follow Requested";
$class = "unfollow";
}
else {
$label = "&#xe806; Request Follow";
$class = "follow";
}
}
else {
$label = "&#xf234; Follow";
$class = "follow";
}
}
$profile['top'] .= "<span id='" . $info['id'] . "' class='profileButton $class'>$label</span>";
}
/* The NSFW button is available even to non logged in users, the value is stored in a cookie */
$profile['top'] .= "<span id='" . $info['id'] . "' class='profileButton " . (in_array($info['id'], $user_settings['nsfw']) ? "unnsfw" : "nsfw") . "'>" . (in_array($info['id'], $user_settings['nsfw']) ? "NSFW <span class='fontello'>&#xf205;</span>" : "NSFW <span class='fontello'>&#xf204;</span>") . "</span>";
/* display some indicators (blocked, muting, etc) for loged in users only */
if ($logedin) {
$profile['bottom'] = "
" . ($info['locked'] ? "<span class='profileButton' style='background-color: rgba(255,255,255,.5);'>&#xe819; Locked</span>" : "") . "
" . ($rel[0]['followed_by'] ? "<span class='profileButton' style='background-color: rgba(255,255,255,.5);'>&#xe80c; Follows You</span>" : "") . "
" . ($rel[0]['blocking'] ? "<span class='profileButton' style='background-color: rgba(255,255,255,.5);'>&#xe82c; Blocked</span>" : "") . "
" . ($rel[0]['muting'] ? "<span class='profileButton' style='background-color: rgba(255,255,255,.5);'>&#xe813; Muted</span>" : "") . "
";
}
else {
$profile['bottom'] = "";
}
/* some values are processed from the profile and stored in the $profile array*/
$profile['avatar'] = $info['avatar'];
$profile['name'] = emojify($info['display_name'], $info['emojis'], 40);
$profile['acct'] = $info['acct'];
$profile['url'] = $info['url'];
$profile['note'] = emojify(trim($info['note']) , $info['emojis']);
$profile['statuses'] = $info['statuses_count'];
$profile['following'] = $info['following_count'];
$profile['followers'] = $info['followers_count'];
/* now we replace the text of the index of $profile array with the contents of the index
in the fetched template */
foreach ($profile as $key => $value) {
$template = str_replace(":$key:", $value, $template);
}
echo $template;
}
elseif (!isset($_GET['next']) && !isset($_GET['since'])) {
echo "<div class='element'></div>";
}
/*
if the timeline array is empty or the content is not a post, we just display an error message.
*/
if ((!isset($thread[0]['id']) && !empty($thread)) || !is_array($thread)) {
//echo "<div class='element'><div class='avatar'></div><span class='button error'> Error loading the timeline. " . json_encode($thread) . "</span></div>";
} else {
$e = 0;
/* process all the posts on the timeline */
foreach ($thread as $elem) {
$post = array();
/* pid is the ABSOLUTE id of a post, not linked to the relative id,
a retweet has a different absolute id than the post that was retweeted */
$post['pid'] = $elem['id'];
$post['replyto'] = "";
$post['rt'] = "";
/* if the post is a retweet, we'll flag it as such and fetch the data of the post entity
from the "reblog" key
*/
if ($elem['reblog'] != null) {
if ($user_settings['reblog'] == 'off'){
continue;
}
$post['name'] = "<b><span id='" . $elem['reblog']['account']['id'] . "' class='user'><a class='link ldr' href='?user=" . $elem['reblog']['account']['id'] . "'>" . emojify($elem['reblog']['account']['display_name'], $elem['reblog']['account']['emojis'], 15) . "</a></span></b>";
$post['replyto'] = "<span>" . ($elem['reblog']['in_reply_to_id'] ? " <a class='fontello link preview ldr' target='_blank' id='" . $elem['reblog']['in_reply_to_id'] . "' href='?thread=" . $elem['reblog']['in_reply_to_id'] . "'>&#xf112;</a>" : "") . "</span>";
$post['rt'] = "<span>[ <span style='font-family:fontello'> &#xe826; </span> by <img src='" . $elem['account']['avatar'] . "' width=30 style='vertical-align:middle; border-radius:30px;'> <a class='link ldr user' id='" . $elem['account']['id'] . "' href='?user=" . $elem['account']['id'] . "'><span class='desktop'>" . emojify($elem['account']['display_name'], $elem['account']['emojis'], 15) . "</span><span class='mobile'>@" . explode("@", $elem['account']['acct']) [0] . "</span></a> ]</span>";
$elem = $elem['reblog'];
}
else {
$post['name'] = "<span style='position:relative;'><b><span id='" . $elem['account']['id'] . "' class='user'><a class='link ldr' href='?user=" . $elem['account']['id'] . "'>" . emojify($elem['account']['display_name'], $elem['account']['emojis'], 20) . "</a></span></span></b><span class='desktop' style='font.size:10px;'> (" . $elem['account']['acct'] . ")</span> ";
$post['replyto'] = "<span>" . ($elem['in_reply_to_id'] ? " <a class='fontello link preview ldr' target='_blank' id='" . $elem['in_reply_to_id'] . "' href='?thread=" . $elem['in_reply_to_id'] . "'>&#xf112;</a>" : "") . "</span>";
}
/* We skip the post if it contains any of the muted words */
foreach($user_settings['mtwords'] as $word){
if(contains($elem['content'],$word)){
continue 2;
}
}
/* if the poster ID was flagged as NSFW by the user, the post is flagged
as sensitive by default */
if (in_array($elem["account"]['id'], $user_settings['nsfw'])) {
$elem['sensitive'] = true;
}
/* if the settings say to avoid showing posts with NSFW content, we skip to the next post */
if ($user_settings['explicit'] == "hide" && $elem['sensitive'] == true && $tl['mode'] != "thread") {
continue;
}
/* if this thread has been hidden by the user, we skip to the next one
note that the "conversation_id" post key is only available with pleroma servers
*/
if (@in_array($elem["pleroma"]['conversation_id'], $user_settings['hide'])) {
continue;
}
/* the $json array stores some data about the thread that is embedded inside the
html of the post and is mostly used by the javascript portion of the FE. It stores
the id of the post, the visibility and who was mentioned in the post.
*/
$json['id'] = $elem['id'];
$json['scope'] = $elem['visibility'];
if ($logedin) {
$pos['mentions'] = "";
$array = $elem["mentions"];
$json['mentions'] = ($user_settings['acct'] == $elem["account"]['acct'] ? "" : "@" . $elem["account"]['acct']) . " ";
if (!empty($array)) {
foreach ($array as $mnt) {
if ($mnt['acct'] != $user_settings['acct']) {
$json['mentions'] .= "@" . $mnt['acct'] . " ";
}
}
}
}
$post['json'] = json_encode($json);
/* the options menu of the post */
$post['menu'] = "<ul>";
if ($logedin) {
$post['menu'] .= ($elem['account']['id'] == $user_settings['uid'] ? "<li><a href='?action=delete&thread=" . $elem['id'] . "' onClick='return false;' class='delete fontello' id=':id:'>&#xe80e; Delete Post</a></li>" : "");
$post['menu'] .= "<li><a href='?action=compose&quote=" . $elem['id'] . "' onClick='return false;' class='quote fontello' id='" . $elem['id'] . "' style='background-color:transparent;'>&#xf10e; Quote Post</a></li>";
$post['menu'] .= ($elem['account']['id'] != $user_settings['uid'] ? "<li><a href='?action=mute&user=" . $elem['account']['id'] . "' onClick='return false;' class='mute fontello' id='" . $elem['account']['id'] . "' style='background-color:transparent;'>&#xe81b; Mute User</a></li>" : "");
$post['menu'] .= ($elem['account']['id'] != $user_settings['uid'] ? "<li><a href='?action=mute&thread=" . $elem['account']['id'] . "' onClick='return false;' class='muteconv fontello' id='" . $elem['id'] . "' style='background-color:transparent;'>&#xf1f7; Drop Thread</a></li>" : "");
$post['menu'] .= (isset($user_settings['pleroma']) ? "<li><a href='?action=hide&thread=" . $elem['pleroma']['conversation_id'] . "' onClick='return false;' class='hide fontello' id='" . $elem['pleroma']['conversation_id'] . "' style='background-color:transparent;'>&#xf1f8; Hide Thread</a></li>" : "");
$post['menu'] .= (isset($user_settings['pleroma']) ? "<li><a href='?action=bookmark&thread=" . $elem['account']['id'] . "' onClick='return false;' class='" . ($elem['bookmarked'] == true ? "un" : "") . "bookmark fontello' id='" . $elem['id'] . "' style='background-color:transparent;'>&#xe81e; " . ($elem['bookmarked'] == true ? "Unb" : "B") . "ookmark</a></li>" : "");
$post['menu'] .= ($elem['account']['id'] != $user_settings['uid'] ? "<li><a href='?action=nsfw&user=" . $elem['account']['id'] . "' onClick='return false;' class='nsfw fontello' id='" . $elem['account']['id'] . "' style='background-color:transparent;'>&#xe829; User is NSFW</a></li>" : "");
}
$post['menu'] .= "<li><a target='_blank' href='" . $elem['url'] . "' class='original link fontello' style='background-color:transparent;'>&#xf14c; Original Note</a></li>";
$post['menu'] .= "</ul>";
$post['style'] = "";
$post['id'] = $elem['id'];
$post['avatar'] = $elem['account']['avatar'];
$post['text'] = processText($elem);
if (!empty($elem['poll'])){
$post['text'] .= "<div class='poll' id='".$elem['poll']['id']."' ".($elem['poll']['multiple'] ? "multiple" : "").">";
$post['text'] .= renderPoll($elem);
$post['text'] .= "</div>";
}
$post['spoiler'] = (empty($elem['spoiler_text']) ? "" : "<span style='font-weight:bold;font-size:20px;'>" . $elem['spoiler_text'] . "</span><br>");
$post['media'] = "";
$urls = get_urls(strip_tags($elem['content'], '<br><br \>'));
/* embed videos in the post via invidio.us if there are youtube links */
if (!empty($urls)) {
foreach ($urls as $url) {
parse_str(parse_url($url, PHP_URL_QUERY) , $my_array_of_vars);
if (isset($my_array_of_vars['v'])) {
$post['media'] = "<embed class='desktop' width='620' height='415' src='https://invidio.us/embed/" . $my_array_of_vars['v'] . "'>";
}
}
}
/* embed the media attachments to the post... */
if (!empty($elem['media_attachments'])) {
/* ...only if there are attachments and the user hasn't turned off the attachments in the settings */
if ($user_settings['attach'] != "off"){
$images = count($elem['media_attachments']);
$class = ($images === 1 ? "" : ($images > 4 ? "class='smaller'" : "class='small'"));
foreach ($elem['media_attachments'] as $file) {
$ext = explode(".", $file['url']);
$ext = end($ext);
$ext = explode("?", $ext) [0];
if (in_array($ext,array('webm','mp4','ogv'))) {
$post['media'] .= "<div style='overflow:hidden; float:left; margin:2px;' $class><video preload='metadata' style='text-align:center; max-width:100%; max-height:100%;' controls ".($user_settings['videoloop'] == "on" ? "loop" : "").">
<source src='" . $file['url'] . "' type='video/".($ext == "ogv" ? "ogg" : $ext)."'>
</video></div>
";
}
elseif (in_array($ext,array('mp3','ogg','oga','opus'))) {
$post['media'] .= "<div style='text-align:center; width:100%;'><audio controls>
<source src='" . $file['url'] . "' type='audio/$ext'>
Your browser does not support the audio tag.
</audio> </div>";
}
else {
/* we'll either hide the attachment or assign a css class depending on the user's settings */
if ($elem['sensitive'] == true && $user_settings['explicit'] != 'off') {
$post['media'] .= " <div style='overflow:hidden; float:left; margin:2px;' $class>
<div class='toggleblur' style='height:25px;'>&#xe81b; Blur</div>
<a target='_blank' href='" . $file['url'] . "' onClick='return false;' style='width:100%;' class='blur'>
<noscript><img src='" . $file['url'] . "' style='width:100%;'></noscript>
<img " . ($e < 2 ? "src='" . $file['url'] . "'" : "data-src='" . $file['url'] . "'") . " class='lazyload' style='max-width:100%; max-height:100% vertical-align:middle;'>
</a>
<a target='_blank' href='" . $file['url'] . "' onClick='return false;' class='open-lightbox' style='display:none;'>
<img src='" . $file['url'] . "' class='lazyload' style='width:100%;'>
</a>
</div>";
}
else {
$post['media'] .= "<div style='margin:0px; background-color:#000;' $class><a target='_blank' href='" . $file['url'] . "' onClick='return false;' class='open-lightbox'><img " . ($e < 2 ? "src='" . $file['url'] . "'" : "data-src='" . $file['url'] . "'") . " class='lazyload' style='" . ($images === 1 ? "width:100%;" : "max-width:100%; max-height:100%;") . " vertical-align:middle;'><noscript><img src='" . $file['url'] . "' style='width:100%;'></noscript></a></div>";
}
}
}
} else {
/* if the user turned off attachments we'll only show a link to it */
$post['text'] .= "<br><br>\n";
foreach ($elem['media_attachments'] as $file) {
$post['text'] .= "<span class='fontello'>&#xe818;</span><a href='".$file['url']."' target='_blank' onClick='return false;' class='open-lightbox'>See attachment ".($elem['sensitive'] == true ? "(NSFW)" : "")."</a><br>\n";
}
}
}
$public = "&#xe83c;";
$private = "&#xe819;";
$unlisted = "&#xe816;";
$direct = "&#xf0e0;";
/* the code of the post footer (date, visibility and like/reblog buttons */
$post['footer'] = "<div style='float:left;'>
<a style='text-decoration:none;' class='ldr postAge' id='".strtotime($elem['created_at'])."' target='_blank' href='?thread=" . $elem['id'] . "&instance=$srv" . "' title='".gmdate("d/m/y H:i", strtotime($elem['created_at']))."'>" .time_elapsed_string($elem['created_at']) . "</a> - <span class='fontello'>" . $$elem['visibility'] . " </span>
</div>
<div class='post_buttons' id='" . $elem['id'] . "'>
" . ($logedin ? "<div class='felem'><a onClick='return false' class='replyform' href='?thread=" . $elem['id'] . "' style='font-family:fontello; vertical-align:middle;' alt='reply'>&#xf112;</a></div>" : "") . "
<div class='felem'><a onClick='return false' " . ($logedin ? "class='" . ($elem['favourited'] == true ? "unfav" : "fav") . "' href='?action=true&mode=".($elem['favourited'] == true ? "off" : "true")."&fav=" . $elem['id'] . "'" : "class='notAllowed'") . " style='font-family:fontello; vertical-align:middle;'>&#xe802; <span>" . $elem['favourites_count'] . "</span></a></div>
<div class='felem'><a onClick='return false' " . ($logedin && ($elem['visibility'] == "public" || $elem['visibility'] == "unlisted") ? "class='" . ($elem['reblogged'] == true ? "unreblog" : "reblog") . "' href='?action=true&mode=".($elem['reblogged'] == true ? "off" : "on")."&reblog=" . $elem['id'] . "'" : "class='notAllowed'") . " style='font-family:fontello; vertical-align:middle;'>&#xe83a; <span>" . $elem['reblogs_count'] . "</span></a></div>
<div class='felem'><a class='replies' onClick='return false' href='?thread=" . $elem['id'] . "' style='font-family:fontello; vertical-align:middle;'>&#xe827; <span>" . $elem['replies_count'] . "</span></a></div>
</div>";
$post['form'] = ($tl['mode'] == 'thread' && $logedin ? str_replace(array(
":id:",
":content:"
) , array(
$elem['id'],
$json['mentions']
) , themes("get","templates/replyform.txt")) : "");
$post['ancestors'] = " ";
$post['descendants'] = " ";
$post['notes'] = "";
/* if the timeline is a single post, we'll fetch the replies and notes */
if ($tl['mode'] == 'thread') {
$replies = ($tl['mode'] == "thread" ? getreplies($tl['thread']) : false);
$notes = ($tl['mode'] == "thread" ? getnotes($tl['thread']) : false);
foreach ($replies as $e) {
if ($e['mode'] == 'ancestor') {
$post['ancestors'] .= render_reply($e['content']);
}
else {
$post['descendants'] .= render_reply($e['content']);
}
}
if ($notes) {
foreach ($notes as $note) {
$post['notes'] .= "<div id='" . $note[1]['id'] . "'>
<a href='?user=" . $note[1]['id'] . "' class='ldr' title='" . $note[1]['acct'] . "'>
<div class='nte' style='background-image:url(" . $note[1]['avatar'] . ");'>
<div class='nte_type' style='background-color:" . ($note[0] == "fav" ? "red" : "green") . "'><span>" . ($note[0] == "fav" ? "&#xe802;" : "&#xe826;") . "</span></div>
</div>
</a>
</div>";
}
}
}
if ((empty($elem['media_attachments']) && $post['media'] == "") || (!(empty($elem['media_attachments']) && $post['media'] == "") && $user_settings['attach'] == "off")) {
$post['template'] = themes("get","templates/textpost.txt");
}
else {
$post['template'] = themes("get","templates/post.txt");
}
foreach ($post as $key => $value) {
$post['template'] = str_replace(":$key:", $value, $post['template']);
}
echo $post['template'];
unset($post);
$e++;
$next = $elem['id'];
}
if ((!isset($_GET['next']) || (isset($_GET['next']) && !isset($_GET['ajax']))) && !isset($_GET['since']) && !isset($_GET['thread'])) {
$query = http_build_query(array_filter(array(
'user' => (isset($tl['user']) ? $tl['user'] : false) ,
'mode' => $tl['mode']
)));
if ($tl['mode'] != "search"){
echo "<div class='element'><div class='avatar' style='height:0px;'></div><div class='loadmore' style='display:table-cell; height:50px; line-height:50px;'><a class='link' onClick='return false;' style='margin:5px;' href='?next=$next&" . $query . "'>Load More Posts</a></div></div>";
}
}
}
?>