]> git.mxchange.org Git - friendica-addons.git/blobdiff - twitter/twitter.php
Remove the use of app function
[friendica-addons.git] / twitter / twitter.php
index 47328ecaffabcda4a20be320ef0f203e52183d8c..56b5ead84c43b6048c422a5df9eb67af7ac8875d 100644 (file)
@@ -66,8 +66,6 @@ use Abraham\TwitterOAuth\TwitterOAuth;
 use Abraham\TwitterOAuth\TwitterOAuthException;
 use Codebird\Codebird;
 use Friendica\App;
-use Friendica\Content\OEmbed;
-use Friendica\Content\PageInfo;
 use Friendica\Content\Text\BBCode;
 use Friendica\Content\Text\Plaintext;
 use Friendica\Core\Hook;
@@ -81,7 +79,6 @@ use Friendica\Model\Contact;
 use Friendica\Model\Conversation;
 use Friendica\Model\Group;
 use Friendica\Model\Item;
-use Friendica\Model\ItemContent;
 use Friendica\Model\ItemURI;
 use Friendica\Model\Post;
 use Friendica\Model\Tag;
@@ -149,7 +146,7 @@ function twitter_follow(App $a, array &$contact)
        $nickname = preg_replace("=https?://twitter.com/(.*)=ism", "$1", $contact["url"]);
        $nickname = str_replace("@twitter.com", "", $nickname);
 
-       $uid = $a->user["uid"];
+       $uid = $a->getUserId();
 
        $ckey = DI::config()->get('twitter', 'consumerkey');
        $csecret = DI::config()->get('twitter', 'consumersecret');
@@ -269,6 +266,9 @@ function twitter_settings(App $a, &$s)
        if (!local_user()) {
                return;
        }
