]> git.mxchange.org Git - friendica.git/blobdiff - src/Model/Item.php
New function to fetch item data especially for users
[friendica.git] / src / Model / Item.php
index 01a0b7497cb6397f6762222c2e540d566c021066..4018db9ff68a74acd4e27e6384aaf4c75ac884cc 100644 (file)
@@ -33,6 +33,371 @@ require_once 'include/text.php';
 
 class Item extends BaseObject
 {
+       // Field list that is used to display the items
+       const DISPLAY_FIELDLIST = ['uid', 'id', 'parent', 'uri', 'thr-parent', 'parent-uri', 'guid',
+                       'commented', 'created', 'edited', 'received', 'verb', 'object-type', 'postopts', 'plink',
+                       'wall', 'private', 'starred', 'origin', 'title', 'body', 'file', 'attach',
+                       'content-warning', 'location', 'coord', 'app', 'rendered-hash', 'rendered-html', 'object',
+                       'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'item_id', 'item_network',
+                       'author-id', 'author-link', 'author-name', 'author-avatar',
+                       'owner-id', 'owner-link', 'owner-name', 'owner-avatar',
+                       'contact-id', 'contact-link', 'contact-name', 'contact-avatar',
+                       'network', 'url', 'name', 'writable', 'self', 'cid', 'alias',
+                       'event-id', 'event-created', 'event-edited', 'event-start', 'event-finish',
+                       'event-summary', 'event-desc', 'event-location', 'event-type',
+                       'event-nofinish', 'event-adjust', 'event-ignore', 'event-id'];
+
+       // Field list that is used to deliver items via the protocols
+       const DELIVER_FIELDLIST = ['uid', 'id', 'parent', 'uri', 'thr-parent', 'parent-uri', 'guid',
+                       'created', 'edited', 'verb', 'object-type', 'object', 'target',
+                       'private', 'title', 'body', 'location', 'coord', 'app',
+                       'attach', 'tag', 'bookmark', 'deleted', 'extid',
+                       'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid',
+                       'author-id', 'author-link', 'owner-link', 'contact-uid',
+                       'signed_text', 'signature', 'signer'];
+
+       /**
+        * Retrieve a single record from the item table for a given user and returns it in an associative array
+        *
+        * @brief Retrieve a single record from a table
+        * @param integer $uid User ID
+        * @param array  $fields
+        * @param array  $condition
+        * @param array  $params
+        * @return bool|array
+        * @see dba::select
+        */
+       public static function selectFirstForUser($uid, array $selected = [], array $condition = [], $params = [])
+       {
+               $params['uid'] = $uid;
+
+               if (empty($selected)) {
+                       $selected = Item::DISPLAY_FIELDLIST;
+               }
+
+               return self::selectFirst($selected, $condition, $params);
+       }
+
+       /**
+        * @brief Select rows from the item table for a given user
+        *
+        * @param integer $uid User ID
+        * @param array  $selected  Array of selected fields, empty for all
+        * @param array  $condition Array of fields for condition
+        * @param array  $params    Array of several parameters
+        *
+        * @return boolean|object
+        */
+       public static function selectForUser($uid, array $selected = [], array $condition = [], $params = [])
+       {
+               $params['uid'] = $uid;
+
+               if (empty($selected)) {
+                       $selected = Item::DISPLAY_FIELDLIST;
+               }
+
+               return self::select($selected, $condition, $params);
+       }
+
+       /**
+        * Retrieve a single record from the item table and returns it in an associative array
+        *
+        * @brief Retrieve a single record from a table
+        * @param array  $fields
+        * @param array  $condition
+        * @param array  $params
+        * @return bool|array
+        * @see dba::select
+        */
+       public static function selectFirst(array $fields = [], array $condition = [], $params = [])
+       {
+               $params['limit'] = 1;
+
+               $result = self::select($fields, $condition, $params);
+
+               if (is_bool($result)) {
+                       return $result;
+               } else {
+                       $row = dba::fetch($result);
+                       dba::close($result);
+                       return $row;
+               }
+       }
+
+       /**
+        * @brief Select rows from the item table
+        *
+        * @param array  $selected  Array of selected fields, empty for all
+        * @param array  $condition Array of fields for condition
+        * @param array  $params    Array of several parameters
+        *
+        * @return boolean|object
+        */
+       public static function select(array $selected = [], array $condition = [], $params = [])
+       {
+               $uid = 0;
+               $usermode = false;
+
+               if (isset($params['uid'])) {
+                       $uid = $params['uid'];
+                       $usermode = true;
+               }
+
+               $fields = self::fieldlist($selected);
+
+               $select_fields = self::constructSelectFields($fields, $selected);
+
+               $condition_string = dba::buildCondition($condition);
+
+               $condition_string = self::addTablesToFields($condition_string, $fields);
+
+               if ($usermode) {
+                       $condition_string = $condition_string . ' AND ' . self::condition(false);
+               }
+
+               $param_string = self::addTablesToFields(dba::buildParameter($params), $fields);
+
+               $table = "`item` " . self::constructJoins($uid, $select_fields . $condition_string . $param_string, false);
+
+               $sql = "SELECT " . $select_fields . " FROM " . $table . $condition_string . $param_string;
+
+               return dba::p($sql, $condition);
+       }
+
+       /**
+        * Retrieve a single record from the starting post in the item table and returns it in an associative array
+        *
+        * @brief Retrieve a single record from a table
+        * @param integer $uid User ID
+        * @param array  $fields
+        * @param array  $condition
+        * @param array  $params
+        * @return bool|array
+        * @see dba::select
+        */
+       public static function selectFirstThreadForUser($uid, array $fields = [], array $condition = [], $params = [])
+       {
+               $params['limit'] = 1;
+               $result = self::selectThreadForUser($uid, $fields, $condition, $params);
+
+               if (is_bool($result)) {
+                       return $result;
+               } else {
+                       $row = dba::fetch($result);
+                       dba::close($result);
+                       return $row;
+               }
+       }
+
+       /**
+        * @brief Select rows from the starting post in the item table
+        *
+        * @param integer $uid User ID
+        * @param array  $fields    Array of selected fields, empty for all
+        * @param array  $condition Array of fields for condition
+        * @param array  $params    Array of several parameters
+        *
+        * @return boolean|object
+        */
+       public static function selectThreadForUser($uid, array $selected = [], array $condition = [], $params = [])
+       {
+               if (empty($selected)) {
+                       $selected = Item::DISPLAY_FIELDLIST;
+               }
+
+               $fields = self::fieldlist($selected);
+
+               $threadfields = ['thread' => ['iid', 'uid', 'contact-id', 'owner-id', 'author-id',
+                       'created', 'edited', 'commented', 'received', 'changed', 'wall', 'private',
+                       'pubmail', 'moderated', 'visible', 'starred', 'ignored', 'bookmark',
+                       'unseen', 'deleted', 'origin', 'forum_mode', 'mention', 'network']];
+
+               $select_fields = self::constructSelectFields($fields, $selected);
+
+               $condition_string = dba::buildCondition($condition);
+
+               $condition_string = self::addTablesToFields($condition_string, $threadfields);
+               $condition_string = self::addTablesToFields($condition_string, $fields);
+
+               $condition_string = $condition_string . ' AND ' . self::condition(true);
+
+               $param_string = dba::buildParameter($params);
+               $param_string = self::addTablesToFields($param_string, $threadfields);
+               $param_string = self::addTablesToFields($param_string, $fields);
+
+               $table = "`thread` " . self::constructJoins($uid, $select_fields . $condition_string . $param_string, true);
+
+               $sql = "SELECT " . $select_fields . " FROM " . $table . $condition_string . $param_string;
+
+               return dba::p($sql, $condition);
+       }
+
+       /**
+        * @brief Returns a list of fields that are associated with the item table
+        *
+        * @return array field list
+        */
+       private static function fieldlist($selected)
+       {
+               $item_fields = ['author-id', 'owner-id', 'contact-id', 'uid', 'id', 'parent',
+                       'uri', 'thr-parent', 'parent-uri', 'content-warning',
+                       'commented', 'created', 'edited', 'received', 'verb', 'object-type', 'postopts', 'plink',
+                       'guid', 'wall', 'private', 'starred', 'origin', 'title', 'body', 'file', 'event-id',
+                       'location', 'coord', 'app', 'attach', 'rendered-hash', 'rendered-html', 'object',
+                       'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid',
+                       'id' => 'item_id', 'network' => 'item_network',
+                       'type', 'extid', 'changed', 'moderated', 'target-type', 'target',
+                       'resource-id', 'tag', 'inform', 'pubmail', 'visible', 'bookmark', 'unseen', 'deleted',
+                       'forum_mode', 'mention', 'global', 'shadow'];
+
+               $author_fields = ['url' => 'author-link', 'name' => 'author-name', 'thumb' => 'author-avatar'];
+               $owner_fields = ['url' => 'owner-link', 'name' => 'owner-name', 'thumb' => 'owner-avatar'];
+               $contact_fields = ['url' => 'contact-link', 'name' => 'contact-name', 'thumb' => 'contact-avatar',
+                       'network', 'url', 'name', 'writable', 'self', 'id' => 'cid', 'alias', 'uid' => 'contact-uid',
+                       'photo', 'name-date', 'uri-date', 'avatar-date', 'thumb', 'dfrn-id'];
+
+               $event_fields = ['created' => 'event-created', 'edited' => 'event-edited',
+                       'start' => 'event-start','finish' => 'event-finish',
+                       'summary' => 'event-summary','desc' => 'event-desc',
+                       'location' => 'event-location', 'type' => 'event-type',
+                       'nofinish' => 'event-nofinish','adjust' => 'event-adjust',
+                       'ignore' => 'event-ignore', 'id' => 'event-id'];
+
+               $fields = ['item' => $item_fields, 'author' => $author_fields, 'owner' => $owner_fields,
+                       'contact' => $contact_fields, 'event' => $event_fields];
+
+               $fields['parent-item'] = ['guid' => 'parent-guid'];
+               $fields['parent-item-author'] = ['url' => 'parent-author-link', 'name' => 'parent-author-name'];
+               $fields['sign'] = ['signed_text', 'signature', 'signer'];
+
+               return $fields;
+       }
+
+       /**
+        * @brief Returns SQL condition for the "select" functions
+        *
+        * @param boolean $thread_mode Called for the items (false) or for the threads (true)
+        *
+        * @return string SQL condition
+        */
+       private static function condition($thread_mode)
+       {
+               if ($thread_mode) {
+                       $master_table = "`thread`";
+               } else {
+                       $master_table = "`item`";
+               }
+               return "$master_table.`visible` AND NOT $master_table.`deleted` AND NOT $master_table.`moderated` AND (`user-item`.`hidden` IS NULL OR NOT `user-item`.`hidden`) ";
+       }
+
+       /**
+        * @brief Returns all needed "JOIN" commands for the "select" functions
+        *
+        * @param integer $uid User ID
+        * @param string $sql_commands The parts of the built SQL commands in the "select" functions
+        * @param boolean $thread_mode Called for the items (false) or for the threads (true)
+        *
+        * @return string The SQL joins for the "select" functions
+        */
+       private static function constructJoins($uid, $sql_commands, $thread_mode)
+       {
+               if ($thread_mode) {
+                       $master_table = "`thread`";
+                       $master_table_key = "`thread`.`iid`";
+                       $joins = "STRAIGHT_JOIN `item` ON `item`.`id` = `thread`.`iid` ";
+               } else {
+                       $master_table = "`item`";
+                       $master_table_key = "`item`.`id`";
+                       $joins = '';
+               }
+
+               $joins .= sprintf("STRAIGHT_JOIN `contact` ON `contact`.`id` = $master_table.`contact-id`
+                       AND NOT `contact`.`blocked`
+                       AND ((NOT `contact`.`readonly` AND NOT `contact`.`pending` AND (`contact`.`rel` IN (%s, %s)))
+                       OR `contact`.`self` OR (`item`.`id` != `item`.`parent`) OR `contact`.`uid` = 0)
+                       STRAIGHT_JOIN `contact` AS `author` ON `author`.`id` = $master_table.`author-id` AND NOT `author`.`blocked`
+                       STRAIGHT_JOIN `contact` AS `owner` ON `owner`.`id` = $master_table.`owner-id` AND NOT `owner`.`blocked`
+                       LEFT JOIN `user-item` ON `user-item`.`iid` = $master_table_key AND `user-item`.`uid` = %d",
+                       CONTACT_IS_SHARING, CONTACT_IS_FRIEND, intval($uid));
+
+               if (strpos($sql_commands, "`group_member`.") !== false) {
+                       $joins .= " STRAIGHT_JOIN `group_member` ON `group_member`.`contact-id` = $master_table.`contact-id`";
+               }
+
+               if (strpos($sql_commands, "`user`.") !== false) {
+                       $joins .= " STRAIGHT_JOIN `user` ON `user`.`uid` = $master_table.`uid`";
+               }
+
+               if (strpos($sql_commands, "`event`.") !== false) {
+                       $joins .= " LEFT JOIN `event` ON `event-id` = `event`.`id`";
+               }
+
+               if (strpos($sql_commands, "`sign`.") !== false) {
+                       $joins .= " LEFT JOIN `sign` ON `sign`.`iid` = `item`.`id`";
+               }
+
+               if ((strpos($sql_commands, "`parent-item`.") !== false) || (strpos($sql_commands, "`parent-author`.") !== false)) {
+                       $joins .= " STRAIGHT_JOIN `item` AS `parent-item` ON `parent-item`.`id` = `item`.`parent`";
+               }
+
+               if (strpos($sql_commands, "`parent-item-author`.") !== false) {
+                       $joins .= " STRAIGHT_JOIN `contact` AS `parent-item-author` ON `parent-item-author`.`id` = `parent-item`.`author-id`";
+               }
+
+               return $joins;
+       }
+
+       /**
+        * @brief Add the field list for the "select" functions
+        *
+        * @param array $fields The field definition array
+        * @param array $selected The array with the selected fields from the "select" functions
+        *
+        * @return string The field list
+        */
+       private static function constructSelectFields($fields, $selected)
+       {
+               $selection = [];
+               foreach ($fields as $table => $table_fields) {
+                       foreach ($table_fields as $field => $select) {
+                               if (empty($selected) || in_array($select, $selected)) {
+                                       if (is_int($field)) {
+                                               $selection[] = "`" . $table . "`.`".$select."`";
+                                       } else {
+                                               $selection[] = "`" . $table . "`.`" . $field . "` AS `".$select ."`";
+                                       }
+                               }
+                       }
+               }
+               return implode(", ", $selection);
+       }
+
+       /**
+        * @brief add table definition to fields in an SQL query
+        *
+        * @param string $query SQL query
+        * @param array $fields The field definition array
+        *
+        * @return string the changed SQL query
+        */
+       private static function addTablesToFields($query, $fields)
+       {
+               foreach ($fields as $table => $table_fields) {
+                       foreach ($table_fields as $alias => $field) {
+                               if (is_int($alias)) {
+                                       $replace_field = $field;
+                               } else {
+                                       $replace_field = $alias;
+                               }
+
+                               $search = "/([^\.])`" . $field . "`/i";
+                               $replace = "$1`" . $table . "`.`" . $replace_field . "`";
+                               $query = preg_replace($search, $replace, $query);
+                       }
+               }
+               return $query;
+       }
+
        /**
         * @brief Update existing item entries
         *
@@ -75,8 +440,9 @@ class Item extends BaseObject
                        Term::insertFromFileFieldByItemId($item['id']);
                        self::updateThread($item['id']);
 
-                       // We only need to notfiy others when it is an original entry from us
-                       if ($item['origin']) {
+                       // We only need to notfiy others when it is an original entry from us.
+                       // Only call the notifier when the item has some content relevant change.
+                       if ($item['origin'] && in_array('edited', array_keys($fields))) {
                                Worker::add(PRIORITY_HIGH, "Notifier", 'edit_post', $item['id']);
                        }
                }
@@ -101,6 +467,32 @@ class Item extends BaseObject
                dba::close($items);
        }
 
+       /**
+        * @brief Delete an item for an user and notify others about it - if it was ours
+        *
+        * @param array $condition The condition for finding the item entries
+        * @param integer $uid User who wants to delete this item
+        */
+       public static function deleteForUser($condition, $uid)
+       {
+               if ($uid == 0) {
+                       return;
+               }
+
+               $items = dba::select('item', ['id', 'uid'], $condition);
+               while ($item = dba::fetch($items)) {
+                       // "Deleting" global items just means hiding them
+                       if ($item['uid'] == 0) {
+                               dba::update('user-item', ['hidden' => true], ['iid' => $item['id'], 'uid' => $uid], true);
+                       } elseif ($item['uid'] == $uid) {
+                               self::deleteById($item['id'], PRIORITY_HIGH);
+                       } else {
+                               logger('Wrong ownership. Not deleting item ' . $item['id']);
+                       }
+               }
+               dba::close($items);
+       }
+
        /**
         * @brief Delete an item and notify others about it - if it was ours
         *
@@ -109,18 +501,20 @@ class Item extends BaseObject
         *
         * @return boolean success
         */
-       public static function deleteById($item_id, $priority = PRIORITY_HIGH)
+       private static function deleteById($item_id, $priority = PRIORITY_HIGH)
        {
                // locate item to be deleted
-               $fields = ['id', 'uid', 'parent', 'parent-uri', 'origin', 'deleted',
-                       'file', 'resource-id', 'event-id', 'attach',
+               $fields = ['id', 'uri', 'uid', 'parent', 'parent-uri', 'origin',
+                       'deleted', 'file', 'resource-id', 'event-id', 'attach',
                        'verb', 'object-type', 'object', 'target', 'contact-id'];
                $item = dba::selectFirst('item', $fields, ['id' => $item_id]);
                if (!DBM::is_result($item)) {
+                       logger('Item with ID ' . $item_id . " hasn't been found.", LOGGER_DEBUG);
                        return false;
                }
 
                if ($item['deleted']) {
+                       logger('Item with ID ' . $item_id . ' has already been deleted.', LOGGER_DEBUG);
                        return false;
                }
 
@@ -129,8 +523,6 @@ class Item extends BaseObject
                        $parent = ['origin' => false];
                }
 
-               logger('delete item: ' . $item['id'], LOGGER_DEBUG);
-
                // clean up categories and tags so they don't end up as orphans
 
                $matches = false;
@@ -183,16 +575,34 @@ class Item extends BaseObject
                Term::insertFromFileFieldByItemId($item['id']);
                self::deleteThread($item['id'], $item['parent-uri']);
 
+               if (!dba::exists('item', ["`uri` = ? AND `uid` != 0 AND NOT `deleted`", $item['uri']])) {
+                       self::delete(['uri' => $item['uri'], 'uid' => 0, 'deleted' => false], $priority);
+               }
+
                // If it's the parent of a comment thread, kill all the kids
                if ($item['id'] == $item['parent']) {
-                       self::delete(['parent' => $item['parent']], $priority);
+                       self::delete(['parent' => $item['parent'], 'deleted' => false], $priority);
                }
 
-               // send the notification upstream/downstream
+               // Is it our comment and/or our thread?
                if ($item['origin'] || $parent['origin']) {
+
+                       // When we delete the original post we will delete all existing copies on the server as well
+                       self::delete(['uri' => $item['uri'], 'deleted' => false], $priority);
+
+                       // send the notification upstream/downstream
                        Worker::add(['priority' => $priority, 'dont_fork' => true], "Notifier", "drop", intval($item['id']));
+               } elseif ($item['uid'] != 0) {
+
+                       // When we delete just our local user copy of an item, we have to set a marker to hide it
+                       $global_item = dba::selectFirst('item', ['id'], ['uri' => $item['uri'], 'uid' => 0, 'deleted' => false]);
+                       if (DBM::is_result($global_item)) {
+                               dba::update('user-item', ['hidden' => true], ['iid' => $global_item['id'], 'uid' => $item['uid']], true);
+                       }
                }
 
+               logger('Item with ID ' . $item_id . " has been deleted.", LOGGER_DEBUG);
+
                return true;
        }
 
@@ -333,12 +743,18 @@ class Item extends BaseObject
                        $item['origin'] = 1;
                        $item['network'] = NETWORK_DFRN;
                        $item['protocol'] = PROTOCOL_DFRN;
+
+                       if (is_int($notify)) {
+                               $priority = $notify;
+                       } else {
+                               $priority = PRIORITY_HIGH;
+                       }
                } else {
                        $item['network'] = trim(defaults($item, 'network', NETWORK_PHANTOM));
                }
 
                $item['guid'] = self::guid($item, $notify);
