X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FModel%2FItem.php;h=ab291a3306dd1f9372b1e7981fc8e0d496139cc7;hb=8564eeec4ed28697eac40a6f56e89ab9ef89144d;hp=efcf05ad6afc9ad2ca2a275f2d91d1e3df657786;hpb=29f7ebe307c22b275466390937b82ccb3820fb1c;p=friendica.git diff --git a/src/Model/Item.php b/src/Model/Item.php index efcf05ad6a..ab291a3306 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -7,6 +7,7 @@ namespace Friendica\Model; use Friendica\BaseObject; +use Friendica\Content\Text; use Friendica\Core\Addon; use Friendica\Core\Config; use Friendica\Core\L10n; @@ -29,7 +30,6 @@ use Text_LanguageDetect; require_once 'boot.php'; require_once 'include/items.php'; require_once 'include/text.php'; -require_once 'include/event.php'; class Item extends BaseObject { @@ -52,17 +52,24 @@ class Item extends BaseObject return false; } + // To ensure the data integrity we do it in an transaction + dba::transaction(); + + // We cannot simply expand the condition to check for origin entries + // The condition needn't to be a simple array but could be a complex condition. + // And we have to execute this query before the update to ensure to fetch the same data. + $items = dba::select('item', ['id', 'origin'], $condition); + $success = dba::update('item', $fields, $condition); if (!$success) { + dba::close($items); + dba::rollback(); return false; } $rows = dba::affected_rows(); - // We cannot simply expand the condition to check for origin entries - // The condition needn't to be a simple array but could be a complex condition. - $items = dba::select('item', ['id', 'origin'], $condition); while ($item = dba::fetch($items)) { Term::insertFromTagFieldByItemId($item['id']); Term::insertFromFileFieldByItemId($item['id']); @@ -74,6 +81,8 @@ class Item extends BaseObject } } + dba::close($items); + dba::commit(); return $rows; } @@ -98,7 +107,7 @@ class Item extends BaseObject * @param integer $item_id Item ID that should be delete * @param integer $priority Priority for the notification * - * @return $boolean success + * @return boolean success */ public static function deleteById($item_id, $priority = PRIORITY_HIGH) { @@ -153,7 +162,7 @@ class Item extends BaseObject // If item is a link to an event, delete the event. if (intval($item['event-id'])) { - event_delete($item['event-id']); + Event::delete($item['event-id']); } // If item has attachments, drop them @@ -346,6 +355,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) { @@ -479,14 +495,20 @@ class Item extends BaseObject // The contact-id should be set before "self::insert" was called - but there seems to be issues sometimes $item["contact-id"] = self::contactId($item); - $item['author-id'] = defaults($item, 'author-id', Contact::getIdForURL($item["author-link"])); + $default = ['url' => $item['author-link'], 'name' => $item['author-name'], + 'photo' => $item['author-avatar'], 'network' => $item['network']]; + + $item['author-id'] = defaults($item, 'author-id', Contact::getIdForURL($item["author-link"], 0, false, $default)); if (Contact::isBlocked($item["author-id"])) { logger('Contact '.$item["author-id"].' is blocked, item '.$item["uri"].' will not be stored'); return 0; } - $item['owner-id'] = defaults($item, 'owner-id', Contact::getIdForURL($item["owner-link"])); + $default = ['url' => $item['owner-link'], 'name' => $item['owner-name'], + 'photo' => $item['owner-avatar'], 'network' => $item['network']]; + + $item['owner-id'] = defaults($item, 'owner-id', Contact::getIdForURL($item["owner-link"], 0, false, $default)); if (Contact::isBlocked($item["owner-id"])) { logger('Contact '.$item["owner-id"].' is blocked, item '.$item["uri"].' will not be stored'); @@ -538,7 +560,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); @@ -790,6 +812,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); /* @@ -834,11 +862,125 @@ class Item extends BaseObject if ($notify) { Worker::add(['priority' => PRIORITY_HIGH, '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; } + /** + * @brief Distributes public items to the receivers + * + * @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, $signed_text = '') + { + $condition = ["`id` IN (SELECT `parent` FROM `item` WHERE `id` = ?)", $itemid]; + $parent = dba::selectFirst('item', ['owner-id'], $condition); + if (!DBM::is_result($parent)) { + return; + } + + // Only distribute public items from native networks + $condition = ['id' => $itemid, 'uid' => 0, + 'network' => [NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_OSTATUS, ""], + 'visible' => true, 'deleted' => false, 'moderated' => false, 'private' => false]; + $item = dba::selectFirst('item', [], ['id' => $itemid]); + if (!DBM::is_result($item)) { + return; + } + + unset($item['id']); + unset($item['parent']); + unset($item['mention']); + unset($item['wall']); + unset($item['origin']); + unset($item['starred']); + unset($item['rendered-hash']); + unset($item['rendered-html']); + + $users = []; + + $condition = ["`nurl` IN (SELECT `nurl` FROM `contact` WHERE `id` = ?) AND `uid` != 0 AND NOT `blocked` AND `rel` IN (?, ?)", + $parent['owner-id'], CONTACT_IS_SHARING, CONTACT_IS_FRIEND]; + $contacts = dba::select('contact', ['uid'], $condition); + while ($contact = dba::fetch($contacts)) { + $users[$contact['uid']] = $contact['uid']; + } + + $origin_uid = 0; + + if ($item['uri'] != $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); + } + } + + /** + * @brief Store public items for the receivers + * + * @param integer $itemid Item ID that should be added + * @param array $item The item entry that will be stored + * @param integer $uid The user that will receive the item entry + */ + private static function storeForUser($itemid, $item, $uid) + { + $item['uid'] = $uid; + $item['origin'] = 0; + $item['wall'] = 0; + if ($item['uri'] == $item['parent-uri']) { + $item['contact-id'] = Contact::getIdForURL($item['owner-link'], $uid); + } else { + $item['contact-id'] = Contact::getIdForURL($item['author-link'], $uid); + } + + if (empty($item['contact-id'])) { + $self = dba::selectFirst('contact', ['id'], ['self' => true, 'uid' => $uid]); + if (!DBM::is_result($self)) { + return; + } + $item['contact-id'] = $self['id']; + } + + if (in_array($item['type'], ["net-comment", "wall-comment"])) { + $item['type'] = 'remote-comment'; + } elseif ($item['type'] == 'wall') { + $item['type'] = 'remote'; + } + + /// @todo Handling of "event-id" + + $notify = false; + if ($item['uri'] == $item['parent-uri']) { + $contact = dba::selectFirst('contact', [], ['id' => $item['contact-id'], 'self' => false]); + if (DBM::is_result($contact)) { + $notify = self::isRemoteSelf($contact, $item); + } + } + + $distributed = self::insert($item, false, $notify, true); + + if (!$distributed) { + logger("Distributed public item " . $itemid . " for user " . $uid . " wasn't stored", LOGGER_DEBUG); + } else { + logger("Distributed public item " . $itemid . " for user " . $uid . " with id " . $distributed, LOGGER_DEBUG); + } + } + /** * @brief Add a shadow entry for a given item id that is a thread starter * @@ -850,8 +992,8 @@ class Item extends BaseObject */ public static function addShadow($itemid) { - $fields = ['uid', 'wall', 'private', 'moderated', 'visible', 'contact-id', 'deleted', 'network', 'author-id', 'owner-id']; - $condition = ["`id` = ? AND (`parent` = ? OR `parent` = 0)", $itemid, $itemid]; + $fields = ['uid', 'private', 'moderated', 'visible', 'deleted', 'network']; + $condition = ['id' => $itemid, 'parent' => [0, $itemid]]; $item = dba::selectFirst('item', $fields, $condition); if (!DBM::is_result($item)) { @@ -873,35 +1015,22 @@ class Item extends BaseObject return; } - // Only do these checks if the post isn't a wall post - if (!$item["wall"]) { - // Check, if hide-friends is activated - then don't do a shadow entry - if (dba::exists('profile', ['is-default' => true, 'uid' => $item['uid'], 'hide-friends' => true])) { - return; - } - - // Check if the contact is hidden or blocked - if (!dba::exists('contact', ['hidden' => false, 'blocked' => false, 'id' => $item['contact-id']])) { - return; - } - } - - // Only add a shadow, if the profile isn't hidden - if (dba::exists('user', ['uid' => $item['uid'], 'hidewall' => true])) { - return; - } - $item = dba::selectFirst('item', [], ['id' => $itemid]); - if (DBM::is_result($item) && ($item["allow_cid"] == '') && ($item["allow_gid"] == '') && + if (DBM::is_result($item) && ($item["allow_cid"] == '') && ($item["allow_gid"] == '') && ($item["deny_cid"] == '') && ($item["deny_gid"] == '')) { if (!dba::exists('item', ['uri' => $item['uri'], 'uid' => 0])) { // Preparing public shadow (removing user specific data) - unset($item['id']); $item['uid'] = 0; - $item['origin'] = 0; - $item['wall'] = 0; + unset($item['id']); + unset($item['parent']); + unset($item['wall']); + unset($item['mention']); + unset($item['origin']); + unset($item['starred']); + unset($item['rendered-hash']); + unset($item['rendered-html']); if ($item['uri'] == $item['parent-uri']) { $item['contact-id'] = Contact::getIdForURL($item['owner-link']); } else { @@ -955,11 +1084,20 @@ class Item extends BaseObject return; } + // Save "origin" and "parent" state + $origin = $item['origin']; + $parent = $item['parent']; + // Preparing public shadow (removing user specific data) - unset($item['id']); $item['uid'] = 0; - $item['origin'] = 0; - $item['wall'] = 0; + unset($item['id']); + unset($item['parent']); + unset($item['wall']); + unset($item['mention']); + unset($item['origin']); + unset($item['starred']); + unset($item['rendered-hash']); + unset($item['rendered-html']); $item['contact-id'] = Contact::getIdForURL($item['author-link']); if (in_array($item['type'], ["net-comment", "wall-comment"])) { @@ -971,6 +1109,14 @@ class Item extends BaseObject $public_shadow = self::insert($item, false, false, true); logger("Stored public shadow for comment ".$item['uri']." under id ".$public_shadow, LOGGER_DEBUG); + + // 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); + } + } } /** @@ -978,35 +1124,35 @@ class Item extends BaseObject * if possible and not already present. * Expects "body" element to exist in $arr. */ - private static function addLanguageInPostopts(&$arr) + private static function addLanguageInPostopts(&$item) { - if (x($arr, 'postopts')) { - if (strstr($arr['postopts'], 'lang=')) { + if (!empty($item['postopts'])) { + if (strstr($item['postopts'], 'lang=')) { // do not override return; } - $postopts = $arr['postopts']; + $postopts = $item['postopts']; } else { $postopts = ""; } - $naked_body = preg_replace('/\[(.+?)\]/','', $arr['body']); - $l = new Text_LanguageDetect(); - $lng = $l->detect($naked_body, 3); + $naked_body = Text\BBCode::toPlaintext($item['body'], false); + + $languages = (new Text_LanguageDetect())->detect($naked_body, 3); - if (sizeof($lng) > 0) { - if ($postopts != "") { + if (sizeof($languages) > 0) { + if ($postopts != '') { $postopts .= '&'; // arbitrary separator, to be reviewed } $postopts .= 'lang='; $sep = ""; - foreach ($lng as $language => $score) { + foreach ($languages as $language => $score) { $postopts .= $sep . $language . ";" . $score; $sep = ':'; } - $arr['postopts'] = $postopts; + $item['postopts'] = $postopts; } } @@ -1307,20 +1453,24 @@ class Item extends BaseObject // Prevent the forwarding of posts that are forwarded if ($datarray["extid"] == NETWORK_DFRN) { + logger('Already forwarded', LOGGER_DEBUG); return false; } // Prevent to forward already forwarded posts if ($datarray["app"] == $a->get_hostname()) { + logger('Already forwarded (second test)', LOGGER_DEBUG); return false; } // Only forward posts if ($datarray["verb"] != ACTIVITY_POST) { + logger('No post', LOGGER_DEBUG); return false; } if (($contact['network'] != NETWORK_FEED) && $datarray['private']) { + logger('Not public', LOGGER_DEBUG); return false; } @@ -1342,6 +1492,10 @@ class Item extends BaseObject unset($datarray['created']); unset($datarray['edited']); + + unset($datarray['network']); + unset($datarray['owner-id']); + unset($datarray['author-id']); } if ($contact['network'] != NETWORK_FEED) { @@ -1349,7 +1503,8 @@ class Item extends BaseObject unset($datarray["plink"]); $datarray["uri"] = item_new_uri($a->get_hostname(), $contact['uid'], $datarray["guid"]); $datarray["parent-uri"] = $datarray["uri"]; - $datarray["extid"] = $contact['network']; + $datarray["thr-parent"] = $datarray["uri"]; + $datarray["extid"] = NETWORK_DFRN; $urlpart = parse_url($datarray2['author-link']); $datarray["app"] = $urlpart["host"]; } else { @@ -1359,10 +1514,11 @@ class Item extends BaseObject if ($contact['network'] != NETWORK_FEED) { // Store the original post - $r = self::insert($datarray2, false, false); - logger('remote-self post original item - Contact '.$contact['url'].' return '.$r.' Item '.print_r($datarray2, true), LOGGER_DEBUG); + $result = self::insert($datarray2, false, false); + logger('remote-self post original item - Contact '.$contact['url'].' return '.$result.' Item '.print_r($datarray2, true), LOGGER_DEBUG); } else { $datarray["app"] = "Feed"; + $result = true; } // Trigger automatic reactions for addons @@ -1372,7 +1528,7 @@ class Item extends BaseObject $_SESSION["authenticated"] = true; $_SESSION["uid"] = $contact['uid']; - return true; + return $result; } /** @@ -1891,7 +2047,7 @@ EOT; { $fields = ['uid', 'guid', 'title', 'body', 'created', 'edited', 'commented', 'received', 'changed', 'wall', 'private', 'pubmail', 'moderated', 'visible', 'spam', 'starred', 'bookmark', 'contact-id', - 'deleted', 'origin', 'forum_mode', 'network', 'rendered-html', 'rendered-hash']; + 'deleted', 'origin', 'forum_mode', 'network', 'author-id', 'owner-id', 'rendered-html', 'rendered-hash']; $condition = ["`id` = ? AND (`parent` = ? OR `parent` = 0)", $itemid, $itemid]; $item = dba::selectFirst('item', $fields, $condition); @@ -1915,7 +2071,7 @@ EOT; $result = dba::update('thread', $fields, ['iid' => $itemid]); - logger("Update thread for item ".$itemid." - guid ".$item["guid"]." - ".(int)$result." ".print_r($item, true), LOGGER_DEBUG); + logger("Update thread for item ".$itemid." - guid ".$item["guid"]." - ".(int)$result, LOGGER_DEBUG); // Updating a shadow item entry $items = dba::selectFirst('item', ['id'], ['guid' => $item['guid'], 'uid' => 0]);