+
+       $user = User::getById(local_user());
+
        DI::page()['htmlhead'] .= '<link rel="stylesheet"  type="text/css" href="' . DI::baseUrl()->get() . '/addon/twitter/twitter.css' . '" media="all" />' . "\r\n";
        /*       * *
         * 1) Check that we have global consumer key & secret
@@ -359,7 +359,7 @@ function twitter_settings(App $a, &$s)
                                $s .= Renderer::replaceMacros($field_checkbox, [
                                        '$field' => ['twitter-enable', DI::l10n()->t('Allow posting to Twitter'), $enabled, DI::l10n()->t('If enabled all your <strong>public</strong> postings can be posted to the associated Twitter account. You can choose to do so by default (here) or for every posting separately in the posting options when writing the entry.')]
                                ]);
-                               if ($a->user['hidewall']) {
+                               if ($user['hidewall']) {
                                        $s .= '<p>' . DI::l10n()->t('<strong>Note</strong>: Due to your privacy settings (<em>Hide your profile details from unknown viewers?</em>) the link potentially included in public postings relayed to Twitter will lead the visitor to a blank page informing the visitor that the access to your profile has been restricted.') . '</p>';
                                }
                                $s .= Renderer::replaceMacros($field_checkbox, [
@@ -411,7 +411,7 @@ function twitter_hook_fork(App $a, array &$b)
 
        if (DI::pConfig()->get($post['uid'], 'twitter', 'import')) {
                // Don't fork if it isn't a reply to a twitter post
-               if (($post['parent'] != $post['id']) && !Item::exists(['id' => $post['parent'], 'network' => Protocol::TWITTER])) {
+               if (($post['parent'] != $post['id']) && !Post::exists(['id' => $post['parent'], 'network' => Protocol::TWITTER])) {
                        Logger::notice('No twitter parent found', ['item' => $post['id']]);
                        $b['execute'] = false;
                        return;
@@ -498,26 +498,30 @@ function twitter_action(App $a, $uid, $pid, $action)
 
        Logger::debug('before action', ['action' => $action, 'pid' => $pid, 'data' => $post]);
 
-       switch ($action) {
-               case 'delete':
-                       // To-Do: $result = $connection->post('statuses/destroy', $post);
-                       $result = [];
-                       break;
-               case 'like':
-                       $result = $connection->post('favorites/create', $post);
-                       if ($connection->getLastHttpCode() != 200) {
-                               Logger::warning('Unable to create favorite', ['result' => $result]);
-                       }
-                       break;
-               case 'unlike':
-                       $result = $connection->post('favorites/destroy', $post);
-                       if ($connection->getLastHttpCode() != 200) {
-                               Logger::warning('Unable to destroy favorite', ['result' => $result]);
-                       }
-                       break;
-               default:
-                       Logger::warning('Unhandled action', ['action' => $action]);
-                       $result = [];
+       try {
+               switch ($action) {
+                       case 'delete':
+                               // To-Do: $result = $connection->post('statuses/destroy', $post);
+                               $result = [];
+                               break;
+                       case 'like':
+                               $result = $connection->post('favorites/create', $post);
+                               if ($connection->getLastHttpCode() != 200) {
+                                       Logger::warning('Unable to create favorite', ['result' => $result]);
+                               }
+                               break;
+                       case 'unlike':
+                               $result = $connection->post('favorites/destroy', $post);
+                               if ($connection->getLastHttpCode() != 200) {
+                                       Logger::warning('Unable to destroy favorite', ['result' => $result]);
+                               }
+                               break;
+                       default:
+                               Logger::warning('Unhandled action', ['action' => $action]);
+                               $result = [];
+               }
+       } catch (TwitterOAuthException $twitterOAuthException) {
+               Logger::warning('Unable to communicate with twitter', ['action' => $action, 'data' => $post, 'code' => $twitterOAuthException->getCode(), 'exception' => $twitterOAuthException]);
        }
 
        Logger::info('after action', ['action' => $action, 'result' => $result]);
@@ -545,6 +549,8 @@ function twitter_post_hook(App $a, array &$b)
                return;
        }
 
+       $b['body'] = Post\Media::addAttachmentsToBody($b['uri-id'], $b['body']);
+
        $thr_parent = null;
 
        if ($b['parent'] != $b['id']) {
@@ -559,7 +565,7 @@ function twitter_post_hook(App $a, array &$b)
                }
 
                $condition = ['uri' => $b["thr-parent"], 'uid' => $b["uid"]];
-               $thr_parent = Item::selectFirst(['uri', 'extid', 'author-nick', 'author-network'], $condition);
+               $thr_parent = Post::selectFirst(['uri', 'extid', 'author-link', 'author-nick', 'author-network'], $condition);
                if (!DBA::isResult($thr_parent)) {
                        Logger::warning('No parent found', ['thr-parent' => $b["thr-parent"]]);
                        return;
@@ -666,12 +672,12 @@ function twitter_post_hook(App $a, array &$b)
 
                $b['body'] = twitter_update_mentions($b['body']);
 
-               $msgarr = ItemContent::getPlaintextPost($b, $max_char, true, BBCode::TWITTER);
+               $msgarr = Plaintext::getPost($b, $max_char, true, BBCode::TWITTER);
                Logger::info('Got plaintext', ['id' => $b['id'], 'message' => $msgarr]);
                $msg = $msgarr["text"];
 
                if (($msg == "") && isset($msgarr["title"])) {
-                       $msg = Plaintext::shorten($msgarr["title"], $max_char - 50);
+                       $msg = Plaintext::shorten($msgarr["title"], $max_char - 50, $b['uid']);
                }
 
                // Add the link to the body if the type isn't a photo or there are more than 4 images in the post
@@ -713,10 +719,10 @@ function twitter_post_hook(App $a, array &$b)
                                                        $data = ['media_id' => $media->media_id_string,
                                                                'alt_text' => ['text' => substr($image['description'], 0, 420)]];
                                                        $ret = $cb->media_metadata_create($data);
-                                                       Logger::info('Metadata create', ['id' => $b['id'], 'data' => $data, 'return' => json_encode($ret)]);
+                                                       Logger::info('Metadata create', ['id' => $b['id'], 'data' => $data, 'return' => $ret]);
                                                }
                                        } else {
-                                               Logger::error('Failed upload', ['id' => $b['id'], 'image' => $image['url']]);
+                                               Logger::error('Failed upload', ['id' => $b['id'], 'image' => $image['url'], 'return' => $media]);
                                                throw new Exception('Failed upload of ' . $image['url']);
                                        }
                                }
@@ -851,10 +857,10 @@ function twitter_expire(App $a)
 
        Logger::notice('Start deleting expired posts');
 
-       $r = Item::select(['id', 'guid'], ['deleted' => true, 'network' => Protocol::TWITTER]);
-       while ($row = DBA::fetch($r)) {
+       $r = Post::select(['id', 'guid'], ['deleted' => true, 'network' => Protocol::TWITTER]);
+       while ($row = Post::fetch($r)) {
                Logger::info('[twitter] Delete expired item', ['id' => $row['id'], 'guid' => $row['guid'], 'callstack' => \Friendica\Core\System::callstack()]);
-               DBA::delete('item', ['id' => $row['id']]);
+               Item::markForDeletionById($row['id']);
        }
        DBA::close($r);
 
@@ -885,7 +891,7 @@ function twitter_prepare_body(App $a, array &$b)
                $item["plink"] = DI::baseUrl()->get() . "/display/" . $item["guid"];
 
                $condition = ['uri' => $item["thr-parent"], 'uid' => local_user()];
-               $orig_post = Item::selectFirst(['author-link'], $condition);
+               $orig_post = Post::selectFirst(['author-link'], $condition);
                if (DBA::isResult($orig_post)) {
                        $nicknameplain = preg_replace("=https?://twitter.com/(.*)=ism", "$1", $orig_post["author-link"]);
                        $nickname = "@[url=" . $orig_post["author-link"] . "]" . $nicknameplain . "[/url]";
@@ -896,7 +902,7 @@ function twitter_prepare_body(App $a, array &$b)
                        }
                }
 
-               $msgarr = ItemContent::getPlaintextPost($item, $max_char, true, BBCode::TWITTER);
+               $msgarr = Plaintext::getPost($item, $max_char, true, BBCode::TWITTER);
                $msg = $msgarr["text"];
 
                if (isset($msgarr["url"]) && ($msgarr["type"] != "photo")) {
@@ -911,6 +917,24 @@ function twitter_prepare_body(App $a, array &$b)
        }
 }
 
+function twitter_statuses_show(string $id, TwitterOAuth $twitterOAuth = null)
+{
+       if ($twitterOAuth === null) {
+               $ckey = DI::config()->get('twitter', 'consumerkey');
+               $csecret = DI::config()->get('twitter', 'consumersecret');
+
+               if (empty($ckey) || empty($csecret)) {
+                       return new stdClass();
+               }
+
+               $twitterOAuth = new TwitterOAuth($ckey, $csecret);
+       }
+
+       $parameters = ['trim_user' => false, 'tweet_mode' => 'extended', 'id' => $id, 'include_ext_alt_text' => true];
+
+       return $twitterOAuth->get('statuses/show', $parameters);
+}
+
 /**
  * Parse Twitter status URLs since Twitter removed OEmbed
  *
@@ -930,18 +954,7 @@ function twitter_parse_link(App $a, array &$b)
                return;
        }
 
-       $ckey = DI::config()->get('twitter', 'consumerkey');
-       $csecret = DI::config()->get('twitter', 'consumersecret');
-
-       if (empty($ckey) || empty($csecret)) {
-               return;
-       }
-
-       $connection = new TwitterOAuth($ckey, $csecret);
-
-       $parameters = ['trim_user' => false, 'tweet_mode' => 'extended', 'id' => $matches[1], 'include_ext_alt_text' => true];
-
-       $status = $connection->get('statuses/show', $parameters);
+       $status = twitter_statuses_show($matches[1]);
 
        if (empty($status->id)) {
                return;
@@ -1207,6 +1220,7 @@ function twitter_user_to_contact($data)
                'location' => $data->location,
                'about'    => $data->description,
                'photo'    => twitter_fix_avatar($data->profile_image_url_https),
+               'header'   => $data->profile_banner_url ?? $data->profile_background_image_url_https,
        ];
 
        return $fields;
@@ -1350,13 +1364,13 @@ function twitter_fetchuser($screen_name)
  *
  * @param string   $body
  * @param stdClass $status
- * @param string   $picture
  * @return array
  * @throws \Friendica\Network\HTTPException\InternalServerErrorException
  */