-               $item['uri'] = notags(trim(defaults($item, 'uri', item_new_uri($a->get_hostname(), $item['uid'], $item['guid']))));
+               $item['uri'] = notags(trim(defaults($item, 'uri', self::newURI($item['uid'], $item['guid']))));
 
                // Store conversation data
                $item = Conversation::insert($item);
@@ -355,6 +771,13 @@ class Item extends BaseObject
                        unset($item['dsprsig']);
                }
 
+               if (!empty($item['diaspora_signed_text'])) {
+                       $diaspora_signed_text = $item['diaspora_signed_text'];
+                       unset($item['diaspora_signed_text']);
+               } else {
+                       $diaspora_signed_text = '';
+               }
+
                // Converting the plink
                /// @TODO Check if this is really still needed
                if ($item['network'] == NETWORK_OSTATUS) {
@@ -508,6 +931,14 @@ class Item extends BaseObject
                        return 0;
                }
 
+               //unset($item['author-link']);
+               unset($item['author-name']);
+               unset($item['author-avatar']);
+
+               //unset($item['owner-link']);
+               unset($item['owner-name']);
+               unset($item['owner-avatar']);
+
                if ($item['network'] == NETWORK_PHANTOM) {
                        logger('Missing network. Called by: '.System::callstack(), LOGGER_DEBUG);
 
@@ -553,7 +984,7 @@ class Item extends BaseObject
 
                        $fields = ['uri', 'parent-uri', 'id', 'deleted',
                                'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid',
-                               'wall', 'private', 'forum_mode'];
+                               'wall', 'private', 'forum_mode', 'origin'];
                        $condition = ['uri' => $item['parent-uri'], 'uid' => $item['uid']];
                        $params = ['order' => ['id' => false]];
                        $parent = dba::selectFirst('item', $fields, $condition, $params);
