]> git.mxchange.org Git - friendica.git/blobdiff - include/conversation.php
Avoid duplicated filed posts
[friendica.git] / include / conversation.php
index 57d368e927b9723ff16ca4191e26b2fe8db72837..c49aacb17780cd3ed6a2c2713110b6adad8dbe14 100644 (file)
  */
 
 use Friendica\App;
+use Friendica\BaseModule;
 use Friendica\Content\ContactSelector;
 use Friendica\Content\Feature;
-use Friendica\Content\Text\BBCode;
+use Friendica\Core\ACL;
 use Friendica\Core\Hook;
 use Friendica\Core\Logger;
 use Friendica\Core\Protocol;
@@ -34,103 +35,19 @@ use Friendica\DI;
 use Friendica\Model\Contact;
 use Friendica\Model\Item;
 use Friendica\Model\Post;
-use Friendica\Model\Profile;
 use Friendica\Model\Tag;
+use Friendica\Model\User;
 use Friendica\Model\Verb;
 use Friendica\Object\Post as PostObject;
 use Friendica\Object\Thread;
 use Friendica\Protocol\Activity;
 use Friendica\Util\Crypto;
 use Friendica\Util\DateTimeFormat;
+use Friendica\Util\Proxy;
 use Friendica\Util\Strings;
 use Friendica\Util\Temporal;
 use Friendica\Util\XML;
 
-function item_extract_images($body) {
-
-       $saved_image = [];
-       $orig_body = $body;
-       $new_body = '';
-
-       $cnt = 0;
-       $img_start = strpos($orig_body, '[img');
-       $img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false);
-       $img_end = ($img_start !== false ? strpos(substr($orig_body, $img_start), '[/img]') : false);
-       while (($img_st_close !== false) && ($img_end !== false)) {
-
-               $img_st_close++; // make it point to AFTER the closing bracket
-               $img_end += $img_start;
-
-               if (!strcmp(substr($orig_body, $img_start + $img_st_close, 5), 'data:')) {
-                       // This is an embedded image
-
-                       $saved_image[$cnt] = substr($orig_body, $img_start + $img_st_close, $img_end - ($img_start + $img_st_close));
-                       $new_body = $new_body . substr($orig_body, 0, $img_start) . '[!#saved_image' . $cnt . '#!]';
-
-                       $cnt++;
-               } else {
-                       $new_body = $new_body . substr($orig_body, 0, $img_end + strlen('[/img]'));
-               }
-
-               $orig_body = substr($orig_body, $img_end + strlen('[/img]'));
-
-               if ($orig_body === false) {
-                       // in case the body ends on a closing image tag
-                       $orig_body = '';
-               }
-
-               $img_start = strpos($orig_body, '[img');
-               $img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false);
-               $img_end = ($img_start !== false ? strpos(substr($orig_body, $img_start), '[/img]') : false);
-       }
-
-       $new_body = $new_body . $orig_body;
-
-       return ['body' => $new_body, 'images' => $saved_image];
-}
-
-function item_redir_and_replace_images($body, $images, $cid) {
-
-       $origbody = $body;
-       $newbody = '';
-
-       $cnt = 1;
-       $pos = BBCode::getTagPosition($origbody, 'url', 0);
-       while ($pos !== false && $cnt < 1000) {
-
-               $search = '/\[url\=(.*?)\]\[!#saved_image([0-9]*)#!\]\[\/url\]' . '/is';
-               $replace = '[url=' . DI::baseUrl() . '/redir/' . $cid
-                                  . '?url=' . '$1' . '][!#saved_image' . '$2' .'#!][/url]';
-
-               $newbody .= substr($origbody, 0, $pos['start']['open']);
-               $subject = substr($origbody, $pos['start']['open'], $pos['end']['close'] - $pos['start']['open']);
-               $origbody = substr($origbody, $pos['end']['close']);
-               if ($origbody === false) {
-                       $origbody = '';
-               }
-
-               $subject = preg_replace($search, $replace, $subject);
-               $newbody .= $subject;
-
-               $cnt++;
-               // Isn't this supposed to use $cnt value for $occurrences? - @MrPetovan
-               $pos = BBCode::getTagPosition($origbody, 'url', 0);
-       }
-       $newbody .= $origbody;
-
-       $cnt = 0;
-       foreach ($images as $image) {
-               /*
-                * We're depending on the property of 'foreach' (specified on the PHP website) that
-                * it loops over the array starting from the first element and going sequentially
-                * to the last element.
-                */
-               $newbody = str_replace('[!#saved_image' . $cnt . '#!]', '[img]' . $image . '[/img]', $newbody);
-               $cnt++;
-       }
-       return $newbody;
-}
-
 /**
  * Render actions localized
  *
@@ -140,11 +57,7 @@ function item_redir_and_replace_images($body, $images, $cid) {
  */
 function localize_item(&$item)
 {
-       $extracted = item_extract_images($item['body']);
-       if ($extracted['images']) {
-               $item['body'] = item_redir_and_replace_images($extracted['body'], $extracted['images'], $item['contact-id']);
-       }
-
+       DI::profiler()->startRecording('rendering');
        /// @todo The following functionality needs to be cleaned up.
        if (!empty($item['verb'])) {
                $activity = DI::activity();
@@ -154,9 +67,11 @@ function localize_item(&$item)
                if (stristr($item['verb'], Activity::POKE)) {
                        $verb = urldecode(substr($item['verb'], strpos($item['verb'],'#') + 1));
                        if (!$verb) {
+                               DI::profiler()->stopRecording();
                                return;
                        }
                        if ($item['object-type'] == "" || $item['object-type'] !== Activity\ObjectType::PERSON) {
+                               DI::profiler()->stopRecording();
                                return;
                        }
 
@@ -209,6 +124,7 @@ function localize_item(&$item)
                                'verb', 'object-type', 'resource-id', 'body', 'plink'];
                        $obj = Post::selectFirst($fields, ['uri' => $item['parent-uri']]);
                        if (!DBA::isResult($obj)) {
+                               DI::profiler()->stopRecording();
                                return;
                        }
 
@@ -259,13 +175,6 @@ function localize_item(&$item)
                }
        }
 
