Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved handling of native quotes #12076

Merged
merged 6 commits into from
Oct 30, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 8 additions & 2 deletions mod/item.php
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,8 @@ function item_post(App $a) {
$datarray['author-uri-id'] = ItemURI::getIdByURI($datarray['author-link']);
$datarray['owner-updated'] = '';
$datarray['has-media'] = false;
$datarray['body'] = DI::contentItem()->improveSharedDataInBody($datarray);
$datarray['quote-uri-id'] = Item::getQuoteUriId($datarray['body'], $datarray['uid']);
$datarray['body'] = BBCode::removeSharedData($datarray['body']);

$o = DI::conversation()->create([array_merge($contact_record, $datarray)], 'search', false, true);

Expand Down Expand Up @@ -652,7 +653,12 @@ function item_post(App $a) {
}

$datarray['uri-id'] = ItemURI::getIdByURI($datarray['uri']);
$datarray['body'] = DI::contentItem()->improveSharedDataInBody($datarray);

$quote_uri_id = Item::getQuoteUriId($datarray['body'], $datarray['uid']);
if (!empty($quote_uri_id)) {
$datarray['quote-uri-id'] = $quote_uri_id;
$datarray['body'] = BBCode::removeSharedData($datarray['body']);
}

if ($orig_post) {
$fields = [
Expand Down
121 changes: 39 additions & 82 deletions src/Content/Item.php
Original file line number Diff line number Diff line change
Expand Up @@ -574,57 +574,30 @@ public function getOwnerAvatar(array $item): string
}

/**
* Add a share block for the given url
* Add a share block for the given uri-id
*
* @param string $url
* @param integer $uid
* @param bool $add_media
* @param array $item
* @param string $body
* @return string
*/
public function createSharedPostByUrl(string $url, int $uid = 0, bool $add_media = false): string
public function addSharedPost(array $item, string $body = ''): 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 '';
if (empty($body)) {
$body = $item['body'];
}

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 '';
if (empty($item['quote-uri-id'])) {
return $body;
}

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]]);
$shared_item = Post::selectFirst($fields, ['uri-id' => $item['quote-uri-id'], 'uid' => [$item['uid'], 0], 'private' => [ItemModel::PUBLIC, ItemModel::UNLISTED]]);
if (!DBA::isResult($shared_item)) {
Logger::notice('Post does not exist.', ['uri-id' => $UriId, 'uid' => $uid]);
return '';
Logger::notice('Post does not exist.', ['uri-id' => $item['quote-uri-id'], 'uid' => $item['uid']]);
return $body;
}

return $this->createSharedBlockByArray($shared_item, $add_media);
return $body . "\n" . $this->createSharedBlockByArray($shared_item, true);
}

/**
Expand All @@ -635,20 +608,13 @@ public function createSharedPostByUriId(int $UriId, int $uid = 0, bool $add_medi
* @param bool $add_media
* @return string
*/
public function createSharedPostByGuid(string $guid, int $uid = 0, string $host = '', bool $add_media = false): string
private function createSharedPostByGuid(string $guid, bool $add_media): 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]);
}
$shared_item = Post::selectFirst($fields, ['guid' => $guid, 'uid' => 0, 'private' => [ItemModel::PUBLIC, ItemModel::UNLISTED]]);

if (!DBA::isResult($shared_item)) {
Logger::notice('Post does not exist.', ['guid' => $guid, 'host' => $host, 'uid' => $uid]);
Logger::notice('Post does not exist.', ['guid' => $guid]);
return '';
}

Expand All @@ -669,8 +635,9 @@ public function createSharedBlockByArray(array $item, bool $add_media = false):
} elseif (!in_array($item['network'] ?? '', Protocol::FEDERATED)) {
$item['guid'] = '';
$item['uri'] = '';
$item['body'] = Post\Media::addAttachmentsToBody($item['uri-id'], $item['body']);
} elseif ($add_media) {
}

if ($add_media) {
$item['body'] = Post\Media::addAttachmentsToBody($item['uri-id'], $item['body']);
}

Expand All @@ -684,7 +651,7 @@ public function createSharedBlockByArray(array $item, bool $add_media = false):

