X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FContent%2FItem.php;h=b363ad982bfefcf498a7ec10e3d9d73dc41b804d;hb=99d54410077f210b54f88d3c6c46d7a48a14b619;hp=f6906b79629b142417ca4c77a375d85c09bdca32;hpb=5539e427434f46f362da1e31cad56395a101229c;p=friendica.git diff --git a/src/Content/Item.php b/src/Content/Item.php index f6906b7962..b363ad982b 100644 --- a/src/Content/Item.php +++ b/src/Content/Item.php @@ -22,20 +22,23 @@ namespace Friendica\Content; use Friendica\Content\Text\BBCode; +use Friendica\Content\Text\HTML; use Friendica\Core\Hook; use Friendica\Core\L10n; use Friendica\Core\Logger; use Friendica\Core\Protocol; -use Friendica\Core\Session; +use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\Model\Contact; use Friendica\Model\Group; -use Friendica\Model\Item as ModelItem; +use Friendica\Model\Item as ItemModel; +use Friendica\Model\Photo; use Friendica\Model\Tag; use Friendica\Model\Post; use Friendica\Protocol\Activity; +use Friendica\Protocol\Diaspora; use Friendica\Util\Profiler; -use Friendica\Util\Strings; +use Friendica\Util\Proxy; use Friendica\Util\XML; /** @@ -85,7 +88,7 @@ class Item * ] * ] */ - public function determineCategoriesTerms(array $item, int $uid = 0) + public function determineCategoriesTerms(array $item, int $uid = 0): array { $categories = []; $folders = []; @@ -93,6 +96,10 @@ class Item $uid = $item['uid'] ?: $uid; + if (empty($item['has-categories'])) { + return [$categories, $folders]; + } + foreach (Post\Category::getArrayByURIId($item['uri-id'], $uid, Post\Category::CATEGORY) as $savedFolderName) { if (!empty($item['author-link'])) { $url = $item['author-link'] . "?category=" . rawurlencode($savedFolderName); @@ -137,16 +144,16 @@ class Item * This function removes the tag $tag from the text $body and replaces it with * the appropriate link. * - * @param string $body the text to replace the tag in - * @param integer $profile_uid the user id to replace the tag for (0 = anyone) - * @param string $tag the tag to replace - * @param string $network The network of the post + * @param string $body the text to replace the tag in + * @param int $profile_uid the user id to replace the tag for (0 = anyone) + * @param string $tag the tag to replace + * @param string $network The network of the post * - * @return array|bool ['replaced' => $replaced, 'contact' => $contact]; + * @return array|bool ['replaced' => $replaced, 'contact' => $contact] or "false" on if already replaced * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - public static function replaceTag(&$body, $profile_uid, $tag, $network = '') + public static function replaceTag(string &$body, int $profile_uid, string $tag, string $network = '') { $replaced = false; @@ -240,71 +247,17 @@ class Item /** * Render actions localized * - * @param $item + * @param array $item + * @return void * @throws ImagickException * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public function localize(&$item) + public function localize(array &$item) { $this->profiler->startRecording('rendering'); /// @todo The following functionality needs to be cleaned up. if (!empty($item['verb'])) { - $xmlhead = "<" . "?xml version='1.0' encoding='UTF-8' ?" . ">"; - - if (stristr($item['verb'], Activity::POKE)) { - $verb = urldecode(substr($item['verb'], strpos($item['verb'],'#') + 1)); - if (!$verb) { - $this->profiler->stopRecording(); - return; - } - if ($item['object-type'] == "" || $item['object-type'] !== Activity\ObjectType::PERSON) { - $this->profiler->stopRecording(); - return; - } - - $obj = XML::parseString($xmlhead . $item['object']); - - $Bname = $obj->title; - $Blink = $obj->id; - $Bphoto = ""; - - foreach ($obj->link as $l) { - $atts = $l->attributes(); - switch ($atts['rel']) { - case "alternate": $Blink = $atts['href']; - case "photo": $Bphoto = $atts['href']; - } - } - - $author = ['uid' => 0, 'id' => $item['author-id'], - 'network' => $item['author-network'], 'url' => $item['author-link']]; - $A = '[url=' . Contact::magicLinkByContact($author) . ']' . $item['author-name'] . '[/url]'; - - if (!empty($Blink)) { - $B = '[url=' . Contact::magicLink($Blink) . ']' . $Bname . '[/url]'; - } else { - $B = ''; - } - - if ($Bphoto != "" && !empty($Blink)) { - $Bphoto = '[url=' . Contact::magicLink($Blink) . '][img=80x80]' . $Bphoto . '[/img][/url]'; - } - - /* - * we can't have a translation string with three positions but no distinguishable text - * So here is the translate string. - */ - $txt = $this->l10n->t('%1$s poked %2$s'); - - // now translate the verb - $poked_t = trim(sprintf($txt, '', '')); - $txt = str_replace($poked_t, $this->l10n->t($verb), $txt); - - // then do the sprintf on the translation string - - $item['body'] = sprintf($txt, $A, $B) . "\n\n\n" . $Bphoto; - - } + $xmlhead = ''; if ($this->activity->match($item['verb'], Activity::TAG)) { $fields = ['author-id', 'author-link', 'author-name', 'author-network', @@ -315,12 +268,20 @@ class Item return; } - $author_arr = ['uid' => 0, 'id' => $item['author-id'], - 'network' => $item['author-network'], 'url' => $item['author-link']]; + $author_arr = [ + 'uid' => 0, + 'id' => $item['author-id'], + 'network' => $item['author-network'], + 'url' => $item['author-link'], + ]; $author = '[url=' . Contact::magicLinkByContact($author_arr) . ']' . $item['author-name'] . '[/url]'; - $author_arr = ['uid' => 0, 'id' => $obj['author-id'], - 'network' => $obj['author-network'], 'url' => $obj['author-link']]; + $author_arr = [ + 'uid' => 0, + 'id' => $obj['author-id'], + 'network' => $obj['author-network'], + 'url' => $obj['author-link'], + ]; $objauthor = '[url=' . Contact::magicLinkByContact($author_arr) . ']' . $obj['author-name'] . '[/url]'; switch ($obj['verb']) { @@ -333,6 +294,7 @@ class Item $post_type = $this->l10n->t('status'); } break; + default: if ($obj['resource-id']) { $post_type = $this->l10n->t('photo'); @@ -353,44 +315,32 @@ class Item } } - $matches = null; - if (preg_match_all('/@\[url=(.*?)\]/is', $item['body'], $matches, PREG_SET_ORDER)) { - foreach ($matches as $mtch) { - if (!strpos($mtch[1], 'zrl=')) { - $item['body'] = str_replace($mtch[0], '@[url=' . Contact::magicLink($mtch[1]) . ']', $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() && $item['private'] == ModelItem::PRIVATE) { - $author = ['uid' => 0, 'id' => $item['author-id'], - 'network' => $item['author-network'], 'url' => $item['author-link']]; - $item['plink'] = Contact::magicLinkByContact($author, $item['plink']); - } $this->profiler->stopRecording(); } - public function photoMenu($item, string $formSecurityToken) + /** + * Renders photo menu based on item + * + * @param array $item + * @param string $formSecurityToken + * @return string + */ + public function photoMenu(array $item, string $formSecurityToken): string { $this->profiler->startRecording('rendering'); - $sub_link = ''; - $poke_link = ''; - $contact_url = ''; - $pm_url = ''; - $status_link = ''; - $photos_link = ''; - $posts_link = ''; - $block_link = ''; - $ignore_link = ''; - - if (local_user() && local_user() == $item['uid'] && $item['gravity'] == GRAVITY_PARENT && !$item['self'] && !$item['mention']) { + $sub_link = $contact_url = $pm_url = $status_link = ''; + $photos_link = $posts_link = $block_link = $ignore_link = ''; + + if (local_user() && local_user() == $item['uid'] && $item['gravity'] == ItemModel::GRAVITY_PARENT && !$item['self'] && !$item['mention']) { $sub_link = 'javascript:doFollowThread(' . $item['id'] . '); return false;'; } - $author = ['uid' => 0, 'id' => $item['author-id'], - 'network' => $item['author-network'], 'url' => $item['author-link']]; + $author = [ + 'uid' => 0, + 'id' => $item['author-id'], + 'network' => $item['author-network'], + 'url' => $item['author-link'], + ]; $profile_link = Contact::magicLinkByContact($author, $item['author-link']); $sparkle = (strpos($profile_link, 'redir/') === 0); @@ -398,7 +348,7 @@ class Item $pcid = $item['author-id']; $network = ''; $rel = 0; - $condition = ['uid' => local_user(), 'nurl' => Strings::normaliseLink($item['author-link'])]; + $condition = ['uid' => local_user(), 'uri-id' => $item['author-uri-id']]; $contact = DBA::selectFirst('contact', ['id', 'network', 'rel'], $condition); if (DBA::isResult($contact)) { $cid = $contact['id']; @@ -421,7 +371,6 @@ class Item if ($cid && !$item['self']) { $contact_url = 'contact/' . $cid; - $poke_link = $contact_url . '/poke'; $posts_link = $contact_url . '/posts'; if (in_array($network, [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA])) { @@ -443,11 +392,7 @@ class Item ]; if (!empty($item['language'])) { - $menu[$this->l10n->t('Languages')] = 'javascript:alert(\'' . ModelItem::getLanguageMessage($item) . '\');'; - } - - if ($network == Protocol::DFRN) { - $menu[$this->l10n->t("Poke")] = $poke_link; + $menu[$this->l10n->t('Languages')] = 'javascript:alert(\'' . ItemModel::getLanguageMessage($item) . '\');'; } if ((($cid == 0) || ($rel == Contact::FOLLOWER)) && @@ -477,24 +422,28 @@ class Item return $o; } - public function visibleActivity($item) { - + /** + * Checks if the activity is visible to current user + * + * @param array $item Activity item + * @return bool Whether the item is visible to the user + */ + public function isVisibleActivity(array $item): bool + { + // Empty verb or hidden? if (empty($item['verb']) || $this->activity->isHidden($item['verb'])) { return false; } - // @TODO below if() block can be rewritten to a single line: $isVisible = allConditionsHere; - if ($this->activity->match($item['verb'], Activity::FOLLOW) && + // Check conditions + return (!($this->activity->match($item['verb'], Activity::FOLLOW) && $item['object-type'] === Activity\ObjectType::NOTE && empty($item['self']) && - $item['uid'] == local_user()) { - return false; - } - - return true; + $item['uid'] == local_user()) + ); } - public function expandTags(array $item, bool $setPermissions = false) + public function expandTags(array $item, bool $setPermissions = false): array { // Look for any tags and linkify them $item['inform'] = ''; @@ -510,6 +459,9 @@ class Item // Search for forum mentions foreach (Tag::getFromBody($item['body'], Tag::TAG_CHARACTER[Tag::MENTION] . Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION]) as $tag) { $contact = Contact::getByURLForUser($tag[2], $item['uid']); + if (empty($contact)) { + continue; + } $receivers[] = $contact['id']; @@ -518,7 +470,7 @@ class Item } $item['inform'] .= 'cid:' . $contact['id']; - if (($item['gravity'] == GRAVITY_COMMENT) || empty($contact['cid']) || ($contact['contact-type'] != Contact::TYPE_COMMUNITY)) { + if (($item['gravity'] == ItemModel::GRAVITY_COMMENT) || empty($contact['cid']) || ($contact['contact-type'] != Contact::TYPE_COMMUNITY)) { continue; } @@ -540,9 +492,9 @@ class Item } Logger::info('Got inform', ['inform' => $item['inform']]); - if (($item['gravity'] == GRAVITY_PARENT) && !empty($forum_contact) && ($private_forum || $only_to_forum)) { + if (($item['gravity'] == ItemModel::GRAVITY_PARENT) && !empty($forum_contact) && ($private_forum || $only_to_forum)) { // we tagged a forum in a top level post. Now we change the post - $item['private'] = $private_forum ? ModelItem::PRIVATE : ModelItem::UNLISTED; + $item['private'] = $private_forum ? ItemModel::PRIVATE : ItemModel::UNLISTED; if ($only_to_forum) { $item['postopts'] = ''; @@ -558,14 +510,14 @@ class Item $item['allow_cid'] = ''; $item['allow_gid'] = ''; } - } elseif ($setPermissions && ($item['gravity'] == GRAVITY_PARENT)) { + } elseif ($setPermissions && ($item['gravity'] == ItemModel::GRAVITY_PARENT)) { if (empty($receivers)) { // For security reasons direct posts without any receiver will be posts to yourself $self = Contact::selectFirst(['id'], ['uid' => $item['uid'], 'self' => true]); $receivers[] = $self['id']; } - $item['private'] = ModelItem::PRIVATE; + $item['private'] = ItemModel::PRIVATE; $item['allow_cid'] = ''; $item['allow_gid'] = ''; $item['deny_cid'] = ''; @@ -577,4 +529,166 @@ class Item } return $item; } + + public function getAuthorAvatar(array $item): string + { + if (in_array($item['network'], [Protocol::FEED, Protocol::MAIL])) { + $author_avatar = $item['contact-id']; + $author_updated = ''; + $author_thumb = $item['contact-avatar']; + } else { + $author_avatar = $item['author-id']; + $author_updated = $item['author-updated']; + $author_thumb = $item['author-avatar']; + } + + + if (empty($author_thumb) || Photo::isPhotoURI($author_thumb)) { + $author_thumb = Contact::getAvatarUrlForId($author_avatar, Proxy::SIZE_THUMB, $author_updated); + } + + return $author_thumb; + } + + public function getOwnerAvatar(array $item): string + { + if (in_array($item['network'], [Protocol::FEED, Protocol::MAIL])) { + $owner_avatar = $item['contact-id']; + $owner_updated = ''; + $owner_thumb = $item['contact-avatar']; + } else { + $owner_avatar = $item['owner-id']; + $owner_updated = $item['owner-updated']; + $owner_thumb = $item['owner-avatar']; + } + + if (empty($owner_thumb) || Photo::isPhotoURI($owner_thumb)) { + $owner_thumb = Contact::getAvatarUrlForId($owner_avatar, Proxy::SIZE_THUMB, $owner_updated); + } + + return $owner_thumb; + } + + /** + * Add a share block for the given url + * + * @param string $url + * @param integer $uid + * @param bool $add_media + * @return string + */ + public function createSharedPostByUrl(string $url, int $uid = 0, bool $add_media = false): string + { + if (!empty($uid)) { + $id = ItemModel::searchByLink($url, $uid); + } + + if (empty($id)) { + $id = ItemModel::fetchByLink($url); + } + + if (!$id) { + Logger::notice('Post could not be fetched.', ['url' => $url, 'uid' => $uid, 'callstack' => System::callstack()]); + return ''; + } + + Logger::debug('Fetched shared post', ['id' => $id, 'url' => $url, 'uid' => $uid, 'callstack' => System::callstack()]); + + $shared_item = Post::selectFirst(['uri-id', 'uri', 'body', 'title', 'author-name', 'author-link', 'author-avatar', 'guid', 'created', 'plink', 'network'], ['id' => $id]); + if (!DBA::isResult($shared_item)) { + Logger::warning('Post does not exist.', ['id' => $id, 'url' => $url, 'uid' => $uid]); + return ''; + } + + return $this->createSharedBlockByArray($shared_item, $add_media); + } + + /** + * Add a share block for the given uri-id + * + * @param integer $UriId + * @param integer $uid + * @param bool $add_media + * @return string + */ + public function createSharedPostByUriId(int $UriId, int $uid = 0, bool $add_media = false): string + { + $fields = ['uri-id', 'uri', 'body', 'title', 'author-name', 'author-link', 'author-avatar', 'guid', 'created', 'plink', 'network']; + $shared_item = Post::selectFirst($fields, ['uri-id' => $UriId, 'uid' => [$uid, 0], 'private' => [ItemModel::PUBLIC, ItemModel::UNLISTED]]); + if (!DBA::isResult($shared_item)) { + Logger::notice('Post does not exist.', ['uri-id' => $UriId, 'uid' => $uid]); + return ''; + } + + return $this->createSharedBlockByArray($shared_item, $add_media); + } + + /** + * Add a share block for the given guid + * + * @param string $guid + * @param integer $uid + * @param bool $add_media + * @return string + */ + public function createSharedPostByGuid(string $guid, int $uid = 0, string $host = '', bool $add_media = false): string + { + $fields = ['uri-id', 'uri', 'body', 'title', 'author-name', 'author-link', 'author-avatar', 'guid', 'created', 'plink', 'network']; + $shared_item = Post::selectFirst($fields, ['guid' => $guid, 'uid' => [$uid, 0], 'private' => [ItemModel::PUBLIC, ItemModel::UNLISTED]]); + + if (!DBA::isResult($shared_item) && !empty($host) && Diaspora::storeByGuid($guid, $host, true)) { + Logger::debug('Fetched post', ['guid' => $guid, 'host' => $host, 'uid' => $uid]); + $shared_item = Post::selectFirst($fields, ['guid' => $guid, 'uid' => [$uid, 0], 'private' => [ItemModel::PUBLIC, ItemModel::UNLISTED]]); + } elseif (DBA::isResult($shared_item)) { + Logger::debug('Found existing post', ['guid' => $guid, 'host' => $host, 'uid' => $uid]); + } + + if (!DBA::isResult($shared_item)) { + Logger::notice('Post does not exist.', ['guid' => $guid, 'host' => $host, 'uid' => $uid]); + return ''; + } + + return $this->createSharedBlockByArray($shared_item, $add_media); + } + + /** + * Add a share block for the given item array + * + * @param array $item + * @param bool $add_media + * @return string + */ + public function createSharedBlockByArray(array $item, bool $add_media = false): string + { + if ($item['network'] == Protocol::FEED) { + return PageInfo::getFooterFromUrl($item['plink']); + } elseif (!in_array($item['network'] ?? '', Protocol::FEDERATED)) { + $item['guid'] = ''; + $item['uri'] = ''; + $item['body'] = Post\Media::addAttachmentsToBody($item['uri-id'], $item['body']); + } elseif ($add_media) { + $item['body'] = Post\Media::addAttachmentsToBody($item['uri-id'], $item['body']); + } + + $shared_content = BBCode::getShareOpeningTag($item['author-name'], $item['author-link'], $item['author-avatar'], $item['plink'], $item['created'], $item['guid'], $item['uri']); + + if (!empty($item['title'])) { + $shared_content .= '[h3]' . $item['title'] . "[/h3]\n"; + } + + $shared = BBCode::fetchShareAttributes($item['body']); + + // If it is a reshared post then reformat it to avoid display problems with two share elements + if (Diaspora::isReshare($item['body'], false)) { + if (!empty($shared['guid']) && ($encaspulated_share = self::createSharedPostByGuid($shared['guid'], 0, '', $add_media))) { + $item['body'] = preg_replace("/\[share.*?\](.*)\[\/share\]/ism", $encaspulated_share, $item['body']); + } + + $item['body'] = HTML::toBBCode(BBCode::convertForUriId($item['uri-id'], $item['body'], BBCode::ACTIVITYPUB)); + } + + $shared_content .= $item['body'] . '[/share]'; + + return $shared_content; + } }