@@ -805,6 +1236,12 @@ class Item extends BaseObject
                                                'signature' => $dsprsig->signature, 'signer' => $dsprsig->signer]);
                }
 
+               if (!empty($diaspora_signed_text)) {
+                       // Formerly we stored the signed text, the signature and the author in different fields.
+                       // We now store the raw data so that we are more flexible.
+                       dba::insert('sign', ['iid' => $current_post, 'signed_text' => $diaspora_signed_text]);
+               }
+
                $deleted = self::tagDeliver($item['uid'], $current_post);
 
                /*
@@ -848,7 +1285,9 @@ class Item extends BaseObject
                check_user_notification($current_post);
 
                if ($notify) {
-                       Worker::add(['priority' => PRIORITY_HIGH, 'dont_fork' => true], "Notifier", $notify_type, $current_post);
+                       Worker::add(['priority' => $priority, 'dont_fork' => true], "Notifier", $notify_type, $current_post);
+               } elseif (!empty($parent) && $parent['origin']) {
+                       Worker::add(['priority' => PRIORITY_HIGH, 'dont_fork' => true], "Notifier", "comment-import", $current_post);
                }
 
                return $current_post;
@@ -857,9 +1296,10 @@ class Item extends BaseObject
        /**
         * @brief Distributes public items to the receivers
         *
-        * @param integer $itemid Item ID that should be added
+        * @param integer $itemid      Item ID that should be added
+        * @param string  $signed_text Original text (for Diaspora signatures), JSON encoded.
         */