-       // add zrl's to public images
-       $photo_pattern = "/\[url=(.*?)\/photos\/(.*?)\/image\/(.*?)\]\[img(.*?)\]h(.*?)\[\/img\]\[\/url\]/is";
-       if (preg_match($photo_pattern, $item['body'])) {
-               $photo_replace = '[url=' . Profile::zrl('$1' . '/photos/' . '$2' . '/image/' . '$3' , true) . '][img' . '$4' . ']h' . '$5'  . '[/img][/url]';
-               $item['body'] = BBCode::pregReplaceInTag($photo_pattern, $photo_replace, 'url', $item['body']);
-       }
-
        // add sparkle links to appropriate permalinks
        // Only create a redirection to a magic link when logged in
        if (!empty($item['plink']) && Session::isAuthenticated()) {
@@ -273,6 +182,7 @@ function localize_item(&$item)
                        'network' => $item['author-network'], 'url' => $item['author-link']];
                $item['plink'] = Contact::magicLinkByContact($author, $item['plink']);
        }
+       DI::profiler()->stopRecording();
 }
 
 /**
@@ -359,6 +269,7 @@ function conv_get_blocklist()
  */
 function conversation(App $a, array $items, $mode, $update, $preview = false, $order = 'commented', $uid = 0)
 {
+       DI::profiler()->startRecording('rendering');
        $page = DI::page();
 
        $page->registerFooterScript(Theme::getPathForFile('asset/typeahead.js/dist/typeahead.bundle.js'));
@@ -368,7 +279,6 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
 
        $ssl_state = (local_user() ? true : false);
 
-       $profile_owner = 0;
        $live_update_div = '';
 
        $blocklist = conv_get_blocklist();
@@ -377,7 +287,6 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
 
        if ($mode === 'network') {
                $items = conversation_add_children($items, false, $order, $uid);
-               $profile_owner = local_user();
                if (!$update) {
                        /*
                         * The special div is needed for liveUpdate to kick in for this page.
@@ -404,7 +313,6 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
                }
        } elseif ($mode === 'profile') {
                $items = conversation_add_children($items, false, $order, $uid);
-               $profile_owner = $a->profile['uid'];
 
                if (!$update) {
                        $tab = 'posts';
@@ -418,13 +326,12 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
                                 */
 
                                $live_update_div = '<div id="live-profile"></div>' . "\r\n"
-                                       . "<script> var profile_uid = " . $a->profile['uid']
+                                       . "<script> var profile_uid = " . $uid
                                        . "; var netargs = '?f='; </script>\r\n";
                        }
                }
        } elseif ($mode === 'notes') {
                $items = conversation_add_children($items, false, $order, local_user());
-               $profile_owner = local_user();
 
                if (!$update) {
                        $live_update_div = '<div id="live-notes"></div>' . "\r\n"
@@ -433,7 +340,6 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
                }
        } elseif ($mode === 'display') {
                $items = conversation_add_children($items, false, $order, $uid);
-               $profile_owner = $a->profile['uid'];
 
                if (!$update) {
                        $live_update_div = '<div id="live-display"></div>' . "\r\n"
@@ -442,7 +348,6 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
                }
        } elseif ($mode === 'community') {
                $items = conversation_add_children($items, true, $order, $uid);
-               $profile_owner = 0;
 
                if (!$update) {
                        $live_update_div = '<div id="live-community"></div>' . "\r\n"
@@ -453,7 +358,6 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
                }
        } elseif ($mode === 'contacts') {
                $items = conversation_add_children($items, false, $order, $uid);
-               $profile_owner = 0;
 
                if (!$update) {
                        $live_update_div = '<div id="live-contact"></div>' . "\r\n"
@@ -464,7 +368,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
                $live_update_div = '<div id="live-search"></div>' . "\r\n";
        }
 
-       $page_dropping = ((local_user() && local_user() == $profile_owner) ? true : false);
+       $page_dropping = ((local_user() && local_user() == $uid) ? true : false);
 
        if (!$update) {
                $_SESSION['return_path'] = DI::args()->getQueryString();
@@ -493,6 +397,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
        $threadsid = -1;
 
        $page_template = Renderer::getMarkupTemplate("conversation.tpl");
+       $formSecurityToken = BaseModule::getFormSecurityToken('contact_action');
 
        if (!empty($items)) {
                if (in_array($mode, ['community', 'contacts'])) {
@@ -514,7 +419,14 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
 
                        $tpl = 'search_item.tpl';
 
+                       $uriids = [];
+
                        foreach ($items as $item) {
+                               if (in_array($item['uri-id'], $uriids)) {
+                                       continue;
+                               }
+
+                               $uriids[] = $item['uri-id'];
 
                                if (!visible_activity($item)) {
                                        continue;
@@ -578,7 +490,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
 
                                $body_html = Item::prepareBody($item, true, $preview);
 
-                               list($categories, $folders) = DI::contentItem()->determineCategoriesTerms($item);
+                               list($categories, $folders) = DI::contentItem()->determineCategoriesTerms($item, local_user());
 
                                if (!empty($item['content-warning']) && DI::pConfig()->get(local_user(), 'system', 'disable_cw', false)) {
                                        $title = ucfirst($item['content-warning']);
@@ -599,11 +511,11 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
                                        'network_icon' => ContactSelector::networkToIcon($item['network'], $item['author-link']),
                                        'linktitle' => DI::l10n()->t('View %s\'s profile @ %s', $profile_name, $item['author-link']),
                                        'profile_url' => $profile_link,
-                                       'item_photo_menu_html' => item_photo_menu($item),
+                                       'item_photo_menu_html' => item_photo_menu($item, $formSecurityToken),
                                        'name' => $profile_name,
                                        'sparkle' => $sparkle,
                                        'lock' => false,
-                                       'thumb' => DI::baseUrl()->remove($item['author-avatar']),
+                                       'thumb' => DI::baseUrl()->remove(Contact::getAvatarUrlForUrl($item['author-link'], $item['uid'], Proxy::SIZE_THUMB)),
                                        'title' => $title,
                                        'body_html' => $body_html,
                                        'tags' => $tags['tags'],
@@ -623,7 +535,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
                                        'indent' => '',
                                        'owner_name' => '',
                                        'owner_url' => '',
-                                       'owner_photo' => DI::baseUrl()->remove($item['owner-avatar']),
+                                       'owner_photo' => DI::baseUrl()->remove(Contact::getAvatarUrlForUrl($item['owner-link'], $item['uid'], Proxy::SIZE_THUMB)),
                                        'plink' => Item::getPlink($item),
                                        'edpost' => false,
                                        'isstarred' => 'unstarred',
@@ -687,7 +599,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
                                }
                        }
 
-                       $threads = $conv->getTemplateData($conv_responses);
+                       $threads = $conv->getTemplateData($conv_responses, $formSecurityToken);
                        if (!$threads) {
                                Logger::log('[ERROR] conversation : Failed to get template data.', Logger::DEBUG);
                                $threads = [];
@@ -702,104 +614,96 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
                '$remove' => DI::l10n()->t('remove'),
                '$mode' => $mode,
                '$update' => $update,
-               '$user' => $a->user,
                '$threads' => $threads,
                '$dropping' => ($page_dropping ? DI::l10n()->t('Delete Selected Items') : False),
        ]);
 
+       DI::profiler()->stopRecording();
        return $o;
 }
 
 /**
- * Fetch all comments from a query. Additionally set the newest resharer as thread owner.
+ * Adds some information (Causer, post reason, direction) to the fetched post row.
  *
- * @param mixed   $thread_items Database statement with thread posts
- * @param boolean $pinned       Is the item pinned?
- * @param array   $activity     Contact data of the resharer
+ * @param array   $row      Post row
+ * @param array   $activity Contact data of the resharer
  *
  * @return array items with parents and comments
  */
-function conversation_fetch_comments($thread_items, bool $pinned, array $activity) {
-       $comments = [];
-
-       while ($row = Post::fetch($thread_items)) {
-               if (!empty($activity)) {
-                       if (($row['gravity'] == GRAVITY_PARENT)) {
-                               $row['post-reason'] = Item::PR_ANNOUNCEMENT;
-                               $row = array_merge($row, $activity);
-                               $contact = Contact::getById($activity['causer-id'], ['url', 'name', 'thumb']);
-                               $row['causer-link'] = $contact['url'];
-                               $row['causer-avatar'] = $contact['thumb'];
-                               $row['causer-name'] = $contact['name'];
-                       } elseif (($row['gravity'] == GRAVITY_ACTIVITY) && ($row['verb'] == Activity::ANNOUNCE) &&
-                               ($row['author-id'] == $activity['causer-id'])) {
-                               continue;
-                       }
-               }
+function conversation_add_row_information(array $row, array $activity) {
+       DI::profiler()->startRecording('rendering');
 
-               $name = $row['causer-contact-type'] == Contact::TYPE_RELAY ? $row['causer-link'] : $row['causer-name'];
+       if ($row['uid'] == 0) {
+               $row['writable'] = in_array($row['network'], Protocol::FEDERATED);
+       }
 
-               switch ($row['post-reason']) {
-                       case Item::PR_TO:
-                               $row['direction'] = ['direction' => 7, 'title' => DI::l10n()->t('You had been addressed (%s).', 'to')];
-                               break;
-                       case Item::PR_CC:
-                               $row['direction'] = ['direction' => 7, 'title' => DI::l10n()->t('You had been addressed (%s).', 'cc')];
-                               break;
-                       case Item::PR_BTO:
-                               $row['direction'] = ['direction' => 7, 'title' => DI::l10n()->t('You had been addressed (%s).', 'bto')];
-                               break;
-                       case Item::PR_BCC:
-                               $row['direction'] = ['direction' => 7, 'title' => DI::l10n()->t('You had been addressed (%s).', 'bcc')];
-                               break;
-                       case Item::PR_FOLLOWER:
-                               $row['direction'] = ['direction' => 6, 'title' => DI::l10n()->t('You are following %s.', $row['author-name'])];
-                               break;
-                       case Item::PR_TAG:
-                               $row['direction'] = ['direction' => 4, 'title' => DI::l10n()->t('Tagged')];
-                               break;
-                       case Item::PR_ANNOUNCEMENT:
-                               if (!empty($row['causer-id']) && DI::pConfig()->get(local_user(), 'system', 'display_resharer')) {
-                                       $row['owner-id'] = $row['causer-id'];
-                                       $row['owner-link'] = $row['causer-link'];
-                                       $row['owner-avatar'] = $row['causer-avatar'];
-                                       $row['owner-name'] = $row['causer-name'];
-                               }
+       if (!empty($activity)) {
+               if (($row['gravity'] == GRAVITY_PARENT)) {
+                       $row['post-reason'] = Item::PR_ANNOUNCEMENT;
+                       $row = array_merge($row, $activity);
+                       $contact = Contact::getById($activity['causer-id'], ['url', 'name', 'thumb']);
+                       $row['causer-link'] = $contact['url'];
+                       $row['causer-avatar'] = $contact['thumb'];
+                       $row['causer-name'] = $contact['name'];
+               } elseif (($row['gravity'] == GRAVITY_ACTIVITY) && ($row['verb'] == Activity::ANNOUNCE) &&
+                       ($row['author-id'] == $activity['causer-id'])) {
+                       return $row;
+               }
+       }
 
-                               if (($row['gravity'] == GRAVITY_PARENT) && !empty($row['causer-id'])) {
-                                       $causer = ['uid' => 0, 'id' => $row['causer-id'],
-                                               'network' => $row['causer-network'], 'url' => $row['causer-link']];
-                                       $row['reshared'] = DI::l10n()->t('%s reshared this.', '<a href="'. htmlentities(Contact::magicLinkByContact($causer)) .'">' . htmlentities($name) . '</a>');
-                               }
-                               $row['direction'] = ['direction' => 3, 'title' => (empty($row['causer-id']) ? DI::l10n()->t('Reshared') : DI::l10n()->t('Reshared by %s', $name))];
-                               break;
-                       case Item::PR_COMMENT:
-                               $row['direction'] = ['direction' => 5, 'title' => DI::l10n()->t('%s is participating in this thread.', $row['author-name'])];
-                               break;
-                       case Item::PR_STORED:
-                               $row['direction'] = ['direction' => 8, 'title' => DI::l10n()->t('Stored')];
-                               break;
-                       case Item::PR_GLOBAL:
-                               $row['direction'] = ['direction' => 9, 'title' => DI::l10n()->t('Global')];
-                               break;
-                       case Item::PR_RELAY:
-                               $row['direction'] = ['direction' => 10, 'title' => (empty($row['causer-id']) ? DI::l10n()->t('Relayed') : DI::l10n()->t('Relayed by %s.', $name))];
-                               break;
-                       case Item::PR_FETCHED:
-                               $row['direction'] = ['direction' => 2, 'title' => (empty($row['causer-id']) ? DI::l10n()->t('Fetched') : DI::l10n()->t('Fetched because of %s', $name))];
-                               break;
+       switch ($row['post-reason']) {
+               case Item::PR_TO:
+                       $row['direction'] = ['direction' => 7, 'title' => DI::l10n()->t('You had been addressed (%s).', 'to')];
+                       break;
+               case Item::PR_CC:
+                       $row['direction'] = ['direction' => 7, 'title' => DI::l10n()->t('You had been addressed (%s).', 'cc')];
+                       break;
+               case Item::PR_BTO:
+                       $row['direction'] = ['direction' => 7, 'title' => DI::l10n()->t('You had been addressed (%s).', 'bto')];
+                       break;
+               case Item::PR_BCC:
+                       $row['direction'] = ['direction' => 7, 'title' => DI::l10n()->t('You had been addressed (%s).', 'bcc')];
+                       break;
+               case Item::PR_FOLLOWER:
+                       $row['direction'] = ['direction' => 6, 'title' => DI::l10n()->t('You are following %s.', $row['author-name'])];
+                       break;
+               case Item::PR_TAG:
+                       $row['direction'] = ['direction' => 4, 'title' => DI::l10n()->t('Tagged')];
+                       break;
+               case Item::PR_ANNOUNCEMENT:
+                       if (!empty($row['causer-id']) && DI::pConfig()->get(local_user(), 'system', 'display_resharer')) {
+                               $row['owner-id'] = $row['causer-id'];
+                               $row['owner-link'] = $row['causer-link'];
+                               $row['owner-avatar'] = $row['causer-avatar'];
+                               $row['owner-name'] = $row['causer-name'];
                        }
 
-               if ($row['gravity'] == GRAVITY_PARENT) {
-                       $row['pinned'] = $pinned;
-               }
-
-               $comments[] = $row;
+                       if (($row['gravity'] == GRAVITY_PARENT) && !empty($row['causer-id'])) {
+                               $causer = ['uid' => 0, 'id' => $row['causer-id'],
+                                       'network' => $row['causer-network'], 'url' => $row['causer-link']];
+                               $row['reshared'] = DI::l10n()->t('%s reshared this.', '<a href="'. htmlentities(Contact::magicLinkByContact($causer)) .'">' . htmlentities($row['causer-name']) . '</a>');
+                       }
+                       $row['direction'] = ['direction' => 3, 'title' => (empty($row['causer-id']) ? DI::l10n()->t('Reshared') : DI::l10n()->t('Reshared by %s <%s>', $row['causer-name'], $row['causer-link']))];
+                       break;
+               case Item::PR_COMMENT:
+                       $row['direction'] = ['direction' => 5, 'title' => DI::l10n()->t('%s is participating in this thread.', $row['author-name'])];
+                       break;
+               case Item::PR_STORED:
+                       $row['direction'] = ['direction' => 8, 'title' => DI::l10n()->t('Stored')];
+                       break;
+               case Item::PR_GLOBAL:
+                       $row['direction'] = ['direction' => 9, 'title' => DI::l10n()->t('Global')];
+                       break;
+               case Item::PR_RELAY:
+                       $row['direction'] = ['direction' => 10, 'title' => (empty($row['causer-id']) ? DI::l10n()->t('Relayed') : DI::l10n()->t('Relayed by %s <%s>', $row['causer-name'], $row['causer-link']))];
+                       break;
+               case Item::PR_FETCHED:
+                       $row['direction'] = ['direction' => 2, 'title' => (empty($row['causer-id']) ? DI::l10n()->t('Fetched') : DI::l10n()->t('Fetched because of %s <%s>', $row['causer-name'], $row['causer-link']))];
+                       break;
        }
 
-       DBA::close($thread_items);
-
-       return $comments;
+       DI::profiler()->stopRecording();
+       return $row;
 }
 
 /**
@@ -817,79 +721,79 @@ function conversation_fetch_comments($thread_items, bool $pinned, array $activit
  * @throws \Friendica\Network\HTTPException\InternalServerErrorException
  */
 function conversation_add_children(array $parents, $block_authors, $order, $uid) {
+       DI::profiler()->startRecording('rendering');
        if (count($parents) > 1) {
                $max_comments = DI::config()->get('system', 'max_comments', 100);
        } else {
                $max_comments = DI::config()->get('system', 'max_display_comments', 1000);
        }
 
-       $params = ['order' => ['gravity', 'uid', 'commented' => true]];
-
-       if ($max_comments > 0) {
-               $params['limit'] = $max_comments;
-       }
+       $params = ['order' => ['uri-id' => true, 'uid' => true]];
 
-       $items = [];
+       $activities      = [];
+       $uriids          = [];
+       $commentcounter  = [];
+       $activitycounter = [];
 
        foreach ($parents AS $parent) {
                if (!empty($parent['thr-parent-id']) && !empty($parent['gravity']) && ($parent['gravity'] == GRAVITY_ACTIVITY)) {
-                       $condition = ["`parent-uri-id` = ? AND `uid` IN (0, ?) AND (`vid` != ? OR `vid` IS NULL)",
-                               $parent['thr-parent-id'], $uid, Verb::getID(Activity::FOLLOW)];
+                       $uriid = $parent['thr-parent-id'];
                        if (!empty($parent['author-id'])) {
-                               $activity = ['causer-id' => $parent['author-id']];
+                               $activities[$uriid] = ['causer-id' => $parent['author-id']];
                                foreach (['commented', 'received', 'created'] as $orderfields) {
                                        if (!empty($parent[$orderfields])) {
-                                               $activity[$orderfields] = $parent[$orderfields];
+                                               $activities[$uriid][$orderfields] = $parent[$orderfields];
                                        }
                                }
                        }
                } else {
-                       $condition = ["`parent-uri-id` = ? AND `uid` IN (0, ?) AND (`vid` != ? OR `vid` IS NULL)",
-                               $parent['uri-id'], $uid, Verb::getID(Activity::FOLLOW)];
-                       $activity = [];
+                       $uriid = $parent['uri-id'];
                }
-               $items = conversation_fetch_items($parent, $items, $condition, $block_authors, $params, $activity);
-       }
+               $uriids[] = $uriid;
 
-       foreach ($items as $index => $item) {
-               if ($item['uid'] == 0) {
-                       $items[$index]['writable'] = in_array($item['network'], Protocol::FEDERATED);
-               }
+               $commentcounter[$uriid]  = 0;
+               $activitycounter[$uriid] = 0;
        }
 
-       $items = conv_sort($items, $order);
-
-       return $items;
-}
-
-/**
- * Fetch conversation items
- *
- * @param array   $parent        Parent Item array
- * @param array   $items         Item array
- * @param array   $condition     SQL condition
- * @param boolean $block_authors Don't show posts from contacts that are hidden (used on the community page)
- * @param array   $params        SQL parameters
- * @param array   $activity      Contact data of the resharer
- * @return array
- */
-function conversation_fetch_items(array $parent, array $items, array $condition, bool $block_authors, array $params, array $activity) {
+       $condition = ['parent-uri-id' => $uriids];
        if ($block_authors) {
-               $condition[0] .= " AND NOT `author-hidden`";
+               $condition['author-hidden'] = false;
        }
 
+       $condition = DBA::mergeConditions($condition,
+               ["`uid` IN (0, ?) AND (`vid` != ? OR `vid` IS NULL)", $uid, Verb::getID(Activity::FOLLOW)]);
+
        $thread_items = Post::selectForUser(local_user(), array_merge(Item::DISPLAY_FIELDLIST, ['pinned', 'contact-uid', 'gravity', 'post-type', 'post-reason']), $condition, $params);
 
-       $comments = conversation_fetch_comments($thread_items, $parent['pinned'] ?? false, $activity);
+       $items = [];
+
+       while ($row = Post::fetch($thread_items)) {
+               if (!empty($items[$row['uri-id']]) && ($row['uid'] == 0)) {
+                       continue;
+               }
 
-       if (count($comments) != 0) {
-               $items = array_merge($items, $comments);
+               if ($max_comments > 0) {
+                       if (($row['gravity'] == GRAVITY_COMMENT) && (++$commentcounter[$row['parent-uri-id']] > $max_comments)) {
+                               continue;
+                       }
+                       if (($row['gravity'] == GRAVITY_ACTIVITY) && (++$activitycounter[$row['parent-uri-id']] > $max_comments)) {
+                               continue;
+                       }
+               }
+               $items[$row['uri-id']] = conversation_add_row_information($row, $activities[$row['uri-id']] ?? []);
        }
+
+       DBA::close($thread_items);
+
+       $items = conv_sort($items, $order);
+
+       DI::profiler()->stopRecording();
        return $items;
 }
 
-function item_photo_menu($item)
+function item_photo_menu($item, string $formSecurityToken)
 {
+       DI::profiler()->startRecording('rendering');
        $sub_link = '';
        $poke_link = '';
        $contact_url = '';
@@ -930,8 +834,8 @@ function item_photo_menu($item)
        if (!empty($pcid)) {
                $contact_url = 'contact/' . $pcid;
                $posts_link  = $contact_url . '/posts';
-               $block_link  = $item['self'] ? '' : $contact_url . '/block';
-               $ignore_link = $item['self'] ? '' : $contact_url . '/ignore';
+               $block_link  = $item['self'] ? '' : $contact_url . '/block?t=' . $formSecurityToken;
+               $ignore_link = $item['self'] ? '' : $contact_url . '/ignore?t=' . $formSecurityToken;
        }
 
        if ($cid && !$item['self']) {
@@ -988,6 +892,7 @@ function item_photo_menu($item)
                        $o .= '<li role="menuitem"><a href="' . $v . '">' . $k . '</a></li>' . PHP_EOL;
                }
        }
+       DI::profiler()->stopRecording();
        return $o;
 }
 
@@ -1086,6 +991,7 @@ function builtin_activity_puller(array $activity, array &$conv_responses)
  * @throws \Friendica\Network\HTTPException\InternalServerErrorException
  */
 function format_activity(array $links, $verb, $id) {
+       DI::profiler()->startRecording('rendering');
        $o = '';
        $expanded = '';
        $phrase = '';
@@ -1165,13 +1071,31 @@ function format_activity(array $links, $verb, $id) {
        ]);
        $o .= $expanded;
 
+       DI::profiler()->stopRecording();
        return $o;
 }
 
-function status_editor(App $a, $x, $notes_cid = 0, $popup = false)
+function status_editor(App $a, array $x = [], $notes_cid = 0, $popup = false)
 {
+       $user = User::getById($a->getLoggedInUserId(), ['uid', 'nickname', 'allow_location', 'default-location']);
+       if (empty($user['uid'])) {
+               return '';
+       }
+
+       DI::profiler()->startRecording('rendering');
        $o = '';
 
+       $x['allow_location']   = $x['allow_location']   ?? $user['allow_location'];
+       $x['default_location'] = $x['default_location'] ?? $user['default-location'];
+       $x['nickname']         = $x['nickname']         ?? $user['nickname'];
+       $x['lockstate']        = $x['lockstate']        ?? ACL::getLockstateForUserId($user['uid']) ? 'lock' : 'unlock';
+       $x['acl']              = $x['acl']              ?? ACL::getFullSelectorHTML(DI::page(), $user['uid'], true);
+       $x['bang']             = $x['bang']             ?? '';
+       $x['visitor']          = $x['visitor']          ?? 'block';
+       $x['is_owner']         = $x['is_owner']         ?? true;
+       $x['profile_uid']      = $x['profile_uid']      ?? local_user();
+
+
        $geotag = !empty($x['allow_location']) ? Renderer::replaceMacros(Renderer::getMarkupTemplate('jot_geotag.tpl'), []) : '';
 
        $tpl = Renderer::getMarkupTemplate('jot-header.tpl');
@@ -1221,6 +1145,13 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false)
                '$placeholdertitle' => DI::l10n()->t('Set title'),
                '$category'     => $x['category'] ?? '',
                '$placeholdercategory' => Feature::isEnabled(local_user(), 'categories') ? DI::l10n()->t("Categories \x28comma-separated list\x29") : '',
+               '$scheduled_at' => Temporal::getDateTimeField(
+                       new DateTime(),
+                       DateTime::createFromFormat(DateTimeFormat::MYSQL, DateTimeFormat::local('now + 6 months')),
+                       null,
+                       DI::l10n()->t('Scheduled at'),
+                       'scheduled_at',
+               ),
                '$wait'         => DI::l10n()->t('Please wait'),
                '$permset'      => DI::l10n()->t('Permission settings'),
                '$shortpermset' => DI::l10n()->t('Permissions'),
@@ -1239,7 +1170,6 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false)
                '$preview'      => DI::l10n()->t('Preview'),
                '$jotplugins'   => $jotplugins,
                '$notes_cid'    => $notes_cid,
-               '$sourceapp'    => DI::l10n()->t($a->sourcename),
                '$cancel'       => DI::l10n()->t('Cancel'),
                '$rand_num'     => Crypto::randomDigits(12),
 
@@ -1258,6 +1188,7 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false)
                $o = '<div id="jot-popup" style="display: none;">' . $o . '</div>';
        }
 
+       DI::profiler()->stopRecording();
        return $o;
 }
 
@@ -1271,6 +1202,7 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false)
  */
 function get_item_children(array &$item_list, array $parent, $recursive = true)
 {
+       DI::profiler()->startRecording('rendering');
        $children = [];
        foreach ($item_list as $i => $item) {
                if ($item['gravity'] != GRAVITY_PARENT) {
@@ -1292,6 +1224,7 @@ function get_item_children(array &$item_list, array $parent, $recursive = true)
                        }
                }
        }
