]> git.mxchange.org Git - friendica.git/blobdiff - include/conversation.php
Last minute fix to include/items
[friendica.git] / include / conversation.php
index a8ac83c1a2ca705365e0a816b86edef95091dc28..958d507d78d979223c71c9c773ebbf73c15d3154 100644 (file)
@@ -1,16 +1,20 @@
 <?php
-
+/**
+ * @file include/conversation.php
+ */
 use Friendica\App;
+use Friendica\Content\Feature;
+use Friendica\Core\Config;
+use Friendica\Core\PConfig;
+use Friendica\Core\System;
+use Friendica\Database\DBM;
+use Friendica\Model\Contact;
+use Friendica\Object\Thread;
+use Friendica\Object\Post;
 
 require_once "include/bbcode.php";
 require_once "include/acl_selectors.php";
 
-/*
- * Note: the code in 'item_extract_images' and 'item_redir_and_replace_images'
- * is identical to the code in mod/message.php for 'item_extract_images' and
- * 'item_redir_and_replace_images'
- */
-if (! function_exists('item_extract_images')) {
 function item_extract_images($body) {
 
        $saved_image = array();
@@ -52,9 +56,8 @@ function item_extract_images($body) {
        $new_body = $new_body . $orig_body;
 
        return array('body' => $new_body, 'images' => $saved_image);
-}}
+}
 