// If it is a reshared post then reformat it to avoid display problems with two share elements
if (!empty($shared)) {
if (!empty($shared['guid']) && ($encaspulated_share = $this->createSharedPostByGuid($shared['guid'], 0, '', $add_media))) {
if (!empty($shared['guid']) && ($encaspulated_share = $this->createSharedPostByGuid($shared['guid'], $add_media))) {
$item['body'] = preg_replace("/\[share.*?\](.*)\[\/share\]/ism", $encaspulated_share, $item['body']);
}

Expand Down Expand Up @@ -730,36 +697,6 @@ public function getSharedPost(array $item, array $fields = []): array
return [];
}

/**
* Improve the data in shared posts
*
* @param array $item
* @param bool $add_media
* @return string body
*/
public function improveSharedDataInBody(array $item, bool $add_media = false): string
{
$shared = BBCode::fetchShareAttributes($item['body']);
if (empty($shared['guid']) && empty($shared['message_id'])) {
return $item['body'];
}

$link = $shared['link'] ?: $shared['message_id'];

if (empty($shared_content)) {
$shared_content = $this->createSharedPostByUrl($link, $item['uid'] ?? 0, $add_media);
}

if (empty($shared_content)) {
return $item['body'];
}

$item['body'] = preg_replace("/\[share.*?\](.*)\[\/share\]/ism", $shared_content, $item['body']);

Logger::debug('New shared data', ['uri-id' => $item['uri-id'], 'link' => $link, 'guid' => $item['guid']]);
return $item['body'];
}

/**
* Return share data from an item array (if the item is shared item)
* We are providing the complete Item array, because at some time in the future
Expand Down Expand Up @@ -796,4 +733,24 @@ private function getShareArray(array $item): array

return [];
}

/**
* Add a link to a shared post at the end of the post
*
* @param string $body
* @param integer $quote_uri_id
* @return string
*/
public function addShareLink(string $body, int $quote_uri_id): string
{
$post = Post::selectFirstPost(['uri', 'plink'], ['uri-id' => $quote_uri_id]);
if (empty($post)) {
return $body;
}

$link = $post['plink'] ?: $post['uri'];
$body .= "\n♲ " . $link;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Energy is expensive, let's save the creation of an intermediary variable.

Suggested change
$link = $post['plink'] ?: $post['uri'];
$body .= "\n" . $link;
$body .= "\n" . ($post['plink'] ?: $post['uri']);


return $body;
}
}
42 changes: 1 addition & 41 deletions src/Content/Text/BBCode.php
Original file line number Diff line number Diff line change
Expand Up @@ -1070,46 +1070,6 @@ private static function extractShareAttributes(string $shareString): array
return $attributes;
}

/**
* Checks, if the provided body contains a native reshare
*
* @param string $body
* @return boolean
*/
public static function isNativeReshare(string $body): bool
{
$shared = BBCode::fetchShareAttributes($body);
return !empty($shared['guid'] ?? '');
}

/**
* Checks if the provided body contains a "share" element
*
* @param string $body
* @return boolean
*/
public static function existsShare(string $body): bool
{
$shared = BBCode::fetchShareAttributes($body);
return !empty($shared['link'] ?? '');
}

/**
* Replace the share block with a link
*
* @param string $body
* @return string
*/
public static function replaceSharedData(string $body): string
{
return BBCode::convertShare(
$body,
function (array $attributes) {
return '♲ ' . $attributes['link'];
}
);
}

/**
* Remove the share block
*
Expand All @@ -1118,7 +1078,7 @@ function (array $attributes) {
*/
public static function removeSharedData(string $body): string
{
return trim(preg_replace("/\s*\[share .*?\].*?\[\/share\]\s*/ism", '', $body));
return trim(preg_replace("/\s*\[share.*?\].*?\[\/share\]\s*/ism", '', $body));
}

