*/
public static function insert(array $item, int $notify = 0, bool $post_local = true): int
{
- $itemInserter = new ItemInserter(
+ $itemHelper = new ItemHelper(
DI::contentItem(),
DI::activity(),
DI::logger(),
// If it is a posting where users should get notifications, then define it as wall posting
if ($notify) {
- $item = $itemInserter->prepareOriginPost($item);
+ $item = $itemHelper->prepareOriginPost($item);
if (is_int($notify) && in_array($notify, Worker::PRIORITIES)) {
$priority = $notify;
$item['network'] = trim(($item['network'] ?? '') ?: Protocol::PHANTOM);
}
- $item = $itemInserter->prepareItemData($item, (bool) $notify);
+ $item = $itemHelper->prepareItemData($item, (bool) $notify);
// Store conversation data
$source = $item['source'] ?? '';
}
}
- $item = $itemInserter->validateItemData($item);
+ $item = $itemHelper->validateItemData($item);
// Ensure that there is an avatar cache
Contact::checkAvatarCache($item['author-id']);
}
if ($item['gravity'] !== self::GRAVITY_PARENT) {
- $toplevel_parent = $itemInserter->getTopLevelParent($item);
+ $toplevel_parent = $itemHelper->getTopLevelParent($item);
if (empty($toplevel_parent)) {
return 0;
}
$parent_id = (int) $toplevel_parent['id'];
- $item = $itemInserter->handleToplevelParent($item, $toplevel_parent, $defined_permissions);
+ $item = $itemHelper->handleToplevelParent($item, $toplevel_parent, $defined_permissions);
$parent_origin = $toplevel_parent['origin'];
} else {
$parent_id = 0;
--- /dev/null
+<?php
+
+// Copyright (C) 2010-2024, the Friendica project
+// SPDX-FileCopyrightText: 2010-2024 the Friendica project
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+namespace Friendica\Model;
+
+use Friendica\App\BaseURL;
+use Friendica\Content\Item as ItemContent;
+use Friendica\Core\Protocol;
+use Friendica\Database\Database;
+use Friendica\Protocol\Activity;
+use Friendica\Util\DateTimeFormat;
+use Psr\Log\LoggerInterface;
+
+/**
+ * A helper class for handling an Item Model
+ *
+ * @internal only for use in Friendica\Content\Item class
+ *
+ * @see Item::insert()
+ */
+final class ItemHelper
+{
+ private ItemContent $itemContent;
+
+ private Activity $activity;
+
+ private LoggerInterface $logger;
+
+ private Database $database;
+
+ private string $baseUrl;
+
+ public function __construct(
+ ItemContent $itemContent,
+ Activity $activity,
+ LoggerInterface $logger,
+ Database $database,
+ BaseURL $baseURL
+ ) {
+ $this->itemContent = $itemContent;
+ $this->activity = $activity;
+ $this->logger = $logger;
+ $this->database = $database;
+ $this->baseUrl = $baseURL->__toString();
+ }
+
+ public function prepareOriginPost(array $item): array
+ {
+ $item = $this->itemContent->initializePost($item);
+ $item = $this->itemContent->finalizePost($item, false);
+
+ return $item;
+ }
+
+ public function prepareItemData(array $item, bool $notify): array
+ {
+ $item['guid'] = Item::guid($item, $notify);
+ $item['uri'] = substr(trim($item['uri'] ?? '') ?: Item::newURI($item['guid']), 0, 255);
+
+ // Store URI data
+ $item['uri-id'] = ItemURI::insert(['uri' => $item['uri'], 'guid' => $item['guid']]);
+
+ // Backward compatibility: parent-uri used to be the direct parent uri.
+ // If it is provided without a thr-parent, it probably is the old behavior.
+ if (empty($item['thr-parent']) || empty($item['parent-uri'])) {
+ $item['thr-parent'] = trim($item['thr-parent'] ?? $item['parent-uri'] ?? $item['uri']);
+ $item['parent-uri'] = $item['thr-parent'];
+ }
+
+ $item['thr-parent-id'] = ItemURI::getIdByURI($item['thr-parent']);
+ $item['parent-uri-id'] = ItemURI::getIdByURI($item['parent-uri']);
+
+ return $item;
+ }
+
+ public function validateItemData(array $item): array
+ {
+ $item['wall'] = intval($item['wall'] ?? 0);
+ $item['extid'] = trim($item['extid'] ?? '');
+ $item['author-name'] = trim($item['author-name'] ?? '');
+ $item['author-link'] = trim($item['author-link'] ?? '');
+ $item['author-avatar'] = trim($item['author-avatar'] ?? '');
+ $item['owner-name'] = trim($item['owner-name'] ?? '');
+ $item['owner-link'] = trim($item['owner-link'] ?? '');
+ $item['owner-avatar'] = trim($item['owner-avatar'] ?? '');
+ $item['received'] = (isset($item['received']) ? DateTimeFormat::utc($item['received']) : DateTimeFormat::utcNow());
+ $item['created'] = (isset($item['created']) ? DateTimeFormat::utc($item['created']) : $item['received']);
+ $item['edited'] = (isset($item['edited']) ? DateTimeFormat::utc($item['edited']) : $item['created']);
+ $item['changed'] = (isset($item['changed']) ? DateTimeFormat::utc($item['changed']) : $item['created']);
+ $item['commented'] = (isset($item['commented']) ? DateTimeFormat::utc($item['commented']) : $item['created']);
+ $item['title'] = substr(trim($item['title'] ?? ''), 0, 255);
+ $item['location'] = trim($item['location'] ?? '');
+ $item['coord'] = trim($item['coord'] ?? '');
+ $item['visible'] = (isset($item['visible']) ? intval($item['visible']) : 1);
+ $item['deleted'] = 0;
+ $item['verb'] = trim($item['verb'] ?? '');
+ $item['object-type'] = trim($item['object-type'] ?? '');
+ $item['object'] = trim($item['object'] ?? '');
+ $item['target-type'] = trim($item['target-type'] ?? '');
+ $item['target'] = trim($item['target'] ?? '');
+ $item['plink'] = substr(trim($item['plink'] ?? ''), 0, 255);
+ $item['allow_cid'] = trim($item['allow_cid'] ?? '');
+ $item['allow_gid'] = trim($item['allow_gid'] ?? '');
+ $item['deny_cid'] = trim($item['deny_cid'] ?? '');
+ $item['deny_gid'] = trim($item['deny_gid'] ?? '');
+ $item['private'] = intval($item['private'] ?? Item::PUBLIC);
+ $item['body'] = trim($item['body'] ?? '');
+ $item['raw-body'] = trim($item['raw-body'] ?? $item['body']);
+ $item['app'] = trim($item['app'] ?? '');
+ $item['origin'] = intval($item['origin'] ?? 0);
+ $item['postopts'] = trim($item['postopts'] ?? '');
+ $item['resource-id'] = trim($item['resource-id'] ?? '');
+ $item['event-id'] = intval($item['event-id'] ?? 0);
+ $item['inform'] = trim($item['inform'] ?? '');
+ $item['file'] = trim($item['file'] ?? '');
+
+ // Items cannot be stored before they happen ...
+ if ($item['created'] > DateTimeFormat::utcNow()) {
+ $item['created'] = DateTimeFormat::utcNow();
+ }
+
+ // We haven't invented time travel by now.
+ if ($item['edited'] > DateTimeFormat::utcNow()) {
+ $item['edited'] = DateTimeFormat::utcNow();
+ }
+
+ $item['plink'] = ($item['plink'] ?? '') ?: $this->baseUrl . '/display/' . urlencode($item['guid']);
+
+ $item['gravity'] = $this->getGravity($item);
+
+ if ($item['gravity'] === Item::GRAVITY_UNKNOWN) {
+ $this->logger->info('Unknown gravity for verb', ['verb' => $item['verb']]);
+ }
+
+ $default = [
+ 'url' => $item['author-link'], 'name' => $item['author-name'],
+ 'photo' => $item['author-avatar'], 'network' => $item['network']
+ ];
+ $item['author-id'] = ($item['author-id'] ?? 0) ?: Contact::getIdForURL($item['author-link'], 0, null, $default);
+
+ $default = [
+ 'url' => $item['owner-link'], 'name' => $item['owner-name'],
+ 'photo' => $item['owner-avatar'], 'network' => $item['network']
+ ];
+ $item['owner-id'] = ($item['owner-id'] ?? 0) ?: Contact::getIdForURL($item['owner-link'], 0, null, $default);
+
+ $item['post-reason'] = Item::getPostReason($item);
+
+ return $item;
+ }
+
+ /**
+ * Fetch top-level parent data for the given item array
+ *
+ * @param array $item
+ * @return array item array with parent data
+ * @throws \Exception
+ */
+ public function getTopLevelParent(array $item): array
+ {
+ $fields = [
+ 'uid', 'uri', 'parent-uri', 'id', 'deleted',
+ 'uri-id', 'parent-uri-id', 'restrictions', 'verb',
+ 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid',
+ 'wall', 'private', 'origin', 'author-id'
+ ];
+ $condition = ['uri-id' => [$item['thr-parent-id'], $item['parent-uri-id']], 'uid' => $item['uid']];
+ $params = ['order' => ['id' => false]];
+ $parent = Post::selectFirst($fields, $condition, $params);
+
+ if (!$this->database->isResult($parent) && Post::exists(['uri-id' => [$item['thr-parent-id'], $item['parent-uri-id']], 'uid' => 0])) {
+ $stored = Item::storeForUserByUriId($item['thr-parent-id'], $item['uid'], ['post-reason' => Item::PR_COMPLETION]);
+ if (!$stored && ($item['thr-parent-id'] != $item['parent-uri-id'])) {
+ $stored = Item::storeForUserByUriId($item['parent-uri-id'], $item['uid'], ['post-reason' => Item::PR_COMPLETION]);
+ }
+ if ($stored) {
+ $this->logger->info('Stored thread parent item for user', ['uri-id' => $item['thr-parent-id'], 'uid' => $item['uid'], 'stored' => $stored]);
+ $parent = Post::selectFirst($fields, $condition, $params);
+ }
+ }
+
+ if (!$this->database->isResult($parent)) {
+ $this->logger->notice('item parent was not found - ignoring item', ['uri-id' => $item['uri-id'], 'thr-parent-id' => $item['thr-parent-id'], 'uid' => $item['uid']]);
+ return [];
+ }
+
+ if ($this->hasRestrictions($item, $parent['author-id'], $parent['restrictions'])) {
+ $this->logger->notice('Restrictions apply - ignoring item', ['restrictions' => $parent['restrictions'], 'verb' => $parent['verb'], 'uri-id' => $item['uri-id'], 'thr-parent-id' => $item['thr-parent-id'], 'uid' => $item['uid']]);
+ return [];
+ }
+
+ if ($parent['uri-id'] == $parent['parent-uri-id']) {
+ return $parent;
+ }
+
+ $condition = [
+ 'uri-id' => $parent['parent-uri-id'],
+ 'parent-uri-id' => $parent['parent-uri-id'],
+ 'uid' => $parent['uid']
+ ];
+ $params = ['order' => ['id' => false]];
+ $toplevel_parent = Post::selectFirst($fields, $condition, $params);
+
+ if (!$this->database->isResult($toplevel_parent) && $item['origin']) {
+ $stored = Item::storeForUserByUriId($item['parent-uri-id'], $item['uid'], ['post-reason' => Item::PR_COMPLETION]);
+ $this->logger->info('Stored parent item for user', ['uri-id' => $item['parent-uri-id'], 'uid' => $item['uid'], 'stored' => $stored]);
+ $toplevel_parent = Post::selectFirst($fields, $condition, $params);
+ }
+
+ if (!$this->database->isResult($toplevel_parent)) {
+ $this->logger->notice('item top level parent was not found - ignoring item', ['parent-uri-id' => $parent['parent-uri-id'], 'uid' => $parent['uid']]);
+ return [];
+ }
+
+ return $toplevel_parent;
+ }
+
+ public function handleToplevelParent(array $item, array $toplevel_parent, bool $defined_permissions): array
+ {
+ $parent_id = (int) $toplevel_parent['id'];
+ $item['parent-uri'] = $toplevel_parent['uri'];
+ $item['parent-uri-id'] = $toplevel_parent['uri-id'];
+ $item['deleted'] = $toplevel_parent['deleted'];
+ $item['wall'] = $toplevel_parent['wall'];
+
+ // Reshares have to keep their permissions to allow groups to work
+ if (!$defined_permissions && (!$item['origin'] || ($item['verb'] != Activity::ANNOUNCE))) {
+ // Don't store the permissions on pure AP posts
+ $store_permissions = ($item['network'] != Protocol::ACTIVITYPUB) || $item['origin'] || !empty($item['diaspora_signed_text']);
+ $item['allow_cid'] = $store_permissions ? $toplevel_parent['allow_cid'] : '';
+ $item['allow_gid'] = $store_permissions ? $toplevel_parent['allow_gid'] : '';
+ $item['deny_cid'] = $store_permissions ? $toplevel_parent['deny_cid'] : '';
+ $item['deny_gid'] = $store_permissions ? $toplevel_parent['deny_gid'] : '';
+ }
+
+ // Don't federate received participation messages
+ if ($item['verb'] != Activity::FOLLOW) {
+ $item['wall'] = $toplevel_parent['wall'];
+ } else {
+ $item['wall'] = false;
+ // Participations are technical messages, so they are set to "seen" automatically
+ $item['unseen'] = false;
+ }
+
+ /*
+ * If the parent is private, force privacy for the entire conversation
+ * This differs from the above settings as it subtly allows comments from
+ * email correspondents to be private even if the overall thread is not.
+ */
+ if (!$defined_permissions && $toplevel_parent['private']) {
+ $item['private'] = $toplevel_parent['private'];
+ }
+
+ // If its a post that originated here then tag the thread as "mention"
+ if ($item['origin'] && $item['uid']) {
+ $this->database->update('post-thread-user', ['mention' => true], ['uri-id' => $item['parent-uri-id'], 'uid' => $item['uid']]);
+ $this->logger->info('tagged thread as mention', ['parent' => $parent_id, 'parent-uri-id' => $item['parent-uri-id'], 'uid' => $item['uid']]);
+ }
+
+ // Update the contact relations
+ Contact\Relation::store($toplevel_parent['author-id'], $item['author-id'], $item['created']);
+
+ return $item;
+ }
+
+ private function hasRestrictions(array $item, int $author_id, int $restrictions = null): bool
+ {
+ if (empty($restrictions) || ($author_id == $item['author-id'])) {
+ return false;
+ }
+
+ // We only have to apply restrictions if the post originates from our server or is federated.
+ // Every other time we can trust the remote system.
+ if (!in_array($item['network'], Protocol::FEDERATED) && !$item['origin']) {
+ return false;
+ }
+
+ if (($restrictions & Item::CANT_REPLY) && ($item['verb'] == Activity::POST)) {
+ return true;
+ }
+
+ if (($restrictions & Item::CANT_ANNOUNCE) && ($item['verb'] == Activity::ANNOUNCE)) {
+ return true;
+ }
+
+ if (($restrictions & Item::CANT_LIKE) && in_array($item['verb'], [Activity::LIKE, Activity::DISLIKE, Activity::ATTEND, Activity::ATTENDMAYBE, Activity::ATTENDNO])) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the gravity for the given item array
+ *
+ * @return int gravity
+ */
+ private function getGravity(array $item): int
+ {
+ if (isset($item['gravity'])) {
+ return intval($item['gravity']);
+ } elseif ($item['parent-uri-id'] === $item['uri-id']) {
+ return Item::GRAVITY_PARENT;
+ } elseif ($this->activity->match($item['verb'], Activity::POST)) {
+ return Item::GRAVITY_COMMENT;
+ } elseif ($this->activity->match($item['verb'], Activity::FOLLOW)) {
+ return Item::GRAVITY_ACTIVITY;
+ } elseif ($this->activity->match($item['verb'], Activity::ANNOUNCE)) {
+ return Item::GRAVITY_ACTIVITY;
+ }
+
+ return Item::GRAVITY_UNKNOWN; // Should not happen
+ }
+}
+++ /dev/null
-<?php
-
-// Copyright (C) 2010-2024, the Friendica project
-// SPDX-FileCopyrightText: 2010-2024 the Friendica project
-//
-// SPDX-License-Identifier: AGPL-3.0-or-later
-
-namespace Friendica\Model;
-
-use Friendica\App\BaseURL;
-use Friendica\Content\Item as ItemContent;
-use Friendica\Core\Protocol;
-use Friendica\Database\Database;
-use Friendica\Protocol\Activity;
-use Friendica\Util\DateTimeFormat;
-use Psr\Log\LoggerInterface;
-
-/**
- * A helper class for inserting an Item Model
- *
- * @internal only for use in Friendica\Content\Item class
- *
- * @see Item::insert()
- */
-final class ItemInserter
-{
- private ItemContent $itemContent;
-
- private Activity $activity;
-
- private LoggerInterface $logger;
-
- private Database $database;
-
- private string $baseUrl;
-
- public function __construct(
- ItemContent $itemContent,
- Activity $activity,
- LoggerInterface $logger,
- Database $database,
- BaseURL $baseURL
- ) {
- $this->itemContent = $itemContent;
- $this->activity = $activity;
- $this->logger = $logger;
- $this->database = $database;
- $this->baseUrl = $baseURL->__toString();
- }
-
- public function prepareOriginPost(array $item): array
- {
- $item = $this->itemContent->initializePost($item);
- $item = $this->itemContent->finalizePost($item, false);
-
- return $item;
- }
-
- public function prepareItemData(array $item, bool $notify): array
- {
- $item['guid'] = Item::guid($item, $notify);
- $item['uri'] = substr(trim($item['uri'] ?? '') ?: Item::newURI($item['guid']), 0, 255);
-
- // Store URI data
- $item['uri-id'] = ItemURI::insert(['uri' => $item['uri'], 'guid' => $item['guid']]);
-
- // Backward compatibility: parent-uri used to be the direct parent uri.
- // If it is provided without a thr-parent, it probably is the old behavior.
- if (empty($item['thr-parent']) || empty($item['parent-uri'])) {
- $item['thr-parent'] = trim($item['thr-parent'] ?? $item['parent-uri'] ?? $item['uri']);
- $item['parent-uri'] = $item['thr-parent'];
- }
-
- $item['thr-parent-id'] = ItemURI::getIdByURI($item['thr-parent']);
- $item['parent-uri-id'] = ItemURI::getIdByURI($item['parent-uri']);
-
- return $item;
- }
-
- public function validateItemData(array $item): array
- {
- $item['wall'] = intval($item['wall'] ?? 0);
- $item['extid'] = trim($item['extid'] ?? '');
- $item['author-name'] = trim($item['author-name'] ?? '');
- $item['author-link'] = trim($item['author-link'] ?? '');
- $item['author-avatar'] = trim($item['author-avatar'] ?? '');
- $item['owner-name'] = trim($item['owner-name'] ?? '');
- $item['owner-link'] = trim($item['owner-link'] ?? '');
- $item['owner-avatar'] = trim($item['owner-avatar'] ?? '');
- $item['received'] = (isset($item['received']) ? DateTimeFormat::utc($item['received']) : DateTimeFormat::utcNow());
- $item['created'] = (isset($item['created']) ? DateTimeFormat::utc($item['created']) : $item['received']);
- $item['edited'] = (isset($item['edited']) ? DateTimeFormat::utc($item['edited']) : $item['created']);
- $item['changed'] = (isset($item['changed']) ? DateTimeFormat::utc($item['changed']) : $item['created']);
- $item['commented'] = (isset($item['commented']) ? DateTimeFormat::utc($item['commented']) : $item['created']);
- $item['title'] = substr(trim($item['title'] ?? ''), 0, 255);
- $item['location'] = trim($item['location'] ?? '');
- $item['coord'] = trim($item['coord'] ?? '');
- $item['visible'] = (isset($item['visible']) ? intval($item['visible']) : 1);
- $item['deleted'] = 0;
- $item['verb'] = trim($item['verb'] ?? '');
- $item['object-type'] = trim($item['object-type'] ?? '');
- $item['object'] = trim($item['object'] ?? '');
- $item['target-type'] = trim($item['target-type'] ?? '');
- $item['target'] = trim($item['target'] ?? '');
- $item['plink'] = substr(trim($item['plink'] ?? ''), 0, 255);
- $item['allow_cid'] = trim($item['allow_cid'] ?? '');
- $item['allow_gid'] = trim($item['allow_gid'] ?? '');
- $item['deny_cid'] = trim($item['deny_cid'] ?? '');
- $item['deny_gid'] = trim($item['deny_gid'] ?? '');
- $item['private'] = intval($item['private'] ?? Item::PUBLIC);
- $item['body'] = trim($item['body'] ?? '');
- $item['raw-body'] = trim($item['raw-body'] ?? $item['body']);
- $item['app'] = trim($item['app'] ?? '');
- $item['origin'] = intval($item['origin'] ?? 0);
- $item['postopts'] = trim($item['postopts'] ?? '');
- $item['resource-id'] = trim($item['resource-id'] ?? '');
- $item['event-id'] = intval($item['event-id'] ?? 0);
- $item['inform'] = trim($item['inform'] ?? '');
- $item['file'] = trim($item['file'] ?? '');
-
- // Items cannot be stored before they happen ...
- if ($item['created'] > DateTimeFormat::utcNow()) {
- $item['created'] = DateTimeFormat::utcNow();
- }
-
- // We haven't invented time travel by now.
- if ($item['edited'] > DateTimeFormat::utcNow()) {
- $item['edited'] = DateTimeFormat::utcNow();
- }
-
- $item['plink'] = ($item['plink'] ?? '') ?: $this->baseUrl . '/display/' . urlencode($item['guid']);
-
- $item['gravity'] = $this->getGravity($item);
-
- if ($item['gravity'] === Item::GRAVITY_UNKNOWN) {
- $this->logger->info('Unknown gravity for verb', ['verb' => $item['verb']]);
- }
-
- $default = [
- 'url' => $item['author-link'], 'name' => $item['author-name'],
- 'photo' => $item['author-avatar'], 'network' => $item['network']
- ];
- $item['author-id'] = ($item['author-id'] ?? 0) ?: Contact::getIdForURL($item['author-link'], 0, null, $default);
-
- $default = [
- 'url' => $item['owner-link'], 'name' => $item['owner-name'],
- 'photo' => $item['owner-avatar'], 'network' => $item['network']
- ];
- $item['owner-id'] = ($item['owner-id'] ?? 0) ?: Contact::getIdForURL($item['owner-link'], 0, null, $default);
-
- $item['post-reason'] = Item::getPostReason($item);
-
- return $item;
- }
-
- /**
- * Fetch top-level parent data for the given item array
- *
- * @param array $item
- * @return array item array with parent data
- * @throws \Exception
- */
- public function getTopLevelParent(array $item): array
- {
- $fields = [
- 'uid', 'uri', 'parent-uri', 'id', 'deleted',
- 'uri-id', 'parent-uri-id', 'restrictions', 'verb',
- 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid',
- 'wall', 'private', 'origin', 'author-id'
- ];
- $condition = ['uri-id' => [$item['thr-parent-id'], $item['parent-uri-id']], 'uid' => $item['uid']];
- $params = ['order' => ['id' => false]];
- $parent = Post::selectFirst($fields, $condition, $params);
-
- if (!$this->database->isResult($parent) && Post::exists(['uri-id' => [$item['thr-parent-id'], $item['parent-uri-id']], 'uid' => 0])) {
- $stored = Item::storeForUserByUriId($item['thr-parent-id'], $item['uid'], ['post-reason' => Item::PR_COMPLETION]);
- if (!$stored && ($item['thr-parent-id'] != $item['parent-uri-id'])) {
- $stored = Item::storeForUserByUriId($item['parent-uri-id'], $item['uid'], ['post-reason' => Item::PR_COMPLETION]);
- }
- if ($stored) {
- $this->logger->info('Stored thread parent item for user', ['uri-id' => $item['thr-parent-id'], 'uid' => $item['uid'], 'stored' => $stored]);
- $parent = Post::selectFirst($fields, $condition, $params);
- }
- }
-
- if (!$this->database->isResult($parent)) {
- $this->logger->notice('item parent was not found - ignoring item', ['uri-id' => $item['uri-id'], 'thr-parent-id' => $item['thr-parent-id'], 'uid' => $item['uid']]);
- return [];
- }
-
- if ($this->hasRestrictions($item, $parent['author-id'], $parent['restrictions'])) {
- $this->logger->notice('Restrictions apply - ignoring item', ['restrictions' => $parent['restrictions'], 'verb' => $parent['verb'], 'uri-id' => $item['uri-id'], 'thr-parent-id' => $item['thr-parent-id'], 'uid' => $item['uid']]);
- return [];
- }
-
- if ($parent['uri-id'] == $parent['parent-uri-id']) {
- return $parent;
- }
-
- $condition = [
- 'uri-id' => $parent['parent-uri-id'],
- 'parent-uri-id' => $parent['parent-uri-id'],
- 'uid' => $parent['uid']
- ];
- $params = ['order' => ['id' => false]];
- $toplevel_parent = Post::selectFirst($fields, $condition, $params);
-
- if (!$this->database->isResult($toplevel_parent) && $item['origin']) {
- $stored = Item::storeForUserByUriId($item['parent-uri-id'], $item['uid'], ['post-reason' => Item::PR_COMPLETION]);
- $this->logger->info('Stored parent item for user', ['uri-id' => $item['parent-uri-id'], 'uid' => $item['uid'], 'stored' => $stored]);
- $toplevel_parent = Post::selectFirst($fields, $condition, $params);
- }
-
- if (!$this->database->isResult($toplevel_parent)) {
- $this->logger->notice('item top level parent was not found - ignoring item', ['parent-uri-id' => $parent['parent-uri-id'], 'uid' => $parent['uid']]);
- return [];
- }
-
- return $toplevel_parent;
- }
-
- public function handleToplevelParent(array $item, array $toplevel_parent, bool $defined_permissions): array
- {
- $parent_id = (int) $toplevel_parent['id'];
- $item['parent-uri'] = $toplevel_parent['uri'];
- $item['parent-uri-id'] = $toplevel_parent['uri-id'];
- $item['deleted'] = $toplevel_parent['deleted'];
- $item['wall'] = $toplevel_parent['wall'];
-
- // Reshares have to keep their permissions to allow groups to work
- if (!$defined_permissions && (!$item['origin'] || ($item['verb'] != Activity::ANNOUNCE))) {
- // Don't store the permissions on pure AP posts
- $store_permissions = ($item['network'] != Protocol::ACTIVITYPUB) || $item['origin'] || !empty($item['diaspora_signed_text']);
- $item['allow_cid'] = $store_permissions ? $toplevel_parent['allow_cid'] : '';
- $item['allow_gid'] = $store_permissions ? $toplevel_parent['allow_gid'] : '';
- $item['deny_cid'] = $store_permissions ? $toplevel_parent['deny_cid'] : '';
- $item['deny_gid'] = $store_permissions ? $toplevel_parent['deny_gid'] : '';
- }
-
- // Don't federate received participation messages
- if ($item['verb'] != Activity::FOLLOW) {
- $item['wall'] = $toplevel_parent['wall'];
- } else {
- $item['wall'] = false;
- // Participations are technical messages, so they are set to "seen" automatically
- $item['unseen'] = false;
- }
-
- /*
- * If the parent is private, force privacy for the entire conversation
- * This differs from the above settings as it subtly allows comments from
- * email correspondents to be private even if the overall thread is not.
- */
- if (!$defined_permissions && $toplevel_parent['private']) {
- $item['private'] = $toplevel_parent['private'];
- }
-
- // If its a post that originated here then tag the thread as "mention"
- if ($item['origin'] && $item['uid']) {
- $this->database->update('post-thread-user', ['mention' => true], ['uri-id' => $item['parent-uri-id'], 'uid' => $item['uid']]);
- $this->logger->info('tagged thread as mention', ['parent' => $parent_id, 'parent-uri-id' => $item['parent-uri-id'], 'uid' => $item['uid']]);
- }
-
- // Update the contact relations
- Contact\Relation::store($toplevel_parent['author-id'], $item['author-id'], $item['created']);
-
- return $item;
- }
-
- private function hasRestrictions(array $item, int $author_id, int $restrictions = null): bool
- {
- if (empty($restrictions) || ($author_id == $item['author-id'])) {
- return false;
- }
-
- // We only have to apply restrictions if the post originates from our server or is federated.
- // Every other time we can trust the remote system.
- if (!in_array($item['network'], Protocol::FEDERATED) && !$item['origin']) {
- return false;
- }
-
- if (($restrictions & Item::CANT_REPLY) && ($item['verb'] == Activity::POST)) {
- return true;
- }
-
- if (($restrictions & Item::CANT_ANNOUNCE) && ($item['verb'] == Activity::ANNOUNCE)) {
- return true;
- }
-
- if (($restrictions & Item::CANT_LIKE) && in_array($item['verb'], [Activity::LIKE, Activity::DISLIKE, Activity::ATTEND, Activity::ATTENDMAYBE, Activity::ATTENDNO])) {
- return true;
- }
-
- return false;
- }
-
- /**
- * Get the gravity for the given item array
- *
- * @return int gravity
- */
- private function getGravity(array $item): int
- {
- if (isset($item['gravity'])) {
- return intval($item['gravity']);
- } elseif ($item['parent-uri-id'] === $item['uri-id']) {
- return Item::GRAVITY_PARENT;
- } elseif ($this->activity->match($item['verb'], Activity::POST)) {
- return Item::GRAVITY_COMMENT;
- } elseif ($this->activity->match($item['verb'], Activity::FOLLOW)) {
- return Item::GRAVITY_ACTIVITY;
- } elseif ($this->activity->match($item['verb'], Activity::ANNOUNCE)) {
- return Item::GRAVITY_ACTIVITY;
- }
-
- return Item::GRAVITY_UNKNOWN; // Should not happen
- }
-}