-       public static function distribute($itemid)
+       public static function distribute($itemid, $signed_text = '')
        {
                $condition = ["`id` IN (SELECT `parent` FROM `item` WHERE `id` = ?)", $itemid];
                $parent = dba::selectFirst('item', ['owner-id'], $condition);
@@ -894,14 +1334,22 @@ class Item extends BaseObject
                        $users[$contact['uid']] = $contact['uid'];
                }
 
+               $origin_uid = 0;
+
                if ($item['uri'] != $item['parent-uri']) {
-                       $parents = dba::select('item', ['uid'], ["`uri` = ? AND `uid` != 0", $item['parent-uri']]);
+                       $parents = dba::select('item', ['uid', 'origin'], ["`uri` = ? AND `uid` != 0", $item['parent-uri']]);
                        while ($parent = dba::fetch($parents)) {
                                $users[$parent['uid']] = $parent['uid'];
+                               if ($parent['origin'] && !$item['origin']) {
+                                       $origin_uid = $parent['uid'];
+                               }
                        }
                }
 
                foreach ($users as $uid) {
+                       if ($origin_uid == $uid) {
+                               $item['diaspora_signed_text'] = $signed_text;
+                       }
                        self::storeForUser($itemid, $item, $uid);
                }
        }
@@ -1047,8 +1495,9 @@ class Item extends BaseObject
                }
 
                // Is this a shadow entry?
