+ return 0;
+ }
+
+ /**
+ * Fetch parent data for the given item array
+ *
+ * @param array $item
+ * @return array item array with parent data
+ */
+ private static function getParentData(array $item)
+ {
+ // find the parent and snarf the item id and ACLs
+ // and anything else we need to inherit
+
+ $fields = ['uri', 'parent-uri', 'id', 'deleted',
+ 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid',
+ 'wall', 'private', 'forum_mode', 'origin', 'author-id'];
+ $condition = ['uri' => $item['parent-uri'], 'uid' => $item['uid']];
+ $params = ['order' => ['id' => false]];
+ $parent = self::selectFirst($fields, $condition, $params);
+
+ if (!DBA::isResult($parent)) {
+ Logger::info('item parent was not found - ignoring item', ['parent-uri' => $item['parent-uri'], 'uid' => $item['uid']]);
+ return [];
+ } else {
+ // is the new message multi-level threaded?
+ // even though we don't support it now, preserve the info
+ // and re-attach to the conversation parent.
+ if ($parent['uri'] != $parent['parent-uri']) {
+ $item['parent-uri'] = $parent['parent-uri'];
+
+ $condition = ['uri' => $item['parent-uri'],
+ 'parent-uri' => $item['parent-uri'],
+ 'uid' => $item['uid']];
+ $params = ['order' => ['id' => false]];
+ $toplevel_parent = self::selectFirst($fields, $condition, $params);
+
+ if (DBA::isResult($toplevel_parent)) {
+ $parent = $toplevel_parent;
+ }
+ }
+
+ $item['parent'] = $parent['id'];
+ $item["deleted"] = $parent['deleted'];
+ $item["allow_cid"] = $parent['allow_cid'];
+ $item['allow_gid'] = $parent['allow_gid'];
+ $item['deny_cid'] = $parent['deny_cid'];
+ $item['deny_gid'] = $parent['deny_gid'];
+ $item['parent_origin'] = $parent['origin'];
+
+ // Don't federate received participation messages
+ if ($item['verb'] != Activity::FOLLOW) {
+ $item['wall'] = $parent['wall'];
+ } else {
+ $item['wall'] = 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 ($parent['private']) {
+ $item['private'] = $parent['private'];
+ }
+
+ /*
+ * Edge case. We host a public forum that was originally posted to privately.
+ * The original author commented, but as this is a comment, the permissions
+ * weren't fixed up so it will still show the comment as private unless we fix it here.
+ */
+ if ((intval($parent['forum_mode']) == 1) && ($parent['private'] != self::PUBLIC)) {
+ $item['private'] = self::PUBLIC;
+ }
+
+ // If its a post that originated here then tag the thread as "mention"
+ if ($item['origin'] && $item['uid']) {
+ DBA::update('thread', ['mention' => true], ['iid' => $item['parent']]);
+ Logger::info('tagged thread as mention', ['parent' => $item['parent'], 'uid' => $item['uid']]);
+ }
+
+ // Update the contact relations
+ if ($item['author-id'] != $parent['author-id']) {
+ DBA::update('contact-relation', ['last-interaction' => $item['created']], ['cid' => $parent['author-id'], 'relation-cid' => $item['author-id']], true);
+ }
+ }
+
+ return $item;
+ }
+
+ /**
+ * Get the gravity for the given item array
+ *
+ * @param array $item
+ * @return integer gravity
+ */
+ private static function getGravity(array $item)
+ {
+ $activity = DI::activity();
+
+ if (isset($item['gravity'])) {
+ return intval($item['gravity']);
+ } elseif ($item['parent-uri'] === $item['uri']) {
+ return GRAVITY_PARENT;
+ } elseif ($activity->match($item['verb'], Activity::POST)) {
+ return GRAVITY_COMMENT;
+ } elseif ($activity->match($item['verb'], Activity::FOLLOW)) {
+ return GRAVITY_ACTIVITY;
+ }
+ Logger::info('Unknown gravity for verb', ['verb' => $item['verb']]);
+ return GRAVITY_UNKNOWN; // Should not happen
+ }
+
+ public static function insert($item, $notify = false, $dontcache = false)
+ {
+ $orig_item = $item;
+
+ $priority = PRIORITY_HIGH;
+
+ // If it is a posting where users should get notifications, then define it as wall posting
+ if ($notify) {
+ $item['wall'] = 1;
+ $item['origin'] = 1;
+ $item['network'] = Protocol::DFRN;
+ $item['protocol'] = Conversation::PARCEL_DFRN;
+
+ if (is_int($notify)) {
+ $priority = $notify;
+ }
+ } else {
+ $item['network'] = trim(($item['network'] ?? '') ?: Protocol::PHANTOM);
+ }
+
+ $uid = intval($item['uid']);
+
+ $item['guid'] = self::guid($item, $notify);
+ $item['uri'] = substr(Strings::escapeTags(trim(($item['uri'] ?? '') ?: self::newURI($item['uid'], $item['guid']))), 0, 255);
+
+ // Store URI data
+ $item['uri-id'] = ItemURI::insert(['uri' => $item['uri'], 'guid' => $item['guid']]);
+
+ // Store conversation data
+ $item = Conversation::insert($item);
+
+ if (!empty($item['thr-parent'])) {
+ $item['parent-uri'] = $item['thr-parent'];
+ }
+
+ /*
+ * Do we already have this item?
+ * We have to check several networks since Friendica posts could be repeated
+ * via OStatus (maybe Diasporsa as well)
+ */
+ $duplicate = self::getDuplicateID($item);
+ if ($duplicate) {
+ return $duplicate;
+ }
+
+ // Additional duplicate checks
+ /// @todo Check why the first duplication check returns the item number and the second a 0
+ if (self::isDuplicate($item)) {
+ return 0;
+ }