-function twitter_expand_entities($body, stdClass $status, $picture)
+function twitter_expand_entities($body, stdClass $status)
 {
        $plain = $body;
+       $contains_urls = false;
 
        $taglist = [];
 
@@ -1382,14 +1396,10 @@ function twitter_expand_entities($body, stdClass $status, $picture)
                ];
        }
 
-       // This URL if set will be used to add an attachment at the bottom of the post
-       $attachmentUrl = '';
-
        foreach ($status->entities->urls ?? [] as $url) {
                $plain = str_replace($url->url, '', $plain);
 
                if ($url->url && $url->expanded_url && $url->display_url) {
-
                        // Quote tweet, we just remove the quoted tweet URL from the body, the share block will be added later.
                        if (!empty($status->quoted_status) && isset($status->quoted_status_id_str)
                                && substr($url->expanded_url, -strlen($status->quoted_status_id_str)) == $status->quoted_status_id_str
@@ -1401,53 +1411,17 @@ function twitter_expand_entities($body, stdClass $status, $picture)
                                continue;
                        }
 
-                       $expanded_url = $url->expanded_url;
-
-                       $final_url = DI::httpRequest()->finalUrl($url->expanded_url);
+                       $contains_urls = true;
 
-                       $oembed_data = OEmbed::fetchURL($final_url);
-
-                       if (empty($oembed_data) || empty($oembed_data->type)) {
-                               continue;
-                       }
+                       $expanded_url = $url->expanded_url;
 
                        // Quickfix: Workaround for URL with '[' and ']' in it
                        if (strpos($expanded_url, '[') || strpos($expanded_url, ']')) {
                                $expanded_url = $url->url;
                        }
 
-                       if ($oembed_data->type == 'video') {
-                               $attachmentUrl = $expanded_url;
-                               $replace = '';
-                       } elseif (($oembed_data->type == 'photo') && isset($oembed_data->url)) {
-                               $replace = '[url=' . $expanded_url . '][img]' . $oembed_data->url . '[/img][/url]';
-                       } elseif ($oembed_data->type != 'link') {
-                               $replace = '[url=' . $expanded_url . ']' . $url->display_url . '[/url]';
-                       } else {
-                               $img_str = DI::httpRequest()->fetch($final_url, 4);
-
-                               $tempfile = tempnam(get_temppath(), 'cache');
-                               file_put_contents($tempfile, $img_str);
-
-                               // See http://php.net/manual/en/function.exif-imagetype.php#79283
-                               if (filesize($tempfile) > 11) {
-                                       $mime = image_type_to_mime_type(exif_imagetype($tempfile));
-                               } else {
-                                       $mime = false;
-                               }
-
-                               unlink($tempfile);
-
-                               if (substr($mime, 0, 6) == 'image/') {
-                                       $replace = '[img]' . $final_url . '[/img]';
-                               } else {
-                                       $attachmentUrl = $expanded_url;
-                                       $replace = '';
-                               }
-                       }
-
                        $replacementList[$url->indices[0]] = [
-                               'replace' => $replace,
+                               'replace' => '[url=' . $expanded_url . ']' . $url->display_url . '[/url]',
                                'length' => $url->indices[1] - $url->indices[0],
                        ];
                }
@@ -1459,57 +1433,91 @@ function twitter_expand_entities($body, stdClass $status, $picture)
                $body = Strings::substringReplace($body, $parameters['replace'], $startIndex, $parameters['length']);
        }
 