+       DI::profiler()->stopRecording();
        return $children;
 }
 
@@ -1303,6 +1236,7 @@ function get_item_children(array &$item_list, array $parent, $recursive = true)
  */
 function sort_item_children(array $items)
 {
+       DI::profiler()->startRecording('rendering');
        $result = $items;
        usort($result, 'sort_thr_received_rev');
        foreach ($result as $k => $i) {
@@ -1310,6 +1244,7 @@ function sort_item_children(array $items)
                        $result[$k]['children'] = sort_item_children($result[$k]['children']);
                }
        }
+       DI::profiler()->stopRecording();
        return $result;
 }
 
@@ -1347,7 +1282,9 @@ function add_children_to_list(array $children, array &$item_list)
  */
 function smart_flatten_conversation(array $parent)
 {
+       DI::profiler()->startRecording('rendering');
        if (!isset($parent['children']) || count($parent['children']) == 0) {
+               DI::profiler()->stopRecording();
                return $parent;
        }
 
@@ -1358,6 +1295,7 @@ function smart_flatten_conversation(array $parent)
                if (isset($child['children']) && count($child['children'])) {
                        // This helps counting only the regular posts
                        $count_post_closure = function($var) {
+                               DI::profiler()->stopRecording();
                                return $var['verb'] === Activity::POST;
                        };
 
@@ -1383,6 +1321,7 @@ function smart_flatten_conversation(array $parent)
                }
        }
 
+       DI::profiler()->stopRecording();
        return $parent;
 }
 
@@ -1400,9 +1339,11 @@ function smart_flatten_conversation(array $parent)
  */
 function conv_sort(array $item_list, $order)
 {
+       DI::profiler()->startRecording('rendering');
        $parents = [];
 
        if (!(is_array($item_list) && count($item_list))) {
+               DI::profiler()->stopRecording();
                return $parents;
        }
 
@@ -1462,6 +1403,7 @@ function conv_sort(array $item_list, $order)
                }
        }
 
+       DI::profiler()->stopRecording();
        return $parents;
 }