-               if ($item['uid'] == 0)
+               if ($item['uid'] == 0) {
                        return;
+               }
 
                // Is there a shadow parent?
                if (!dba::exists('item', ['uri' => $item['parent-uri'], 'uid' => 0])) {
@@ -1088,10 +1537,8 @@ class Item extends BaseObject
 
                // If this was a comment to a Diaspora post we don't get our comment back.
                // This means that we have to distribute the comment by ourselves.
-               if ($origin) {
-                       if (dba::exists('item', ['id' => $parent, 'network' => NETWORK_DIASPORA])) {
-                               self::distribute($public_shadow);
-                       }
+               if ($origin && dba::exists('item', ['id' => $parent, 'network' => NETWORK_DIASPORA])) {
+                       self::distribute($public_shadow);
                }
        }
 
@@ -1102,14 +1549,14 @@ class Item extends BaseObject
         */
        private static function addLanguageInPostopts(&$item)
        {
+               $postopts = "";
+
                if (!empty($item['postopts'])) {
                        if (strstr($item['postopts'], 'lang=')) {
                                // do not override
                                return;
                        }
                        $postopts = $item['postopts'];
-               } else {
-                       $postopts = "";
                }
 
                $naked_body = Text\BBCode::toPlaintext($item['body'], false);
@@ -1160,6 +1607,29 @@ class Item extends BaseObject
                return $guid_prefix.$host_hash;
        }
 
+       /**
+        * generate an unique URI
+        *
+        * @param integer $uid User id
+        * @param string $guid An existing GUID (Otherwise it will be generated)
+        *
+        * @return string
+        */
+       public static function newURI($uid, $guid = "")
+       {
+               if ($guid == "") {
+                       $guid = get_guid(32);
+               }
+
+               $hostname = self::getApp()->get_hostname();
+
+               $user = dba::selectFirst('user', ['nickname'], ['uid' => $uid]);
+
+               $uri = "urn:X-dfrn:" . $hostname . ':' . $user['nickname'] . ':' . $guid;
+
+               return $uri;
+       }
+
        /**
         * @brief Set "success_update" and "last-item" to the date of the last time we heard from this contact
         *
@@ -1208,7 +1678,7 @@ class Item extends BaseObject
                }
        }
 
-       private static function setHashtags(&$item)
+       public static function setHashtags(&$item)
        {
 
                $tags = get_tags($item["body"]);
@@ -1409,8 +1879,7 @@ class Item extends BaseObject
                $forum_mode = ($prvgroup ? 2 : 1);
 
                $fields = ['wall' => true, 'origin' => true, 'forum_mode' => $forum_mode, 'contact-id' => $self['id'],
-                       'owner-id' => $owner_id, 'owner-name' => $self['name'], 'owner-link' => $self['url'],
-                       'owner-avatar' => $self['thumb'], 'private' => $private, 'allow_cid' => $user['allow_cid'],
+                       'owner-id' => $owner_id, 'owner-link' => $self['url'], 'private' => $private, 'allow_cid' => $user['allow_cid'],
                        'allow_gid' => $user['allow_gid'], 'deny_cid' => $user['deny_cid'], 'deny_gid' => $user['deny_gid']];
                dba::update('item', $fields, ['id' => $item_id]);
 
@@ -1477,7 +1946,7 @@ class Item extends BaseObject
                        if ($contact['network'] != NETWORK_FEED) {
                                $datarray["guid"] = get_guid(32);
                                unset($datarray["plink"]);
-                               $datarray["uri"] = item_new_uri($a->get_hostname(), $contact['uid'], $datarray["guid"]);
+                               $datarray["uri"] = self::newURI($contact['uid'], $datarray["guid"]);
                                $datarray["parent-uri"] = $datarray["uri"];
                                $datarray["thr-parent"] = $datarray["uri"];
                                $datarray["extid"] = NETWORK_DFRN;
@@ -1949,7 +2418,7 @@ EOT;
 
                $new_item = [
                        'guid'          => get_guid(32),
-                       'uri'           => item_new_uri(self::getApp()->get_hostname(), $item['uid']),
+                       'uri'           => self::newURI($item['uid']),
                        'uid'           => $item['uid'],
                        'contact-id'    => $item_contact_id,
                        'type'          => 'activity',
@@ -2001,7 +2470,7 @@ EOT;
        private static function addThread($itemid, $onlyshadow = false)
        {
                $fields = ['uid', 'created', 'edited', 'commented', 'received', 'changed', 'wall', 'private', 'pubmail',
-                       'moderated', 'visible', 'spam', 'starred', 'bookmark', 'contact-id',
+                       'moderated', 'visible', 'starred', 'bookmark', 'contact-id',
                        'deleted', 'origin', 'forum_mode', 'mention', 'network', 'author-id', 'owner-id'];
                $condition = ["`id` = ? AND (`parent` = ? OR `parent` = 0)", $itemid, $itemid];
                $item = dba::selectFirst('item', $fields, $condition);
@@ -2022,7 +2491,7 @@ EOT;
        private static function updateThread($itemid, $setmention = false)
        {
                $fields = ['uid', 'guid', 'title', 'body', 'created', 'edited', 'commented', 'received', 'changed',
-                       'wall', 'private', 'pubmail', 'moderated', 'visible', 'spam', 'starred', 'bookmark', 'contact-id',
+                       'wall', 'private', 'pubmail', 'moderated', 'visible', 'starred', 'bookmark', 'contact-id',
                        'deleted', 'origin', 'forum_mode', 'network', 'author-id', 'owner-id', 'rendered-html', 'rendered-hash'];
                $condition = ["`id` = ? AND (`parent` = ? OR `parent` = 0)", $itemid, $itemid];