-if (! function_exists('item_redir_and_replace_images')) {
 function item_redir_and_replace_images($body, $images, $cid) {
 
        $origbody = $body;
@@ -65,7 +68,7 @@ function item_redir_and_replace_images($body, $images, $cid) {
        while ($pos !== false && $cnt < 1000) {
 
                $search = '/\[url\=(.*?)\]\[!#saved_image([0-9]*)#!\]\[\/url\]' . '/is';
-               $replace = '[url=' . z_path() . '/redir/' . $cid
+               $replace = '[url=' . System::baseUrl() . '/redir/' . $cid
                                   . '?f=1&url=' . '$1' . '][!#saved_image' . '$2' .'#!][/url]';
 
                $newbody .= substr($origbody, 0, $pos['start']['open']);
@@ -94,7 +97,7 @@ function item_redir_and_replace_images($body, $images, $cid) {
                $cnt++;
        }
        return $newbody;
-}}
+}
 
 /**
  * Render actions localized
@@ -119,7 +122,7 @@ function localize_item(&$item) {
                        WHERE `item`.`contact-id`=`contact`.`id`
                        AND `item`.`uri`='%s'",
                        dbesc($item['parent-uri']));
-               if (!dbm::is_result($r)) {
+               if (!DBM::is_result($r)) {
                        return;
                }
                $obj = $r[0];
@@ -248,20 +251,6 @@ function localize_item(&$item) {
                $item['body'] = sprintf($txt, $A, $B). "\n\n\n" . $Bphoto;
 
        }
-       if (stristr($item['verb'], ACTIVITY_MOOD)) {
-               $verb = urldecode(substr($item['verb'], strpos($item['verb'], '#') + 1));
-               if (! $verb) {
-                       return;
-               }
-
-               $Aname = $item['author-name'];
-               $Alink = $item['author-link'];
-               $A = '[url=' . zrl($Alink) . ']' . $Aname . '[/url]';
-
-               $txt = t('%1$s is currently %2$s');
-
-               $item['body'] = sprintf($txt, $A, t($verb));
-       }
 
        if (activity_match($item['verb'], ACTIVITY_TAG)) {
                /// @TODO may hurt performance "joining" two tables + asterisk
@@ -270,7 +259,7 @@ function localize_item(&$item) {
                        AND `item`.`uri`='%s'",
                        dbesc($item['parent-uri']));
 
-               if (!dbm::is_result($r)) {
+               if (!DBM::is_result($r)) {
                        return;
                }
 
@@ -326,7 +315,7 @@ function localize_item(&$item) {
                                        intval($item['uid'])
                        );
 
-                       if (dbm::is_result($r) && $r[0]['plink']) {
+                       if (DBM::is_result($r) && $r[0]['plink']) {
                                $target = $r[0];
                                $Bname = $target['author-name'];
                                $Blink = $target['author-link'];
@@ -358,7 +347,7 @@ function localize_item(&$item) {
        $x = stristr($item['plink'],'/display/');
        if ($x) {
                $sparkle = false;
-               $y = best_link_url($item, $sparkle, true);
+               $y = best_link_url($item, $sparkle);
 
                if (strstr($y, '/redir/')) {
                        $item['plink'] = $y . '?f=&url=' . $item['plink'];
@@ -462,7 +451,14 @@ These Fields are not added below (yet). They are here to for bug search.
                `author`.`thumb` AS `author-thumb`, `owner`.`thumb` AS `owner-thumb`,
 
                `contact`.`network`, `contact`.`url`, `contact`.`name`, `contact`.`writable`,
-               `contact`.`self`, `contact`.`id` AS `cid`, `contact`.`alias`";
+               `contact`.`self`, `contact`.`id` AS `cid`, `contact`.`alias`,
+
+               `event`.`created` AS `event-created`, `event`.`edited` AS `event-edited`,
+               `event`.`start` AS `event-start`,`event`.`finish` AS `event-finish`,
+               `event`.`summary` AS `event-summary`,`event`.`desc` AS `event-desc`,
+               `event`.`location` AS `event-location`, `event`.`type` AS `event-type`,
+               `event`.`nofinish` AS `event-nofinish`,`event`.`adjust` AS `event-adjust`,
+               `event`.`ignore` AS `event-ignore`, `event`.`id` AS `event-id`";
 }
 
 /**
@@ -472,7 +468,8 @@ function item_joins() {
        return "STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND
                (NOT `contact`.`blocked` OR `contact`.`pending`)
                LEFT JOIN `contact` AS `author` ON `author`.`id`=`item`.`author-id`
-               LEFT JOIN `contact` AS `owner` ON `owner`.`id`=`item`.`owner-id`";
+               LEFT JOIN `contact` AS `owner` ON `owner`.`id`=`item`.`owner-id`
+               LEFT JOIN `event` ON `event-id` = `event`.`id`";
 }
 
 /**
@@ -482,7 +479,6 @@ function item_condition() {
        return "`item`.`visible` AND NOT `item`.`deleted` AND NOT `item`.`moderated`";
 }
 
-if (!function_exists('conversation')) {
 /**
  * "Render" a conversation or list of items for HTML display.
  * There are two major forms of display:
@@ -496,19 +492,17 @@ if (!function_exists('conversation')) {
 function conversation(App $a, $items, $mode, $update, $preview = false) {
 
        require_once 'include/bbcode.php';
-       require_once 'include/Contact.php';
        require_once 'mod/proxy.php';
 
        $ssl_state = ((local_user()) ? true : false);
 
        $profile_owner = 0;
-       $page_writeable = false;
        $live_update_div = '';
 
        $arr_blocked = null;
 
        if (local_user()) {
-               $str_blocked = get_pconfig(local_user(), 'system', 'blocked');
+               $str_blocked = PConfig::get(local_user(), 'system', 'blocked');
                if ($str_blocked) {
                        $arr_blocked = explode(',', $str_blocked);
                        for ($x = 0; $x < count($arr_blocked); $x ++) {
@@ -522,7 +516,6 @@ function conversation(App $a, $items, $mode, $update, $preview = false) {
 
        if ($mode === 'network') {
                $profile_owner = local_user();
-               $page_writeable = true;
                if (!$update) {
                        /*
                         * The special div is needed for liveUpdate to kick in for this page.
@@ -550,11 +543,12 @@ function conversation(App $a, $items, $mode, $update, $preview = false) {
                }
        } elseif ($mode === 'profile') {
                $profile_owner = $a->profile['profile_uid'];
-               $page_writeable = can_write_wall($a,$profile_owner);
 
                if (!$update) {
-                       $tab = notags(trim($_GET['tab']));
-                       $tab = ( $tab ? $tab : 'posts' );
+                       $tab = 'posts';
+                       if (x($_GET, 'tab')) {
+                               $tab = notags(trim($_GET['tab']));
+                       }
                        if ($tab === 'posts') {
                                /*
                                 * This is ugly, but we can't pass the profile_uid through the session to the ajax updater,
@@ -568,7 +562,6 @@ function conversation(App $a, $items, $mode, $update, $preview = false) {
                }
        } elseif ($mode === 'notes') {
                $profile_owner = local_user();
-               $page_writeable = true;
                if (!$update) {
                        $live_update_div = '<div id="live-notes"></div>' . "\r\n"
                                . "<script> var profile_uid = " . local_user()
@@ -576,7 +569,6 @@ function conversation(App $a, $items, $mode, $update, $preview = false) {
                }
        } elseif ($mode === 'display') {
                $profile_owner = $a->profile['uid'];
-               $page_writeable = can_write_wall($a,$profile_owner);
                if (!$update) {
                        $live_update_div = '<div id="live-display"></div>' . "\r\n"
                                . "<script> var profile_uid = " . $_SESSION['uid'] . ";"
@@ -584,7 +576,6 @@ function conversation(App $a, $items, $mode, $update, $preview = false) {
                }
        } elseif ($mode === 'community') {
                $profile_owner = 0;
-               $page_writeable = false;
                if (!$update) {
                        $live_update_div = '<div id="live-community"></div>' . "\r\n"
                                . "<script> var profile_uid = -1; var netargs = '/?f='; var profile_page = " . $a->pager['page'] . "; </script>\r\n";
@@ -622,6 +613,12 @@ function conversation(App $a, $items, $mode, $update, $preview = false) {
        $page_template = get_markup_template("conversation.tpl");
 
        if ($items && count($items)) {
+               // Currently behind a config value. This allows the commenting and sharing of every public item.
+               if (Config::get('system', 'comment_public') && local_user()) {
+                       $writable = ($items[0]['uid'] == 0) && in_array($items[0]['network'], array(NETWORK_OSTATUS, NETWORK_DIASPORA));
+               } else {
+                       $writable = false;
+               }
 
                if ($mode === 'network-new' || $mode === 'search' || $mode === 'community') {
 
@@ -652,20 +649,10 @@ function conversation(App $a, $items, $mode, $update, $preview = false) {
 
                                $threadsid++;
 
-                               $comment     = '';
                                $owner_url   = '';
                                $owner_name  = '';
                                $sparkle     = '';
 
-                               if ($mode === 'search' || $mode === 'community') {
-                                       if (((activity_match($item['verb'], ACTIVITY_LIKE)) || (activity_match($item['verb'], ACTIVITY_DISLIKE)))
-                                               && ($item['id'] != $item['parent']))
-                                               continue;
-                                       $nickname = $item['nickname'];
-                               } else {
-                                       $nickname = $a->user['nickname'];
-                               }
-
                                // prevent private email from leaking.
                                if ($item['network'] === NETWORK_MAIL && local_user() != $item['uid']) {
                                        continue;
@@ -680,15 +667,19 @@ function conversation(App $a, $items, $mode, $update, $preview = false) {
                                $hashtags = array();
                                $mentions = array();
 
-                               $taglist = q("SELECT `type`, `term`, `url` FROM `term` WHERE `otype` = %d AND `oid` = %d AND `type` IN (%d, %d) ORDER BY `tid`",
-                                               intval(TERM_OBJ_POST), intval($item['id']), intval(TERM_HASHTAG), intval(TERM_MENTION));
+                               $searchpath = System::baseUrl()."/search?tag=";
 
-                               foreach ($taglist as $tag) {
+                               $taglist = dba::select('term', array('type', 'term', 'url'),
+                                                       array("`otype` = ? AND `oid` = ? AND `type` IN (?, ?)", TERM_OBJ_POST, $item['id'], TERM_HASHTAG, TERM_MENTION),
+                                                       array('order' => array('tid')));
 
+                               while ($tag = dba::fetch($taglist)) {
                                        if ($tag["url"] == "") {
                                                $tag["url"] = $searchpath . strtolower($tag["term"]);
                                        }
 
+                                       $tag["url"] = best_link_url($item, $sp, $tag["url"]);
+
                                        if ($tag["type"] == TERM_HASHTAG) {
                                                $hashtags[] = "#<a href=\"" . $tag["url"] . "\" target=\"_blank\">" . $tag["term"] . "</a>";
                                                $prefix = "#";
@@ -698,9 +689,10 @@ function conversation(App $a, $items, $mode, $update, $preview = false) {
                                        }
                                        $tags[] = $prefix."<a href=\"" . $tag["url"] . "\" target=\"_blank\">" . $tag["term"] . "</a>";
                                }
+                               dba::close($taglist);
 
                                $sp = false;
-                               $profile_link = best_link_url($item,$sp);
+                               $profile_link = best_link_url($item, $sp);
                                if ($profile_link === 'mailbox') {
                                        $profile_link = '';
                                }
@@ -712,7 +704,7 @@ function conversation(App $a, $items, $mode, $update, $preview = false) {
                                }
 
                                if (!x($item, 'author-thumb') || ($item['author-thumb'] == "")) {
-                                       $author_contact = get_contact_details_by_url($item['author-link'], $profile_owner);
+                                       $author_contact = Contact::getDetailsByURL($item['author-link'], $profile_owner);
                                        if ($author_contact["thumb"]) {
                                                $item['author-thumb'] = $author_contact["thumb"];
                                        } else {
@@ -721,7 +713,7 @@ function conversation(App $a, $items, $mode, $update, $preview = false) {
                                }
 
                                if (!isset($item['owner-thumb']) || ($item['owner-thumb'] == "")) {
-                                       $owner_contact = get_contact_details_by_url($item['owner-link'], $profile_owner);
+                                       $owner_contact = Contact::getDetailsByURL($item['owner-link'], $profile_owner);
                                        if ($owner_contact["thumb"]) {
                                                $item['owner-thumb'] = $owner_contact["thumb"];
                                        } else {
@@ -759,25 +751,14 @@ function conversation(App $a, $items, $mode, $update, $preview = false) {
 
                                list($categories, $folders) = get_cats_and_terms($item);
 
-                               if ($a->theme['template_engine'] === 'internal') {
-                                       $profile_name_e = template_escape($profile_name);
-                                       $item['title_e'] = template_escape($item['title']);
-                                       $body_e = template_escape($body);
-                                       $tags_e = template_escape($tags);
-                                       $hashtags_e = template_escape($hashtags);
-                                       $mentions_e = template_escape($mentions);
-                                       $location_e = template_escape($location);
-                                       $owner_name_e = template_escape($owner_name);
-                               } else {
-                                       $profile_name_e = $profile_name;
-                                       $item['title_e'] = $item['title'];
-                                       $body_e = $body;
-                                       $tags_e = $tags;
-                                       $hashtags_e = $hashtags;
-                                       $mentions_e = $mentions;
-                                       $location_e = $location;
-                                       $owner_name_e = $owner_name;
-                               }
+                               $profile_name_e = $profile_name;
+                               $item['title_e'] = $item['title'];
+                               $body_e = $body;
+                               $tags_e = $tags;
+                               $hashtags_e = $hashtags;
+                               $mentions_e = $mentions;
+                               $location_e = $location;
+                               $owner_name_e = $owner_name;
 
                                if ($item['item_network'] == "") {
                                        $item['item_network'] = $item['network'];
@@ -786,6 +767,7 @@ function conversation(App $a, $items, $mode, $update, $preview = false) {
                                $tmp_item = array(
                                        'template' => $tpl,
                                        'id' => (($preview) ? 'P0' : $item['item_id']),
+                                       'guid' => (($preview) ? 'Q0' : $item['guid']),
                                        'network' => $item['item_network'],
                                        'network_name' => network_to_name($item['item_network'], $profile_link),
                                        'linktitle' => sprintf( t('View %s\'s profile @ %s'), $profile_name, ((strlen($item['author-link'])) ? $item['author-link'] : $item['url'])),
@@ -794,7 +776,7 @@ function conversation(App $a, $items, $mode, $update, $preview = false) {
                                        'name' => $profile_name_e,
                                        'sparkle' => $sparkle,
                                        'lock' => $lock,
-                                       'thumb' => App::remove_baseurl(proxy_url($item['author-thumb'], false, PROXY_SIZE_THUMB)),
+                                       'thumb' => System::removedBaseUrl(proxy_url($item['author-thumb'], false, PROXY_SIZE_THUMB)),
                                        'title' => $item['title_e'],
                                        'body' => $body_e,
                                        'tags' => $tags_e,
@@ -813,7 +795,7 @@ function conversation(App $a, $items, $mode, $update, $preview = false) {
                                        'indent' => '',
                                        'owner_name' => $owner_name_e,
                                        'owner_url' => $owner_url,
-                                       'owner_photo' => App::remove_baseurl(proxy_url($item['owner-thumb'], false, PROXY_SIZE_THUMB)),
+                                       'owner_photo' => System::removedBaseUrl(proxy_url($item['owner-thumb'], false, PROXY_SIZE_THUMB)),
                                        'plink' => get_plink($item),
                                        'edpost' => false,
                                        'isstarred' => $isstarred,
@@ -823,7 +805,6 @@ function conversation(App $a, $items, $mode, $update, $preview = false) {
                                        'like' => '',
                                        'dislike' => '',
                                        'comment' => '',
-                                       //'conv' => (($preview) ? '' : array('href'=> 'display/' . $nickname . '/' . $item['id'], 'title'=> t('View in context'))),
                                        'conv' => (($preview) ? '' : array('href'=> 'display/'.$item['guid'], 'title'=> t('View in context'))),
                                        'previewing' => $previewing,
                                        'wait' => t('Please wait'),
@@ -842,10 +823,7 @@ function conversation(App $a, $items, $mode, $update, $preview = false) {
                        // Normal View
                        $page_template = get_markup_template("threaded_conversation.tpl");
 
-                       require_once 'object/Conversation.php';
-                       require_once 'object/Item.php';
-
-                       $conv = new Conversation($mode, $preview);
+                       $conv = new Thread($mode, $preview, $writable);
 
                        /*
                         * get all the topmost parents
@@ -885,12 +863,12 @@ function conversation(App $a, $items, $mode, $update, $preview = false) {
                                $item['pagedrop'] = $page_dropping;
 
                                if ($item['id'] == $item['parent']) {
-                                       $item_object = new Item($item);
-                                       $conv->add_thread($item_object);
+                                       $item_object = new Post($item);
+                                       $conv->addParent($item_object);
                                }
                        }
 
-                       $threads = $conv->get_template_data($conv_responses);
+                       $threads = $conv->getTemplateData($conv_responses);
 
                        if (!$threads) {
                                logger('[ERROR] conversation : Failed to get template data.', LOGGER_DEBUG);
@@ -900,20 +878,20 @@ function conversation(App $a, $items, $mode, $update, $preview = false) {
        }
 
        $o = replace_macros($page_template, array(
-               '$baseurl' => App::get_baseurl($ssl_state),
+               '$baseurl' => System::baseUrl($ssl_state),
                '$return_path' => $a->query_string,
                '$live_update' => $live_update_div,
                '$remove' => t('remove'),
                '$mode' => $mode,
                '$user' => $a->user,
                '$threads' => $threads,
-               '$dropping' => ($page_dropping && feature_enabled(local_user(), 'multi_delete') ? t('Delete Selected Items') : False),
+               '$dropping' => ($page_dropping && Feature::isEnabled(local_user(), 'multi_delete') ? t('Delete Selected Items') : False),
        ));
 
        return $o;
-}}
+}
 
-function best_link_url($item, &$sparkle, $ssl_state = false) {
+function best_link_url($item, &$sparkle, $url = '') {
 
        $best_url = '';
        $sparkle  = false;
@@ -924,13 +902,23 @@ function best_link_url($item, &$sparkle, $ssl_state = false) {
                $r = dba::select('contact', array('id'),
                        array('network' => NETWORK_DFRN, 'uid' => local_user(), 'nurl' => normalise_link($clean_url), 'pending' => false),
                        array('limit' => 1));
-               if (dbm::is_result($r)) {
+               if (DBM::is_result($r)) {
                        $best_url = 'redir/' . $r['id'];
                        $sparkle = true;
+                       if ($url != '') {
+                               $hostname = get_app()->get_hostname();
+                               if (!strstr($url, $hostname)) {
+                                       $best_url .= "?url=".$url;
+                               } else {
+                                       $best_url = $url;
+                               }
+                       }
                }
        }
        if (! $best_url) {
-               if (strlen($item['author-link'])) {
+               if ($url != '') {
+                       $best_url = $url;
+               } elseif (strlen($item['author-link'])) {
                        $best_url = $item['author-link'];
                } else {
                        $best_url = $item['url'];
@@ -942,12 +930,6 @@ function best_link_url($item, &$sparkle, $ssl_state = false) {
 
 
 function item_photo_menu($item) {
-       $ssl_state = false;
-
-       if (local_user()) {
-               $ssl_state = true;
-       }
-
        $sub_link = '';
        $poke_link = '';
        $contact_url = '';
@@ -962,7 +944,7 @@ function item_photo_menu($item) {
        }
 
        $sparkle = false;
-       $profile_link = best_link_url($item, $sparkle, $ssl_state);
+       $profile_link = best_link_url($item, $sparkle);
        if ($profile_link === 'mailbox') {
                $profile_link = '';
        }
@@ -971,7 +953,7 @@ function item_photo_menu($item) {
        $network = '';
        $rel = 0;
        $r = dba::select('contact', array('id', 'network', 'rel'), array('uid' => local_user(), 'nurl' => normalise_link($item['author-link'])), array('limit' => 1));
-       if (dbm::is_result($r)) {
+       if (DBM::is_result($r)) {
                $cid = $r['id'];
                $network = $r['network'];
                $rel = $r['rel'];
@@ -1037,7 +1019,6 @@ function item_photo_menu($item) {
        return $o;
 }
 
-if (! function_exists('builtin_activity_puller')) {
 /**
  * @brief Checks item to see if it is one of the builtin activities (like/dislike, event attendance, consensus items, etc.)
  * Increments the count of each matching activity and adds a link to the author as needed.
@@ -1113,9 +1094,8 @@ function builtin_activity_puller($item, &$conv_responses) {
                        return;
                }
        }
-}}
+}
 
-if (! function_exists('format_like')) {
 /**
  * Format the vote text for a profile item
  * @param int $cnt = number of people who vote the item
@@ -1206,41 +1186,42 @@ function format_like($cnt, array $arr, $type, $id) {
        $o .= $expanded;
 
        return $o;
-}}
+}
 
-function status_editor(App $a, $x, $notes_cid = 0, $popup = false) {
+function status_editor(App $a, $x, $notes_cid = 0, $popup = false)
+{
        $o = '';
 
-       $geotag = (x($x, 'allow_location') ? replace_macros(get_markup_template('jot_geotag.tpl'), array()) : '');
+       $geotag = x($x, 'allow_location') ? replace_macros(get_markup_template('jot_geotag.tpl'), array()) : '';
 
        $tpl = get_markup_template('jot-header.tpl');
        $a->page['htmlhead'] .= replace_macros($tpl, array(
-               '$newpost' => 'true',
-               '$baseurl' => App::get_baseurl(true),
-               '$geotag' => $geotag,
-               '$nickname' => $x['nickname'],
-               '$ispublic' => t('Visible to <strong>everybody</strong>'),
-               '$linkurl' => t('Please enter a link URL:'),
-               '$vidurl' => t("Please enter a video link/URL:"),
-               '$audurl' => t("Please enter an audio link/URL:"),
-               '$term' => t('Tag term:'),
-               '$fileas' => t('Save to Folder:'),
+               '$newpost'   => 'true',
+               '$baseurl'   => System::baseUrl(true),
+               '$geotag'    => $geotag,
+               '$nickname'  => $x['nickname'],
+               '$ispublic'  => t('Visible to <strong>everybody</strong>'),
+               '$linkurl'   => t('Please enter a link URL:'),
+               '$vidurl'    => t("Please enter a video link/URL:"),
+               '$audurl'    => t("Please enter an audio link/URL:"),
+               '$term'      => t('Tag term:'),
+               '$fileas'    => t('Save to Folder:'),
                '$whereareu' => t('Where are you right now?'),
-               '$delitems' => t('Delete item(s)?')
+               '$delitems'  => t('Delete item(s)?')
        ));
 
        $tpl = get_markup_template('jot-end.tpl');
        $a->page['end'] .= replace_macros($tpl, array(
-               '$newpost' => 'true',
-               '$baseurl' => App::get_baseurl(true),
-               '$geotag' => $geotag,
-               '$nickname' => $x['nickname'],
-               '$ispublic' => t('Visible to <strong>everybody</strong>'),
-               '$linkurl' => t('Please enter a link URL:'),
-               '$vidurl' => t("Please enter a video link/URL:"),
-               '$audurl' => t("Please enter an audio link/URL:"),
-               '$term' => t('Tag term:'),
-               '$fileas' => t('Save to Folder:'),
+               '$newpost'   => 'true',
+               '$baseurl'   => System::baseUrl(true),
+               '$geotag'    => $geotag,
+               '$nickname'  => $x['nickname'],
+               '$ispublic'  => t('Visible to <strong>everybody</strong>'),
+               '$linkurl'   => t('Please enter a link URL:'),
+               '$vidurl'    => t("Please enter a video link/URL:"),
+               '$audurl'    => t("Please enter an audio link/URL:"),
+               '$term'      => t('Tag term:'),
+               '$fileas'    => t('Save to Folder:'),
                '$whereareu' => t('Where are you right now?')
        ));
 
@@ -1273,57 +1254,56 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false) {
        $tpl = get_markup_template("jot.tpl");
 
        $o .= replace_macros($tpl,array(
-               '$return_path' => $query_str,
-               '$action' =>  'item',
-               '$share' => (x($x,'button') ? $x['button'] : t('Share')),
-               '$upload' => t('Upload photo'),
-               '$shortupload' => t('upload photo'),
-               '$attach' => t('Attach file'),
-               '$shortattach' => t('attach file'),
-               '$weblink' => t('Insert web link'),
+               '$return_path'  => $query_str,
+               '$action'       => 'item',
+               '$share'        => defaults($x, 'button', t('Share')),
+               '$upload'       => t('Upload photo'),
+               '$shortupload'  => t('upload photo'),
+               '$attach'       => t('Attach file'),
+               '$shortattach'  => t('attach file'),
+               '$weblink'      => t('Insert web link'),
                '$shortweblink' => t('web link'),
-               '$video' => t('Insert video link'),
-               '$shortvideo' => t('video link'),
-               '$audio' => t('Insert audio link'),
-               '$shortaudio' => t('audio link'),
-               '$setloc' => t('Set your location'),
-               '$shortsetloc' => t('set location'),
-               '$noloc' => t('Clear browser location'),
-               '$shortnoloc' => t('clear location'),
-               '$title' => $x['title'],
+               '$video'        => t('Insert video link'),
+               '$shortvideo'   => t('video link'),
+               '$audio'        => t('Insert audio link'),
+               '$shortaudio'   => t('audio link'),
+               '$setloc'       => t('Set your location'),
+               '$shortsetloc'  => t('set location'),
+               '$noloc'        => t('Clear browser location'),
+               '$shortnoloc'   => t('clear location'),
+               '$title'        => defaults($x, 'title', ''),
                '$placeholdertitle' => t('Set title'),
-               '$category' => $x['category'],
-               '$placeholdercategory' => (feature_enabled(local_user(), 'categories') ? t('Categories (comma-separated list)') : ''),
-               '$wait' => t('Please wait'),
-               '$permset' => t('Permission settings'),
+               '$category'     => defaults($x, 'category', ''),
+               '$placeholdercategory' => Feature::isEnabled(local_user(), 'categories') ? t('Categories (comma-separated list)') : '',
+               '$wait'         => t('Please wait'),
+               '$permset'      => t('Permission settings'),
                '$shortpermset' => t('permissions'),
-               '$ptyp' => (($notes_cid) ? 'note' : 'wall'),
-               '$content' => $x['content'],
-               '$post_id' => $x['post_id'],
-               '$baseurl' => App::get_baseurl(true),
-               '$defloc' => $x['default_location'],
-               '$visitor' => $x['visitor'],
-               '$pvisit' => (($notes_cid) ? 'none' : $x['visitor']),
-               '$public' => t('Public post'),
-               '$jotnets' => $jotnets,
-               '$lockstate' => $x['lockstate'],
-               '$bang' => $x['bang'],
-               '$profile_uid' => $x['profile_uid'],
-               '$preview' => ((feature_enabled($x['profile_uid'],'preview')) ? t('Preview') : ''),
-               '$jotplugins' => $jotplugins,
-               '$notes_cid' => $notes_cid,
-               '$sourceapp' => t($a->sourcename),
-               '$cancel' => t('Cancel'),
-               '$rand_num' => random_digits(12),
+               '$ptyp'         => $notes_cid ? 'note' : 'wall',
+               '$content'      => defaults($x, 'content', ''),
+               '$post_id'      => defaults($x, 'post_id', ''),
+               '$baseurl'      => System::baseUrl(true),
+               '$defloc'       => $x['default_location'],
+               '$visitor'      => $x['visitor'],
+               '$pvisit'       => $notes_cid ? 'none' : $x['visitor'],
+               '$public'       => t('Public post'),
+               '$lockstate'    => $x['lockstate'],
+               '$bang'         => $x['bang'],
+               '$profile_uid'  => $x['profile_uid'],
+               '$preview'      => Feature::isEnabled($x['profile_uid'], 'preview') ? t('Preview') : '',
+               '$jotplugins'   => $jotplugins,
+               '$notes_cid'    => $notes_cid,
+               '$sourceapp'    => t($a->sourcename),
+               '$cancel'       => t('Cancel'),
+               '$rand_num'     => random_digits(12),
 
                // ACL permissions box
-               '$acl' => $x['acl'],
-               '$acl_data' => $x['acl_data'],
-               '$group_perms' => t('Post to Groups'),
+               '$acl'           => $x['acl'],
+               '$acl_data'      => $x['acl_data'],
+               '$group_perms'   => t('Post to Groups'),
                '$contact_perms' => t('Post to Contacts'),
-               '$private' => t('Private post'),
-               '$is_private' => $private_post,
-               '$public_link' => $public_post_link,
+               '$private'       => t('Private post'),
+               '$is_private'    => $private_post,
+               '$public_link'   => $public_post_link,
 
                //jot nav tab (used in some themes)
                '$message' => t('Message'),
@@ -1332,19 +1312,28 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false) {
 
 
        if ($popup == true) {
-               $o = '<div id="jot-popup" style="display: none;">'.$o.'</div>';
+               $o = '<div id="jot-popup" style="display: none;">' . $o . '</div>';
        }
 
        return $o;
 }
 
-
-function get_item_children($arr, $parent) {
-       $children = array();
-       $a = get_app();
-       foreach ($arr as $item) {
+/**
+ * Plucks the children of the given parent from a given item list.
+ *
+ * @brief Plucks all the children in the given item list of the given parent
+ *
+ * @param array $item_list
+ * @param array $parent
+ * @param bool $recursive
+ * @return type
+ */
+function get_item_children(array &$item_list, array $parent, $recursive = true)
+{
+       $children = [];
+       foreach ($item_list as $i => $item) {
                if ($item['id'] != $item['parent']) {
-                       if (get_config('system', 'thread_allow') && $a->theme_thread_allow) {
+                       if ($recursive) {
                                // Fallback to parent-uri if thr-parent is not set
                                $thr_parent = $item['thr-parent'];
                                if ($thr_parent == '') {
@@ -1352,65 +1341,141 @@ function get_item_children($arr, $parent) {
                                }
 
                                if ($thr_parent == $parent['uri']) {
-                                       $item['children'] = get_item_children($arr, $item);
+                                       $item['children'] = get_item_children($item_list, $item);
                                        $children[] = $item;
+                                       unset($item_list[$i]);
                                }
                        } elseif ($item['parent'] == $parent['id']) {
                                $children[] = $item;
+                               unset($item_list[$i]);
                        }
                }
        }
        return $children;
 }
 
-/// @TODO Add type-hint
-function sort_item_children($items) {
+/**
+ * @brief Recursively sorts a tree-like item array
+ *
+ * @param array $items
+ * @return array
+ */
+function sort_item_children(array $items)
+{
        $result = $items;
        usort($result, 'sort_thr_created_rev');
        foreach ($result as $k => $i) {
-               if (count($result[$k]['children'])) {
+               if (isset($result[$k]['children'])) {
                        $result[$k]['children'] = sort_item_children($result[$k]['children']);
                }
        }
        return $result;
 }
 
-/// @TODO Add type-hint
-function add_children_to_list($children, &$arr) {
-       foreach ($children as $y) {
-               $arr[] = $y;
-               if (count($y['children'])) {
-                       add_children_to_list($y['children'], $arr);
+/**
+ * @brief Recursively add all children items at the top level of a list
+ *
+ * @param array $children List of items to append
+ * @param array $item_list
+ */
+function add_children_to_list(array $children, array &$item_list)
+{
+       foreach ($children as $child) {
+               $item_list[] = $child;
+               if (isset($child['children'])) {
+                       add_children_to_list($child['children'], $item_list);
                }
        }
 }
 
-/// @TODO Add type-hint
-function conv_sort($arr, $order) {
-
-       if ((!(is_array($arr) && count($arr)))) {
-               return array();
+/**
+ * This recursive function takes the item tree structure created by conv_sort() and
+ * flatten the extraneous depth levels when people reply sequentially, removing the
+ * stairs effect in threaded conversations limiting the available content width.
+ *
+ * The basic principle is the following: if a post item has only one reply and is
+ * the last reply of its parent, then the reply is moved to the parent.
+ *
+ * This process is rendered somewhat more complicated because items can be either
+ * replies or likes, and these don't factor at all in the reply count/last reply.
+ *
+ * @brief Selectively flattens a tree-like item structure to prevent threading stairs
+ *
+ * @param array $parent A tree-like array of items
+ * @return array
+ */
+function smart_flatten_conversation(array $parent)
+{
+       if (! isset($parent['children']) || count($parent['children']) == 0) {
+               return $parent;
        }
 
-       $parents = array();
-       $children = array();
-       $newarr = array();
+       // We use a for loop to ensure we process the newly-moved items
+       for ($i = 0; $i < count($parent['children']); $i++) {
+               $child = $parent['children'][$i];
 
-       /*
-        * This is a preparation for having two different items with the same uri in one thread
-        * This will otherwise lead to an endless loop.
-        */
-       foreach ($arr as $x) {
-               if (!isset($newarr[$x['uri']])) {
-                       $newarr[$x['uri']] = $x;
+               if (isset($child['children']) && count($child['children'])) {
+                       // This helps counting only the regular posts
+                       $count_post_closure = function($var) {
+                               return $var['verb'] === ACTIVITY_POST;
+                       };
+
+                       $child_post_count = count(array_filter($child['children'], $count_post_closure));
+
+                       $remaining_post_count = count(array_filter(array_slice($parent['children'], $i), $count_post_closure));
+
+                       // If there's only one child's children post and this is the last child post
+                       if ($child_post_count == 1 && $remaining_post_count == 1) {
+
+                               // Searches the post item in the children
+                               $j = 0;
+                               while($child['children'][$j]['verb'] !== ACTIVITY_POST && $j < count($child['children'])) {
+                                       $j ++;
+                               }
+
+                               $moved_item = $child['children'][$j];
+                               unset($parent['children'][$i]['children'][$j]);
+                               $parent['children'][] = $moved_item;
+                       } else {
+                               $parent['children'][$i] = smart_flatten_conversation($child);
+                       }
                }
        }
 
-       $arr = $newarr;
+       return $parent;
+}
+
+
+/**
+ * Expands a flat list of items into corresponding tree-like conversation structures,
+ * sort the top-level posts either on "created" or "commented", and finally
+ * append all the items at the top level (???)
+ *
+ * @brief Expands a flat item list into a conversation array for display
+ *
+ * @param array  $item_list A list of items belonging to one or more conversations
+ * @param string $order     Either on "created" or "commented"
+ * @return array
+ */
+function conv_sort(array $item_list, $order)
+{
+       $parents = [];
+
+       if (!(is_array($item_list) && count($item_list))) {
+               return $parents;
+       }
+
+       $item_array = [];
+
+       // Dedupes the item list on the uri to prevent infinite loops
+       foreach ($item_list as $item) {
+               $item_array[$item['uri']] = $item;
+       }
 
-       foreach ($arr as $x) {
-               if ($x['id'] == $x['parent']) {
-                       $parents[] = $x;
+       // Extract the top level items
+       foreach ($item_array as $item) {
+               if ($item['id'] == $item['parent']) {
+                       $parents[] = $item;
                }
        }
 
@@ -1420,73 +1485,73 @@ function conv_sort($arr, $order) {
                usort($parents, 'sort_thr_commented');
        }
 
-       if (count($parents)) {
-               foreach ($parents as $i => $_x) {
-                       $parents[$i]['children'] = get_item_children($arr, $_x);
-               }
+       /*
+        * Plucks children from the item_array, second pass collects eventual orphan
+        * items and add them as children of their top-level post.
+        */
+       foreach ($parents as $i => $parent) {
+               $parents[$i]['children'] =
+                       array_merge(get_item_children($item_array, $parent, true),
+                               get_item_children($item_array, $parent, false));
        }
 
-       /// @TODO Old-lost code?
-       /*foreach ($arr as $x) {
-               if ($x['id'] != $x['parent']) {
-                       $p = find_thread_parent_index($parents,$x);
-                       if ($p !== false)
-                               $parents[$p]['children'][] = $x;
-               }
-       }*/
-       if (count($parents)) {
-               foreach ($parents as $k => $v) {
-                       if (count($parents[$k]['children'])) {
-                               $parents[$k]['children'] = sort_item_children($parents[$k]['children']);
-                               /// @TODO Old-lost code?
-                               /*$y = $parents[$k]['children'];
-                               usort($y,'sort_thr_created_rev');
-                               $parents[$k]['children'] = $y;*/
-                       }
+       foreach ($parents as $i => $parent) {
+               $parents[$i]['children'] = sort_item_children($parents[$i]['children']);
+       }
+
+       if (PConfig::get(local_user(), 'system', 'smart_threading', 0)) {
+               foreach ($parents as $i => $parent) {
+                       $parents[$i] = smart_flatten_conversation($parent);
                }
        }
 
-       $ret = array();
-       if (count($parents)) {
-               foreach ($parents as $x) {
-                       $ret[] = $x;
-                       if (count($x['children'])) {
-                               add_children_to_list($x['children'], $ret);
-                               /// @TODO Old-lost code?
-                               /*foreach ($x['children'] as $y)
-                                       $ret[] = $y;*/
-                       }
+       /// @TODO: Stop recusrsively adding all children back to the top level (!!!)
+       /// However, this apparently ensures responses (likes, attendance) display (?!)
+       foreach ($parents as $parent) {
+               if (count($parent['children'])) {
+                       add_children_to_list($parent['children'], $parents);
                }
        }
 
-       return $ret;
+       return $parents;
 }
 
-/// @TODO Add type-hint
-function sort_thr_created($a, $b) {
+/**
+ * @brief usort() callback to sort item arrays by the created key
+ *
+ * @param array $a
+ * @param array $b
+ * @return int
+ */
+function sort_thr_created(array $a, array $b)
+{
        return strcmp($b['created'], $a['created']);
 }
 
-/// @TODO Add type-hint
-function sort_thr_created_rev($a, $b) {
+/**
+ * @brief usort() callback to reverse sort item arrays by the created key
+ *
+ * @param array $a
+ * @param array $b
+ * @return int
+ */
+function sort_thr_created_rev(array $a, array $b)
+{
        return strcmp($a['created'], $b['created']);
 }
 
-/// @TODO Add type-hint
-function sort_thr_commented($a, $b) {
+/**
+ * @brief usort() callback to sort item arrays by the commented key
+ *
+ * @param array $a
+ * @param array $b
+ * @return type
+ */
+function sort_thr_commented(array $a, array $b)
+{
        return strcmp($b['commented'], $a['commented']);
 }
 
-/// @TODO Add type-hint
-function find_thread_parent_index($arr, $x) {
-       foreach ($arr as $k => $v) {
-               if ($v['id'] == $x['parent']) {
-                       return $k;
-               }
-       }
-       return false;
-}
-
 /// @TODO Add type-hint
 function render_location_dummy($item) {
        if ($item['location'] != "") {
@@ -1503,13 +1568,13 @@ function get_responses($conv_responses, $response_verbs, $ob, $item) {
        $ret = array();
        foreach ($response_verbs as $v) {
                $ret[$v] = array();
-               $ret[$v]['count'] = ((x($conv_responses[$v], $item['uri'])) ? $conv_responses[$v][$item['uri']] : '');
-               $ret[$v]['list']  = ((x($conv_responses[$v], $item['uri'])) ? $conv_responses[$v][$item['uri'] . '-l'] : '');
-               $ret[$v]['self']  = ((x($conv_responses[$v], $item['uri'])) ? $conv_responses[$v][$item['uri'] . '-self'] : '0');
+               $ret[$v]['count'] = defaults($conv_responses[$v], $item['uri'], '');
+               $ret[$v]['list']  = defaults($conv_responses[$v], $item['uri'] . '-l', '');
+               $ret[$v]['self']  = defaults($conv_responses[$v], $item['uri'] . '-self', '0');
                if (count($ret[$v]['list']) > MAX_LIKERS) {
                        $ret[$v]['list_part'] = array_slice($ret[$v]['list'], 0, MAX_LIKERS);
                        array_push($ret[$v]['list_part'], '<a href="#" data-toggle="modal" data-target="#' . $v . 'Modal-'
-                               . (($ob) ? $ob->get_id() : $item['id']) . '"><b>' . t('View all') . '</b></a>');
+                               . (($ob) ? $ob->getId() : $item['id']) . '"><b>' . t('View all') . '</b></a>');
                } else {
                        $ret[$v]['list_part'] = '';
                }