-       // Footer will be taken care of with a share block in the case of a quote
-       if (empty($status->quoted_status)) {
-               $footer = '';
-               if ($attachmentUrl) {
-                       $footer = "\n" . PageInfo::getFooterFromUrl($attachmentUrl, false, $picture);
-               }
+       $body = trim($body);
 
-               if (trim($footer)) {
-                       $body .= $footer;
-               } elseif ($picture) {
-                       $body .= "\n\n[img]" . $picture . "[/img]\n";
-               } else {
-                       $body = PageInfo::searchAndAppendToBody($body);
+       return ['body' => trim($body), 'plain' => trim($plain), 'taglist' => $taglist, 'urls' => $contains_urls];
+}
+
+/**
+ * Store entity attachments
+ *
+ * @param integer $uriid
+ * @param object $post Twitter object with the post
+ */
+function twitter_store_attachments(int $uriid, $post)
+{
+       if (!empty($post->extended_entities->media)) {
+               foreach ($post->extended_entities->media AS $medium) {
+                       switch ($medium->type) {
+                               case 'photo':
+                                       $attachment = ['uri-id' => $uriid, 'type' => Post\Media::IMAGE];
+
+                                       $attachment['url'] = $medium->media_url_https . '?name=large';
+                                       $attachment['width'] = $medium->sizes->large->w;
+                                       $attachment['height'] = $medium->sizes->large->h;
+
+                                       if ($medium->sizes->small->w != $attachment['width']) {
+                                               $attachment['preview'] = $medium->media_url_https . '?name=small';
+                                               $attachment['preview-width'] = $medium->sizes->small->w;
+                                               $attachment['preview-height'] = $medium->sizes->small->h;
+                                       }
+
+                                       $attachment['name'] = $medium->display_url ?? null;
+                                       $attachment['description'] = $medium->ext_alt_text ?? null;
+                                       Logger::debug('Photo attachment', ['attachment' => $attachment]);
+                                       Post\Media::insert($attachment);
+                                       break;
+                               case 'video':
+                               case 'animated_gif':
+                                       $attachment = ['uri-id' => $uriid, 'type' => Post\Media::VIDEO];
+                                       if (is_array($medium->video_info->variants)) {
+                                               $bitrate = 0;
+                                               // We take the video with the highest bitrate
+                                               foreach ($medium->video_info->variants AS $variant) {
+                                                       if (($variant->content_type == 'video/mp4') && ($variant->bitrate >= $bitrate)) {
+                                                               $attachment['url'] = $variant->url;
+                                                               $bitrate = $variant->bitrate;
+                                                       }
+                                               }
+                                       }
+
+                                       $attachment['name'] = $medium->display_url ?? null;
+                                       $attachment['preview'] = $medium->media_url_https . ':small';
+                                       $attachment['preview-width'] = $medium->sizes->small->w;
+                                       $attachment['preview-height'] = $medium->sizes->small->h;
+                                       $attachment['description'] = $medium->ext_alt_text ?? null;
+                                       Logger::debug('Video attachment', ['attachment' => $attachment]);
+                                       Post\Media::insert($attachment);
+                                       break;
+                               default:
+                                       Logger::notice('Unknown media type', ['medium' => $medium]);
+                       }
                }
        }
 
-       return ['body' => $body, 'plain' => trim($plain), 'taglist' => $taglist];
+       if (!empty($post->entities->urls)) {
+               foreach ($post->entities->urls as $url) {
+                       $attachment = ['uri-id' => $uriid, 'type' => Post\Media::UNKNOWN, 'url' => $url->expanded_url, 'name' => $url->display_url];
+                       Logger::debug('Attached link', ['attachment' => $attachment]);
+                       Post\Media::insert($attachment);
+               }
+       }
 }
 
 /**
  * @brief Fetch media entities and add media links to the body
  *
- * @param object $post Twitter object with the post
- * @param array $postarray Array of the item that is about to be posted
- *
- * @return $picture string Image URL or empty string
+ * @param object  $post      Twitter object with the post
+ * @param array   $postarray Array of the item that is about to be posted
+ * @param integer $uriid URI Id used to store tags. -1 = don't store tags for this post.
  */
-function twitter_media_entities($post, array &$postarray)
+function twitter_media_entities($post, array &$postarray, int $uriid = -1)
 {
        // There are no media entities? So we quit.
        if (empty($post->extended_entities->media)) {
-               return '';
-       }
-
-       // When the post links to an external page, we only take one picture.
-       // We only do this when there is exactly one media.
-       if ((count($post->entities->urls) > 0) && (count($post->extended_entities->media) == 1)) {
-               $medium = $post->extended_entities->media[0];
-               $picture = '';
-               foreach ($post->entities->urls as $link) {
-                       // Let's make sure the external link url matches the media url
-                       if ($medium->url == $link->url && isset($medium->media_url_https)) {
-                               $picture = $medium->media_url_https;
-                               $postarray['body'] = str_replace($medium->url, '', $postarray['body']);
-                               return $picture;
-                       }
-               }
+               return;
        }
 
-
-
        // This is a pure media post, first search for all media urls
        $media = [];
        foreach ($post->extended_entities->media AS $medium) {
@@ -1526,8 +1534,12 @@ function twitter_media_entities($post, array &$postarray)
                                }
 
                                $postarray['object-type'] = Activity\ObjectType::IMAGE;
+                               $postarray['post-type'] = Item::PT_IMAGE;
                                break;
                        case 'video':
+                               // Currently deactivated, since this causes the video to be display before the content
+                               // We have to figure out a better way for declaring the post type and the display style.
+                               //$postarray['post-type'] = Item::PT_VIDEO;
                        case 'animated_gif':
                                if (!empty($medium->ext_alt_text)) {
                                        Logger::info('Got text description', ['alt_text' => $medium->ext_alt_text]);
@@ -1548,18 +1560,20 @@ function twitter_media_entities($post, array &$postarray)
                                        }
                                }
                                break;
-                       // The following code will only be activated for test reasons
-                       //default:
-                       //      $postarray['body'] .= print_r($medium, true);
                }
        }
 
