$output )); $result = curl_exec($cSession); curl_close($cSession); return $result; } /* this function fetches all the data from a profile (bio, username, relationships, etc) - $user is the id of the queried user - returns the array conversion of the json response */ function user_info($user) { global $user_settings; $info = api_get("accounts/" . $user); $rel = api_get("accounts/relationships?id=" . $user); return array( $info, $rel ); } /* this function fetches the context (the previous posts and replies) of a specified post - $post = ID of the post queried. - returns an array conversion of the json response */ function context($post) { global $srv; global $token; $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, "https://$srv/api/v1/statuses/$post/context"); if (!is_null($token)) { curl_setopt($curl, CURLOPT_HTTPHEADER, array( 'Authorization: Bearer ' . $token )); } curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); $result = curl_exec($curl); curl_close($curl); return $result; } /* a function to fav or unfav a specified post - $post = ID of the post queried. - $mode can be true if is a fav or false if it's an unfav - returns the number of favs of the post after the specified action or "error" if there was an error performing the action */ function favourite($post, $mode) { $result = api_post(($mode == true ? "statuses/$post/favourite" : "statuses/$post/unfavourite"),array()); if (isset($result['favourites_count'])) { return $result['favourites_count']; } else { return "error"; } } /* a function to reblog or unreblog a specified post - $post = ID of the post queried. - $mode can be true if is a reblog or false if it's an unreblog - returns the number of reblogs of the post after the specified action or "error" if there was an error performing the action */ function reblog($post, $mode) { $result = api_post(($mode == true ? "statuses/$post/reblog" : "statuses/$post/unreblog"),array()); if (isset($result['reblog']['reblogs_count'])) { return $result['reblog']['reblogs_count']; } elseif (isset($result['reblogs_count'])) { return $result['reblogs_count']; } else { return "error"; } } /* function to delete a post - $id is the id of the post to delete - returns 1 if the post was deleted succesfully or 0 if there was an error */ function delpost($id) { global $srv; global $token; if (!is_null($token)) { $curl = curl_init(); curl_setopt($curl, CURLOPT_HEADER, false); curl_setopt($curl, CURLOPT_POST, 1); curl_setopt($curl, CURLOPT_URL, "https://$srv/api/v1/statuses/$id"); curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE"); curl_setopt($curl, CURLOPT_HTTPHEADER, array( 'Authorization: Bearer ' . $token )); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); $result = curl_exec($curl); curl_close($curl); $result = json_decode($result, true); if (empty($result)) { return "1"; } else { return "0"; } } } /* function to issue a vote to a poll - $id is the id of the poll to vote on - $choices is a comma separated string of the choices that will be voted. */ function vote($poll, $choices) { global $srv; global $token; if (!is_null($token)) { $cSession = curl_init(); curl_setopt($cSession, CURLOPT_URL, "https://$srv/api/v1/polls/$poll/votes"); curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true); curl_setopt($cSession, CURLOPT_POST, 1); curl_setopt($cSession, CURLOPT_HTTPHEADER, array( 'Authorization: Bearer ' . $token )); $query = ""; $choicelist = explode(",",$choices); foreach($choicelist as $choice){ $query .= "choices[]=$choice&"; } curl_setopt($cSession, CURLOPT_POSTFIELDS, $query); $result = curl_exec($cSession); curl_close($cSession); return $result; } else { return false; } } /* function to send a new post to the logged in instance - $text = the body of the message - $media = array of uploaded media id's - $reply = the id of a post being replied to, can be null. - $markdown = specify if the post uses markdown (unused at this moment) - $scope = the scope of the post (public, private, unlisted or direct) - $sensitive = bool to specify if the post media is sensitive - $spoiler = string of the title of the post - returns the json of the api response */ function sendpost($text, $media, $reply = null, $markdown = false, $scope = "public", $sensitive = false, $spoiler = false) { global $srv; global $token; if (!is_null($token)) { $cSession = curl_init(); curl_setopt($cSession, CURLOPT_URL, "https://$srv/api/v1/statuses"); curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true); curl_setopt($cSession, CURLOPT_POST, 1); curl_setopt($cSession, CURLOPT_HTTPHEADER, array( 'Authorization: Bearer ' . $token )); $query = ""; $query .= "status=" . urlencode(html_entity_decode($text, ENT_QUOTES)) . "&visibility=" . $scope;; if (!is_null($reply) && $reply != "null") { $query .= "&in_reply_to_id=" . $reply; } if ($markdown == true) { $query .= "&content_type=text/markdown"; } if ($sensitive == 'true') { $query .= "&sensitive=true"; } if ($spoiler == true) { $query .= "&spoiler_text=" . $spoiler; } if (!is_null($media)) { foreach ($media as $mid) { $query .= "&media_ids[]=" . $mid; } } curl_setopt($cSession, CURLOPT_POSTFIELDS, $query); $result = curl_exec($cSession); curl_close($cSession); return $result; } else { return false; } } /* uploads a file to the logged in instance. - $file = path of the file to upload on local storage - returns an array where: 0: the ID of the uploaded file 1: the url of the file (if the file isn't an image, returns a placeholder) */ function uploadpic($file) { global $srv; global $token; if (!is_null($token)) { $mime = get_mime($file); $info = pathinfo($file); $name = $info['basename']; $output = new CURLFile($file, $mime, $name); $cSession = curl_init(); curl_setopt($cSession, CURLOPT_URL, "https://$srv/api/v1/media"); curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true); curl_setopt($cSession, CURLOPT_POST, 1); curl_setopt($cSession, CURLOPT_HTTPHEADER, array( 'Authorization: Bearer ' . $token )); curl_setopt($cSession, CURLOPT_POSTFIELDS, array( 'file' => $output )); $result = curl_exec($cSession); curl_close($cSession); echo $result; $array = json_decode($result, true); $ext = explode(".", $array['url']); $ext = end($ext); $ext = explode("?", $ext) [0]; if (in_array($ext, array('jpg','jpeg','gif','png','svg','webm'))) { $file = $array['url']; } elseif (in_array($ext, array('mp4','webp','ogv'))) { $file = "img/vid.png"; } elseif (in_array($ext, array('mp3','ogg','oga','opus'))) { $file = "img/aud.png"; } else { $file = "img/doc.png"; } return json_encode(array( $array['id'], $file )); } else { return false; } } /* this is used to register DashFE as an application on an instance mostly used when logging in - $instance = the url of the instance where to log in - returns the array conversion of the json response */ function register_app($instance) { global $setting; $cSession = curl_init(); curl_setopt($cSession, CURLOPT_URL, "https://$instance/api/v1/apps"); curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true); curl_setopt($cSession, CURLOPT_HEADER, false); curl_setopt($cSession, CURLOPT_POST, 1); curl_setopt($cSession, CURLOPT_POSTFIELDS, http_build_query(array( 'client_name' => $setting['appname'], 'redirect_uris' => $setting['url'], 'scopes' => 'read write follow' ))); $result = curl_exec($cSession); curl_close($cSession); return json_decode($result, true); } /* this function will get all the notes (reblogs and favs) that has an specified post - $thread = the id of the post queried - returns an array with all the notes each note is another array where: 0 = type of note ("reb" reblog or "fab" favorite) 1 = array of the details of the user, converted from the json of the api response. */ function getnotes($thread) { global $user_settings; global $token; global $srv; global $setting; global $logedin; @$reb = array( api_get("statuses/" . $thread . "/reblogged_by") ); @$fab = array( api_get("statuses/" . $thread . "/favourited_by") ); $limit = (count($reb[0]) > count($fab[0]) ? count($reb[0]) - 1 : count($fab[0]) - 1); $notes = array(); $index = 0; for ($i = 0;$i <= $limit;$i++) { if (isset($reb[0][$i])) { $notes[$index][0] = "reb"; $notes[$index][1] = $reb[0][$i]; $index++; } if (isset($fab[0][$i])) { $notes[$index][0] = "fav"; $notes[$index][1] = $fab[0][$i]; $index++; } } return $notes; } /* this function will fetch replies of a post - $thread = the id of the post from where to fetch replies - $since = id of a post. If specified, the function will fetch the replies only since the specified id. - returns an array with all the replies each element is another array where: 'mode' = type of the reply (ancestor or descendant) 'content' = array of the contents of the reply, converted from the json of the api response. */ function getreplies($thread, $since = false) { global $user_settings; global $token; global $srv; global $setting; global $logedin; $context = json_decode(context($thread) , true); $array = array(); if (!empty($context['ancestors'])) { if ($since == false) { foreach ($context['ancestors'] as $elem) { $elem['type'] = 'ancestor'; $array[] = $elem; } } } $flag = 0; if (!empty($context['descendants'])) { foreach ($context['descendants'] as $elem) { if (($since != false && $flag == 1) || $since == false) { $elem['type'] = 'descendant'; $array[] = $elem; } if ($since != false && $elem['id'] == $since) { $flag = 1; } } } $replies = array(); foreach ($array as $item) { $reply['mode'] = ""; if ($item['type'] == 'ancestor') { $reply['mode'] = "ancestor"; } $replies[] = array( 'mode' => $reply['mode'], 'content' => $item ); } return $replies; } /* this function takes some options from the init.php file and the user_settings cookie and fetches all the posts of the appropiate timeline - $query = an array with a set of elements generated by the file "include/init.php" - returns an array of the posts list fetched converted from the json response of the api. */ function timeline($query) { global $token; global $srv; $notes = ""; $hq = array(); $hq['limit'] = 10; $hq['only_media'] = ($query['text'] == "off" ? 'true' : 'false'); if ($query['next']){ $hq['max_id'] = $query['next']; $next = $query['next']; } elseif ($query['since']) { $hq['since_id'] = $query['since']; } switch ($query['mode']) { case "home": $array = api_get("timelines/home?".http_build_query($hq)); break; case "federated": $array = api_get("timelines/public?".http_build_query($hq)); break; case "tag": $array = api_get("timelines/tag/" . $query['tag'] . "?".http_build_query($hq)); break; case "local": $array = api_get("timelines/public?local=true&".http_build_query($hq)); break; case "user": $array = api_get("accounts/" . $query['user'] . "/statuses?".http_build_query($hq)); break; case "thread": $array = array( api_get("statuses/" . $query['thread']) ); break; case "favourites": $array = api_get("favourites?".http_build_query($hq)); break; case "direct": $array = api_get("timelines/direct?".http_build_query($hq)); break; case "list": $array = api_get("timelines/list/" . $query['list'] . "?".http_build_query($hq)); break; case "bookmarks": $array = api_get("bookmarks?".http_build_query($hq)); break; case "search": $array = api_getv2("search?limit=40&q=".$query['search']."{$next}")['statuses']; break; case "account": $info = api_get("accounts/verify_credentials"); $array = api_get("accounts/" . $info['id'] . "/statuses?".http_build_query($hq)); break; default: $array = api_get("timelines/public?".http_build_query($hq)); break; } if (!is_array($array)) { return false; } $next = end($array) ['id']; $thread = array(); /* foreach ($array as $elem) { if ($query['replies'] == "on" || $query['mode'] == "thread") { $thread[] = $elem; } else { if ($elem['in_reply_to_id'] == null) { $thread[] = $elem; } } }*/ foreach ($array as $elem) { if ($query['replies'] == "on" || $query['mode'] == "thread") { $thread[] = $elem; } else { if ($elem['in_reply_to_id'] == null || $elem['in_reply_to_account_id'] == $query['uid']) { $thread[] = $elem; } else { $rel = api_get("accounts/relationships?id=" . $elem['in_reply_to_account_id']); if ($rel[0]['following']){ $thread[] = $elem; } } } } return $thread; } // FUNCTIONS THAT HAVE TO DO WITH RENDERING STUFF FOR THE PAGE /* this function is used to generate the html code of a poll */ function renderPoll($elem) { global $logedin; $output = ""; $output .= "
"; $votes = $elem['poll']['votes_count']; if ($elem['poll']['voted'] || $elem['poll']['expired']) { $output.= "Votes: $votes
"; foreach ($elem['poll']['options'] as $option){ $percentage = ($option['votes_count'] / $votes ) * 100; $output .= "
".$option['title']."
"; } } else { foreach ($elem['poll']['options'] as $option){ $output .= "
".$option['title']."
"; } $output .= ($logedin ? "" : ""); } return $output; } /* this function is used to generate the html code of a reply */ function render_reply($item) { global $user_settings; global $logedin; global $srv; $reply['mode'] = ""; if (isset($item['type']) && $item['type'] == 'ancestor') { $reply['mode'] = "ancestor"; } $reply['id'] = $item['id']; $reply['uid'] = $item['account']['id']; $reply['name'] = emojify($item['account']['display_name'], $item['account']['emojis'], 20); $reply['acct'] = $item['account']['acct']; $reply['handle'] = "@".explode("@",$item['account']['acct'])[0]; $reply['avatar'] = $item['account']['avatar']; $reply['menu'] = ""; $json['id'] = $item['id']; $json['scope'] = $item['visibility']; if ($logedin) { $json['mentions'] = ""; $array = $item["mentions"]; $json['mentions'] = ($user_settings['acct'] == $item["account"]['acct'] ? "" : "@" . $item["account"]['acct']) . " "; if (!empty($array)) { foreach ($array as $mnt) { if ($mnt['acct'] != $user_settings['acct']) { $json['mentions'] .= "@" . $mnt['acct'] . " "; } } } } $reply['json'] = json_encode($json); $reply['replyto'] = ($item['in_reply_to_id'] ? " " : ""); $reply['text'] = processText($item); $reply['date'] = "" . time_elapsed_string($item['created_at']) . ""; $reply['visibility'] = $item['visibility']; $reply['media'] = ""; if (!empty($item['media_attachments'])) { $reply['media'] = "
"; $images = count($item['media_attachments']); $class = ($images > 1 ? "class='icon'" : ""); foreach ($item['media_attachments'] as $file) { $ext = explode(".", $file['url']); $ext = end($ext); $ext = explode("?", $ext) [0]; if (in_array($ext,array('webm','mp4','ogv'))) { $reply['media'] .= "
"; } elseif (in_array($ext,array('mp3','ogg','oga','opus'))) { $reply['media'] .= "
"; } else { if ($item['sensitive'] == true && $user_settings['explicit'] != 'off') { $reply['media'] .= "
"; } else { $reply['media'] .= "
"; } } } $reply['media'] .= "
"; } $reply['buttons'] = " " . ($logedin ? "
" : "") . "
" . $item['favourites_count'] . "
" . $item['reblogs_count'] . "
"; $result = themes("get","templates/reply.txt"); foreach ($reply as $key => $elem) { $result = str_replace(":$key:", $elem, $result); } return $result; } /* this is the same as above but is used on other places, like user bios and places where the shortcodes and the emoji url is defined in the same place */ function emojify($string, $emojis, $size = 40) { foreach ($emojis as $emoji) { $string = str_replace(":" . $emoji['shortcode'] . ":", "" . $emoji[", $string); } return $string; } /* This function displays the emoji list of an instance based on a search string given on $val */ function emoji_list($val){ $emojilist = api_get("/custom_emojis"); $c = 0; $return = ""; foreach ($emojilist as $emoji){ if (starts_with($emoji['shortcode'],$val) && $c < 50){ $return .= ""; $c++; } } if ($c < 50){ foreach ($emojilist as $emoji){ if ((contains($emoji['shortcode'],$val) && !starts_with($emoji['shortcode'],$val)) && $c < 50){ $return .= ""; $c++; } } } return $return; } function contact_search($val){ global $user_settings; $return = ""; $list = api_get("/accounts/search?q=".$val); foreach ($list as $contact){ $return .= "
".emojify($contact['display_name'], $contact['emojis'], 15)."
".$contact['acct']."
"; } return $return; } /* This function will fetch and render all the notifications since an $id or get a list of all past notification ($max notifications specified) */ function getnotif($id = false, $max = false) { global $srv; global $token; global $user_settings; $n = ""; $exclude = ""; $exclude .= (str_split($user_settings['notif'])[0] == 0 ? "&exclude_types[]=favourite" : ""); $exclude .= (str_split($user_settings['notif'])[1] == 0 ? "&exclude_types[]=reblog" : ""); $exclude .= (str_split($user_settings['notif'])[2] == 0 ? "&exclude_types[]=mention" : ""); $exclude .= (str_split($user_settings['notif'])[3] == 0 ? "&exclude_types[]=follow" : ""); $notif = api_get("notifications?" . ($id == false ? "limit=9&" : "") . ($id != false ? ($max == true ? "max_id=$id" : "since_id=$id") : "").$exclude); if (!empty($notif)) { foreach ($notif as $post) { if (!isset($post["type"])){ break; } $user = "" . (empty($post['account']['display_name']) ? $post['account']['acct'] : emojify($post['account']['display_name'], $post['account']['emojis'], 10)) . ""; $preview = ""; $buttons = ""; $media = ""; if (!in_array($post["type"],array("mention","favourite","reblog","follow"))){ continue; } switch ($post["type"]) { case "mention": if ($post['status']['in_reply_to_id'] == null) { $type = ""; $string = "mentioned you in a post"; $preview = "" . emojify(strip_tags(trim($post['status']['content']) , '

') , $post['status']['emojis'], 15) . "
"; $media = (!empty($post['status']['media_attachments']) ? "
" : ""); } else { $type = ""; $string = "replied to your post"; foreach ($post['status']['mentions'] as $mention) { if(!contains($post['status']['content'],$mention['username'])) { $post['status']['content'] = "@" . $mention['username'] . " ".$post['status']['content']; } } $preview = "" . emojify(strip_tags(trim($post['status']['content']) , '

') , $post['status']['emojis'], 15) . "
"; $media = (!empty($post['status']['media_attachments']) ? "
" : ""); } $array = $post['status']["mentions"]; $mentions = ($user_settings['acct'] == $post['status']['account']['acct'] ? "" : "@" . $post['status']['account']['acct']) . " "; if (!empty($array)) { foreach ($array as $mnt) { if ($mnt['acct'] != $user_settings['acct']) { $mentions .= "@" . $mnt['acct'] . " "; } } } $buttons = "
"; break; case "favourite": $type = ""; $string = "favourited your post"; $preview = "" . (!empty($post['status']['content']) ? emojify(strip_tags(trim($post['status']['content']) , '

') , $post['status']['emojis'], 15) : "Favourited your image") . "
"; $media = (!empty($post['status']['media_attachments']) ? "
" : ""); break; case "reblog": $type = ""; $string = "reblogged your post"; $preview = "" . (!empty($post['status']['content']) ? emojify(strip_tags(trim($post['status']['content']) , '

') , $post['status']['emojis'], 15) : "Reblogged your image") . "
"; @$media = (!is_null($post['status']['media_attachments']) ? "
" : ""); break; case "follow": list($info, $rel) = user_info($post["account"]["id"]); $type = ""; $preview = "started following you"; if ($rel[0]['following']) { $label = " Following"; $class = "unfollow"; } else { if ($info['locked']) { if ($rel[0]['requested']) { $label = " Follow Requested"; $class = "unfollow"; } else { $label = " Request Follow"; $class = "follow"; } } else { $label = " Follow"; $class = "follow"; } } $buttons .= "
$label
"; break; } $n .= "
$type $user " . trim($preview) . " $buttons
$media
"; } return $n; } } /* function to parse opengraph from a html source */ /* taken from https://ajaxhispano.com/ask/como-obtener-el-protocolo-open-graph-de-una-pagina-web-por-php-109483/ */ function getOgTags($html) { $pattern='/<\s*meta\s+property="og:([^"]+)"\s+content="([^"]*)/i'; if(preg_match_all($pattern, $html, $out)) return array_combine($out[1], $out[2]); return array(); } /* this function takes in a whole post entity and spits out the HTMLfied text of the post with the urls and @handles linkified and all the emojis replaced */ function processText($elem) { global $user_settings; global $logedin; require_once "vendor/simple_html_dom.php"; $content = trim(html_entity_decode($elem['content'],ENT_QUOTES)); $content = preg_replace("/<(?=[^>]*(?:<|$))/","<",$content); if (!empty($content)) { $html = str_get_html($content); foreach ($html->find('a') as $lnk) { //remove text links to media attachments foreach ($elem['media_attachments'] as $f) { if (is_numeric(strpos($f['description'],explode("…",$lnk->innertext)[0]))) { $content = str_replace($lnk->outertext . "
", null, $content); $content = str_replace("
" . $lnk->outertext, null, $content); $content = str_replace($lnk->outertext, null, $content); } } //modify links for hashtags and external urls if (is_numeric(strpos($lnk->href, $user_settings['instance'])) || in_array($lnk->class, array( "u-url mention", "hashtag" )) || $lnk->rel == "tag") { $content = str_replace($lnk->outertext, $lnk->innertext, $content); } else { $prv = $lnk->outertext; $lnk->target = '_blank'; $lnk->class = 'link external'; $content = str_replace($prv, $lnk->outertext, $content); } } } $result = strip_tags($content, '

'); $result = str_replace('
', '
', $result); foreach ($elem['mentions'] as $mention) { if(contains($result,"@".$mention['username'])) { $result = str_replace("@" . $mention['username'], "
@" . $mention['username'] . "", $result); } else { $result = "@" . $mention['username'] . " ".$result; } } $result = emojify($result, $elem['emojis']); /* We convert hashtags to clickable links. The regex detects only strings that are not part of an url. If the user is not logged in, it just shows the hashtag in bold. Regex expression got it from here https://stackoverflow.com/questions/39414007/php-find-all-hashtags-but-no-in-link */ if ($logedin){ $result = preg_replace("!(?:f|ht)tps?://[-a-zA-Zа-яА-Я()0-9@:%_+.~#?&;/=]+(*SKIP)(*F)|#(\w+)!", "#$1", $result); } else { $result = preg_replace("!(?:f|ht)tps?://[-a-zA-Zа-яА-Я()0-9@:%_+.~#?&;/=]+(*SKIP)(*F)|#(\w+)!", "#$1", $result); } return $result; } // OTHER FUNCTIONS /* the purpose of this function is to encode the auth token it is not used for now */ function msc($string, $action = 'e') { // you may change these values to your own $secret_key = 'yAmfVhZwm0749FSY24dC'; $secret_iv = 'm37uvAeKjYLKdI1lPkcJ'; $output = false; $encrypt_method = "AES-256-CBC"; $key = hash('sha256', $secret_key); $iv = substr(hash('sha256', $secret_iv) , 0, 16); if ($action == 'e') { $output = base64_encode(openssl_encrypt($string, $encrypt_method, $key, 0, $iv)); } else if ($action == 'd') { $output = openssl_decrypt(base64_decode($string) , $encrypt_method, $key, 0, $iv); } return $output; } /* this function extracts the urls from a text and return them in an array */ function get_urls($input) { $pattern = '$(https?://[a-z0-9_./?=&-~]+)(?![^<>]*>)$i'; if (preg_match_all($pattern, $input, $matches)) { list($dummy, $links) = ($matches); return $links; } return false; } /* general function to check if one strings starts with a given search */ function starts_with($string,$search){ if (substr(strtolower($string),0,strlen($search)) == strtolower($search)){ return true; } return false; } /* general function to check if one strings contains with a given search */ function contains($string,$search){ if (is_numeric(strpos(strtolower($string),strtolower($search)))){ return true; } return false; } /* same as avobe but check against all elements of an array */ function contains_any($where, $array) { $n = 1; foreach ($array as $elem) { if (is_numeric(strpos($where, $elem))) { return $n; } $n++; } return false; } /* this function just reduces an image to a 1x1 pixel image to get the overall color. */ function averageColor($url) { @$image = imagecreatefromstring(file_get_contents($url)); if (!$image) { $mainColor = "CCCCCC"; } else { $thumb = imagecreatetruecolor(1, 1); imagecopyresampled($thumb, $image, 0, 0, 0, 0, 1, 1, imagesx($image) , imagesy($image)); $mainColor = strtoupper(dechex(imagecolorat($thumb, 0, 0))); } return $mainColor; } /* function used in the process of uploading a file */ function get_mime($filename) { $result = new finfo(); if (is_resource($result) === true) { return $result->file($filename, FILEINFO_MIME_TYPE); } return false; } function sanitize($text){ return preg_replace("/[^a-zA-Z0-9.]+/", "", $text); } function themes($mode,$name = false){ global $user_settings; switch ($mode){ case "list": $themes = scandir("themes/"); $themelist = array(); foreach ($themes as $elem){ if ($elem != ".." && $elem != "." && $elem != "custom" && is_dir("themes/".$elem)){ $themelist[] = $elem; } } return $themelist; case "file": $theme = sanitize($user_settings['theme']); if (file_exists("themes/$theme/$name")){ return "themes/$theme/$name"; } else { return "$name"; } case "get": $theme = sanitize($user_settings['theme']); if (file_exists("themes/$theme/$name")){ return file_get_contents("themes/$theme/$name"); } else { return file_get_contents("$name"); } } } function time_elapsed_string($datetime, $full = false) { $now = new DateTime; $ago = new DateTime($datetime); $diff = $now->diff($ago); $diff->w = floor($diff->d / 7); $diff->d -= $diff->w * 7; $string = array( 'y' => 'year', 'm' => 'month', 'w' => 'week', 'd' => 'day', 'h' => 'hour', 'i' => 'minute', 's' => 'second', ); foreach ($string as $k => &$v) { if ($diff->$k) { $v = $diff->$k . ' ' . $v . ($diff->$k > 1 ? 's' : ''); } else { unset($string[$k]); } } if (!$full) $string = array_slice($string, 0, 1); return $string ? implode(', ', $string) . ' ago' : 'just now'; } function getHeaders($respHeaders) { $headers = array(); $headerText = substr($respHeaders, 0, strpos($respHeaders, "\r\n\r\n")); foreach (explode("\r\n", $headerText) as $i => $line) { if ($i === 0) { $headers['http_code'] = $line; } else { list ($key, $value) = explode(': ', $line); $headers[$key] = $value; } } return $headerText; }