/**
Expand Down
84 changes: 72 additions & 12 deletions src/Model/Item.php
Original file line number Diff line number Diff line change
Expand Up @@ -197,12 +197,20 @@ public static function update(array $fields, array $condition)

Logger::info('Updating per single row method', ['fields' => $fields, 'condition' => $condition]);

$items = Post::select(['id', 'origin', 'uri-id', 'uid', 'author-network'], $condition);
$items = Post::select(['id', 'origin', 'uri-id', 'uid', 'author-network', 'quote-uri-id'], $condition);

$notify_items = [];

while ($item = DBA::fetch($items)) {
if (!empty($fields['body'])) {
if (!empty($item['quote-uri-id'])) {
$fields['body'] = BBCode::removeSharedData($fields['body']);

if (!empty($fields['raw-body'])) {
$fields['raw-body'] = BBCode::removeSharedData($fields['raw-body']);
}
}

Post\Media::insertFromAttachmentData($item['uri-id'], $fields['body']);

$content_fields = ['raw-body' => trim($fields['raw-body'] ?? $fields['body'])];
Expand Down Expand Up @@ -578,7 +586,7 @@ private static function isDuplicate(array $item): bool
public static function isValid(array $item): bool
{
// When there is no content then we don't post it
if (($item['body'] . $item['title'] == '') && (empty($item['uri-id']) || !Post\Media::existsByURIId($item['uri-id']))) {
if (($item['body'] . $item['title'] == '') && empty($item['quote-uri-id']) && (empty($item['uri-id']) || !Post\Media::existsByURIId($item['uri-id']))) {
Logger::notice('No body, no title.');
return false;
}
Expand Down Expand Up @@ -1119,19 +1127,27 @@ public static function insert(array $item, int $notify = 0, bool $post_local = t
unset($item['attachments']);
}

if (empty($item['quote-uri-id'])) {
$quote_id = self::getQuoteUriId($item['body']);
if (!empty($quote_id)) {
// This is one of these "should not happen" situations.
// The protocol implementations should already have done this job.
Logger::notice('Quote-uri-id detected in post', ['id' => $quote_id, 'guid' => $item['guid'], 'uri-id' => $item['uri-id'], 'callstack' => System::callstack(20)]);
$item['quote-uri-id'] = $quote_id;
}
}

if (!empty($item['quote-uri-id'])) {
$item['raw-body'] = BBCode::removeSharedData($item['raw-body']);
$item['body'] = BBCode::removeSharedData($item['body']);
}

Post\Media::insertFromAttachmentData($item['uri-id'], $item['body']);

// Remove all media attachments from the body and store them in the post-media table
$item['raw-body'] = Post\Media::insertFromBody($item['uri-id'], $item['raw-body']);
$item['raw-body'] = self::setHashtags($item['raw-body']);

$quote_id = self::getQuoteUriId($item['body']);

if (!empty($quote_id) && Post::exists(['uri-id' => $quote_id, 'network' => Protocol::FEDERATED])) {
$item['quote-uri-id'] = $quote_id;
$item['raw-body'] = BBCode::removeSharedData($item['raw-body']);
}

if (!DBA::exists('contact', ['id' => $item['author-id'], 'network' => Protocol::DFRN])) {
Post\Media::insertFromRelevantUrl($item['uri-id'], $item['raw-body']);
}
Expand Down Expand Up @@ -3630,12 +3646,56 @@ protected static function isAllowedByUser(array $item, int $user_id): bool
* @param string $body
* @return integer
*/
private static function getQuoteUriId(string $body): int
public static function getQuoteUriId(string $body, int $uid = 0): int
{
$shared = BBCode::fetchShareAttributes($body);
if (empty($shared['message_id'])) {
if (empty($shared['guid']) && empty($shared['message_id'])) {
return 0;
}
return ItemURI::getIdByURI($shared['message_id']);

if (empty($shared['link']) && empty($shared['message_id'])) {
Logger::notice('Invalid share block.', ['share' => $shared]);
return 0;
}

if (!empty($shared['guid'])) {
$shared_item = Post::selectFirst(['uri-id'], ['guid' => $shared['guid'], 'uid' => [0, $uid]]);
if (!empty($shared_item['uri-id'])) {
Logger::debug('Found post by guid', ['guid' => $shared['guid'], 'uid' => $uid]);
}
}

if (empty($shared_item['uri-id']) && !empty($shared['message_id'])) {
$shared_item = Post::selectFirst(['uri-id'], ['uri' => $shared['message_id'], 'uid' => [0, $uid]]);
if (!empty($shared_item['uri-id'])) {
Logger::debug('Found post by message_id', ['message_id' => $shared['message_id'], 'uid' => $uid]);
}
}

if (empty($shared_item['uri-id']) && !empty($shared['link'])) {
$shared_item = Post::selectFirst(['uri-id'], ['plink' => $shared['link'], 'uid' => [0, $uid]]);
if (!empty($shared_item['uri-id'])) {
Logger::debug('Found post by link', ['link' => $shared['link'], 'uid' => $uid]);
}
}

if (empty($shared_item['uri-id'])) {
$url = $shared['message_id'] ?: $shared['link'];
$id = self::fetchByLink($url);
if (!$id) {
Logger::notice('Post could not be fetched.', ['url' => $url, 'uid' => $uid]);
return 0;
}

Logger::debug('Fetched shared post', ['id' => $id, 'url' => $url, 'uid' => $uid]);

$shared_item = Post::selectFirst(['uri-id'], ['id' => $id]);
if (!DBA::isResult($shared_item)) {
Logger::warning('Post does not exist.', ['id' => $id, 'url' => $url, 'uid' => $uid]);
return 0;
}
}

return $shared_item['uri-id'];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather we used early returns than the repetitive empty($shared_item['uri-id']) condition which means something different every time.

Suggested change
if (!empty($shared['guid'])) {
$shared_item = Post::selectFirst(['uri-id'], ['guid' => $shared['guid'], 'uid' => [0, $uid]]);
if (!empty($shared_item['uri-id'])) {
Logger::debug('Found post by guid', ['guid' => $shared['guid'], 'uid' => $uid]);
}
}
if (empty($shared_item['uri-id']) && !empty($shared['message_id'])) {
$shared_item = Post::selectFirst(['uri-id'], ['uri' => $shared['message_id'], 'uid' => [0, $uid]]);
if (!empty($shared_item['uri-id'])) {
Logger::debug('Found post by message_id', ['message_id' => $shared['message_id'], 'uid' => $uid]);
}
}
if (empty($shared_item['uri-id']) && !empty($shared['link'])) {
$shared_item = Post::selectFirst(['uri-id'], ['plink' => $shared['link'], 'uid' => [0, $uid]]);
if (!empty($shared_item['uri-id'])) {
Logger::debug('Found post by link', ['link' => $shared['link'], 'uid' => $uid]);
}
}
if (empty($shared_item['uri-id'])) {
$url = $shared['message_id'] ?: $shared['link'];
$id = self::fetchByLink($url);
if (!$id) {
Logger::notice('Post could not be fetched.', ['url' => $url, 'uid' => $uid]);
return 0;
}
Logger::debug('Fetched shared post', ['id' => $id, 'url' => $url, 'uid' => $uid]);
$shared_item = Post::selectFirst(['uri-id'], ['id' => $id]);
if (!DBA::isResult($shared_item)) {
Logger::warning('Post does not exist.', ['id' => $id, 'url' => $url, 'uid' => $uid]);
return 0;
}
}
return $shared_item['uri-id'];
if (!empty($shared['guid'])) {
$shared_item = Post::selectFirst(['uri-id'], ['guid' => $shared['guid'], 'uid' => [0, $uid]]);
if (!empty($shared_item['uri-id'])) {
Logger::debug('Found post by guid', ['guid' => $shared['guid'], 'uid' => $uid]);
return $shared_item['uri-id'];
}
}
if (!empty($shared['message_id'])) {
$shared_item = Post::selectFirst(['uri-id'], ['uri' => $shared['message_id'], 'uid' => [0, $uid]]);
if (!empty($shared_item['uri-id'])) {
Logger::debug('Found post by message_id', ['message_id' => $shared['message_id'], 'uid' => $uid]);
return $shared_item['uri-id'];
}
}
if (!empty($shared['link'])) {
$shared_item = Post::selectFirst(['uri-id'], ['plink' => $shared['link'], 'uid' => [0, $uid]]);
if (!empty($shared_item['uri-id'])) {
Logger::debug('Found post by link', ['link' => $shared['link'], 'uid' => $uid]);
return $shared_item['uri-id'];
}
}
$url = $shared['message_id'] ?: $shared['link'];
$id = self::fetchByLink($url);
if (!$id) {
Logger::notice('Post could not be fetched.', ['url' => $url, 'uid' => $uid]);
return 0;
}
Logger::debug('Fetched shared post', ['id' => $id, 'url' => $url, 'uid' => $uid]);
$shared_item = Post::selectFirst(['uri-id'], ['id' => $id]);
if (!DBA::isResult($shared_item)) {
Logger::warning('Post does not exist.', ['id' => $id, 'url' => $url, 'uid' => $uid]);
return 0;
}
return $shared_item['uri-id'];

}
}