+       if ($uriid != -1) {
+               foreach ($media AS $key => $value) {
+                       $postarray['body'] = str_replace($key, '', $postarray['body']);
+               }
+               return;
+       }
+
        // Now we replace the media urls.
        foreach ($media AS $key => $value) {
                $postarray['body'] = str_replace($key, "\n" . $value . "\n", $postarray['body']);
        }
-
-       return '';
 }
 
 /**
@@ -1584,13 +1598,14 @@ function twitter_createpost(App $a, $uid, $post, array $self, $create_user, $onl
        $postarray['uri'] = "twitter::" . $post->id_str;
        $postarray['protocol'] = Conversation::PARCEL_TWITTER;
        $postarray['source'] = json_encode($post);
+       $postarray['direction'] = Conversation::PULL;
 
        if (empty($uriid)) {
                $uriid = $postarray['uri-id'] = ItemURI::insert(['uri' => $postarray['uri']]);
        }
 
        // Don't import our own comments
-       if (Item::exists(['extid' => $postarray['uri'], 'uid' => $uid])) {
+       if (Post::exists(['extid' => $postarray['uri'], 'uid' => $uid])) {
                Logger::info('Item found', ['extid' => $postarray['uri']]);
                return [];
        }
@@ -1600,9 +1615,9 @@ function twitter_createpost(App $a, $uid, $post, array $self, $create_user, $onl
        if ($post->in_reply_to_status_id_str != "") {
                $thr_parent = "twitter::" . $post->in_reply_to_status_id_str;
 
-               $item = Item::selectFirst(['uri'], ['uri' => $thr_parent, 'uid' => $uid]);
+               $item = Post::selectFirst(['uri'], ['uri' => $thr_parent, 'uid' => $uid]);
                if (!DBA::isResult($item)) {
-                       $item = Item::selectFirst(['uri'], ['extid' => $thr_parent, 'uid' => $uid]);
+                       $item = Post::selectFirst(['uri'], ['extid' => $thr_parent, 'uid' => $uid]);
                }
 
                if (DBA::isResult($item)) {
@@ -1661,10 +1676,10 @@ function twitter_createpost(App $a, $uid, $post, array $self, $create_user, $onl
        $postarray['app'] = strip_tags($post->source);
 
        if ($post->user->protected) {
-               $postarray['private'] = 1;
+               $postarray['private'] = Item::PRIVATE;
                $postarray['allow_cid'] = '<' . $self['id'] . '>';
        } else {
-               $postarray['private'] = 0;
+               $postarray['private'] = Item::UNLISTED;
                $postarray['allow_cid'] = '';
        }
 
@@ -1680,19 +1695,24 @@ function twitter_createpost(App $a, $uid, $post, array $self, $create_user, $onl
        }
 
        // Search for media links
-       $picture = twitter_media_entities($post, $postarray);
+       twitter_media_entities($post, $postarray, $uriid);
+
+       $converted = twitter_expand_entities($postarray['body'], $post);
+
+       // When the post contains external links then images or videos are just "decorations".
+       if (!empty($converted['urls'])) {
+               $postarray['post-type'] = Item::PT_NOTE;
+       }
 
-       $converted = twitter_expand_entities($postarray['body'], $post, $picture);
        $postarray['body'] = $converted['body'];
        $postarray['created'] = DateTimeFormat::utc($post->created_at);
        $postarray['edited'] = DateTimeFormat::utc($post->created_at);
 
        if ($uriid > 0) {
                twitter_store_tags($uriid, $converted['taglist']);
+               twitter_store_attachments($uriid, $post);
        }
 
-       $statustext = $converted["plain"];
-
        if (!empty($post->place->name)) {
                $postarray["location"] = $post->place->name;
        }
@@ -1724,6 +1744,7 @@ function twitter_createpost(App $a, $uid, $post, array $self, $create_user, $onl
                        $postarray['thr-parent'] = $retweet['uri'];
                } else {
                        $retweet['source'] = $postarray['source'];
+                       $retweet['direction'] = $postarray['direction'];
                        $retweet['private'] = $postarray['private'];
                        $retweet['allow_cid'] = $postarray['allow_cid'];
                        $retweet['contact-id'] = $postarray['contact-id'];
@@ -1740,14 +1761,19 @@ function twitter_createpost(App $a, $uid, $post, array $self, $create_user, $onl
                        // To avoid recursive share blocks we just provide the link to avoid removing quote context.
                        $postarray['body'] .= "\n\nhttps://twitter.com/" . $post->quoted_status->user->screen_name . "/status/" . $post->quoted_status->id_str;
                } else {
-                       $quoted = twitter_createpost($a, $uid, $post->quoted_status, $self, false, false, true, $uriid);
+                       $quoted = twitter_createpost($a, 0, $post->quoted_status, $self, false, false, true);
                        if (!empty($quoted['body'])) {
+                               Item::insert($quoted);
+                               $post = Post::selectFirst(['guid', 'uri-id'], ['uri' => $quoted['uri'], 'uid' => 0]);
+                               Logger::info('Stored quoted post', ['uid' => $uid, 'uri-id' => $uriid, 'post' => $post]);
+
                                $postarray['body'] .= "\n" . BBCode::getShareOpeningTag(
                                                $quoted['author-name'],
                                                $quoted['author-link'],
                                                $quoted['author-avatar'],
                                                $quoted['plink'],
-                                               $quoted['created']
+                                               $quoted['created'],
+                                               $post['guid'] ?? ''
                                        );
 
                                $postarray['body'] .= $quoted['body'] . '[/share]';
@@ -1781,26 +1807,24 @@ function twitter_fetchparentposts(App $a, $uid, $post, TwitterOAuth $connection,
        $posts = [];
 
        while (!empty($post->in_reply_to_status_id_str)) {
-               $parameters = ["trim_user" => false, "tweet_mode" => "extended", "id" => $post->in_reply_to_status_id_str, "include_ext_alt_text" => true];
-
                try {
-                       $post = $connection->get('statuses/show', $parameters);
+                       $post = twitter_statuses_show($post->in_reply_to_status_id_str, $connection);
                } catch (TwitterOAuthException $e) {
                        Logger::warning('Error fetching parent post', ['uid' => $uid, 'post' => $post->id_str, 'message' => $e->getMessage()]);
                        break;
                }
 
                if (empty($post)) {
-                       Logger::log("twitter_fetchparentposts: Can't fetch post " . $parameters['id'], Logger::DEBUG);
+                       Logger::info("twitter_fetchparentposts: Can't fetch post");
                        break;
                }
 
                if (empty($post->id_str)) {
-                       Logger::log("twitter_fetchparentposts: This is not a post " . json_encode($post), Logger::DEBUG);
+                       Logger::info("twitter_fetchparentposts: This is not a post", ['post' => $post]);
                        break;
                }
 
-               if (Item::exists(['uri' => 'twitter::' . $post->id_str, 'uid' => $uid])) {
+               if (Post::exists(['uri' => 'twitter::' . $post->id_str, 'uid' => $uid])) {
                        break;
                }