X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FModel%2FItem.php;h=d41e84c5b9f44c5e6ff0b1d61cb15c9d542c56a3;hb=f9994548c1f1110c7f548e00fcf1b6ee42b9de3b;hp=0fcb445e0bb5d75362021edd02c83f58e1ee05f0;hpb=ba0d3b2435501a89500542b18e3e24e471eb8fb4;p=friendica.git diff --git a/src/Model/Item.php b/src/Model/Item.php index 0fcb445e0b..d41e84c5b9 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -34,7 +34,7 @@ use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\Database\DBStructure; use Friendica\DI; -use Friendica\Model\Post\Category; +use Friendica\Model\Post; use Friendica\Protocol\Activity; use Friendica\Protocol\ActivityPub; use Friendica\Protocol\Diaspora; @@ -71,8 +71,6 @@ class Item const PT_FETCHED = 75; const PT_PERSONAL_NOTE = 128; - const LOCK_INSERT = 'item-insert'; - // Field list that is used to display the items const DISPLAY_FIELDLIST = [ 'uid', 'id', 'parent', 'uri-id', 'uri', 'thr-parent', 'parent-uri', 'guid', 'network', 'gravity', @@ -235,6 +233,8 @@ class Item return $row; } + $row = DBA::castFields('item', $row); + // ---------------------- Transform item structure data ---------------------- // We prefer the data from the user's contact over the public one @@ -310,7 +310,7 @@ class Item if (!array_key_exists('verb', $row) || in_array($row['verb'], ['', Activity::POST, Activity::SHARE])) { // Build the file string out of the term entries if (array_key_exists('file', $row) && empty($row['file'])) { - $row['file'] = Category::getTextByURIId($row['internal-uri-id'], $row['internal-uid']); + $row['file'] = Post\Category::getTextByURIId($row['internal-uri-id'], $row['internal-uid']); } } @@ -911,6 +911,8 @@ class Item return false; } + $data_fields = $fields; + // To ensure the data integrity we do it in an transaction DBA::transaction(); @@ -967,6 +969,8 @@ class Item $notify_items = []; while ($item = DBA::fetch($items)) { + Post\User::update($item['uri-id'], $item['uid'], $data_fields); + if (empty($content_fields['verb']) || !in_array($content_fields['verb'], self::ACTIVITIES)) { if (!empty($content_fields['body'])) { $content_fields['raw-body'] = trim($content_fields['raw-body'] ?? $content_fields['body']); @@ -996,7 +1000,7 @@ class Item } if (!is_null($files)) { - Category::storeTextByURIId($item['uri-id'], $item['uid'], $files); + Post\Category::storeTextByURIId($item['uri-id'], $item['uid'], $files); if (!empty($item['file'])) { DBA::update('item', ['file' => ''], ['id' => $item['id']]); } @@ -1056,14 +1060,13 @@ class Item return; } - $items = self::select(['id', 'uid'], $condition); + $items = self::select(['id', 'uid', 'uri-id'], $condition); while ($item = self::fetch($items)) { + Post\User::update($item['uri-id'], $item['uid'], ['hidden' => true]); + // "Deleting" global items just means hiding them if ($item['uid'] == 0) { DBA::update('user-item', ['hidden' => true], ['iid' => $item['id'], 'uid' => $uid], true); - - // Delete notifications - DBA::delete('notify', ['iid' => $item['id'], 'uid' => $uid]); } elseif ($item['uid'] == $uid) { self::markForDeletionById($item['id'], PRIORITY_HIGH); } else { @@ -1151,14 +1154,11 @@ class Item } } - // Delete notifications - DBA::delete('notify', ['iid' => $item['id'], 'uid' => $item['uid']]); - // Set the item to "deleted" $item_fields = ['deleted' => true, 'edited' => DateTimeFormat::utcNow(), 'changed' => DateTimeFormat::utcNow()]; DBA::update('item', $item_fields, ['id' => $item['id']]); - Category::storeTextByURIId($item['uri-id'], $item['uid'], ''); + Post\Category::storeTextByURIId($item['uri-id'], $item['uid'], ''); self::deleteThread($item['id'], $item['parent-uri']); if (!self::exists(["`uri` = ? AND `uid` != 0 AND NOT `deleted`", $item['uri']])) { @@ -1167,9 +1167,6 @@ class Item Post\DeliveryData::delete($item['uri-id']); - if (!empty($item['icid']) && !self::exists(['icid' => $item['icid'], 'deleted' => false])) { - DBA::delete('item-content', ['id' => $item['icid']], ['cascade' => false]); - } // When the permission set will be used in photo and events as well, // this query here needs to be extended. // @todo Currently deactivated. We need the permission set in the deletion process. @@ -1184,13 +1181,16 @@ class Item } // Is it our comment and/or our thread? - if ($item['origin'] || $parent['origin']) { + if (($item['origin'] || $parent['origin']) && ($item['uid'] != 0)) { // When we delete the original post we will delete all existing copies on the server as well self::markForDeletion(['uri' => $item['uri'], 'deleted' => false], $priority); // send the notification upstream/downstream - Worker::add(['priority' => $priority, 'dont_fork' => true], "Notifier", Delivery::DELETION, intval($item['id'])); + if ($priority) { + Worker::add(['priority' => $priority, 'dont_fork' => true], "Notifier", Delivery::DELETION, intval($item['id'])); + } } elseif ($item['uid'] != 0) { + Post\User::update($item['uri-id'], $item['uid'], ['hidden' => true]); // When we delete just our local user copy of an item, we have to set a marker to hide it $global_item = self::selectFirst(['id'], ['uri' => $item['uri'], 'uid' => 0, 'deleted' => false]); @@ -1384,27 +1384,6 @@ class Item return false; } - // check for create date and expire time - $expire_interval = DI::config()->get('system', 'dbclean-expire-days', 0); - - $user = DBA::selectFirst('user', ['expire'], ['uid' => $item['uid']]); - if (DBA::isResult($user) && ($user['expire'] > 0) && (($user['expire'] < $expire_interval) || ($expire_interval == 0))) { - $expire_interval = $user['expire']; - } - - if (($expire_interval > 0) && !empty($item['created'])) { - $expire_date = time() - ($expire_interval * 86400); - $created_date = strtotime($item['created']); - if ($created_date < $expire_date) { - Logger::notice('Item created before expiration interval.', [ - 'created' => date('c', $created_date), - 'expired' => date('c', $expire_date), - '$item' => $item - ]); - return false; - } - } - if (!empty($item['author-id']) && Contact::isBlocked($item['author-id'])) { Logger::notice('Author is blocked node-wide', ['author-link' => $item['author-link'], 'item-uri' => $item['uri']]); return false; @@ -1448,6 +1427,38 @@ class Item return true; } + /** + * Check if the item array is too old + * + * @param array $item + * @return boolean item is too old + */ + public static function isTooOld(array $item) + { + // check for create date and expire time + $expire_interval = DI::config()->get('system', 'dbclean-expire-days', 0); + + $user = DBA::selectFirst('user', ['expire'], ['uid' => $item['uid']]); + if (DBA::isResult($user) && ($user['expire'] > 0) && (($user['expire'] < $expire_interval) || ($expire_interval == 0))) { + $expire_interval = $user['expire']; + } + + if (($expire_interval > 0) && !empty($item['created'])) { + $expire_date = time() - ($expire_interval * 86400); + $created_date = strtotime($item['created']); + if ($created_date < $expire_date) { + Logger::notice('Item created before expiration interval.', [ + 'created' => date('c', $created_date), + 'expired' => date('c', $expire_date), + '$item' => $item + ]); + return true; + } + } + + return false; + } + /** * Return the id of the given item array if it has been stored before * @@ -1555,7 +1566,7 @@ class Item $item['wall'] = 1; $item['origin'] = 1; $item['network'] = Protocol::DFRN; - $item['protocol'] = Conversation::PARCEL_DFRN; + $item['protocol'] = Conversation::PARCEL_DIRECT; if (is_int($notify)) { $priority = $notify; @@ -1862,7 +1873,7 @@ class Item // Attached file links if (!empty($item['file'])) { - Category::storeTextByURIId($item['uri-id'], $item['uid'], $item['file']); + Post\Category::storeTextByURIId($item['uri-id'], $item['uid'], $item['file']); } unset($item['file']); @@ -1881,23 +1892,16 @@ class Item Tag::storeFromBody($item['uri-id'], $body); } - // Remove all fields that aren't part of the item table - foreach ($item as $field => $value) { - if (!in_array($field, $structure['item'])) { - unset($item[$field]); + if (Post\User::insert($item['uri-id'], $item['uid'], $item)) { + // Remove all fields that aren't part of the item table + foreach ($item as $field => $value) { + if (!in_array($field, $structure['item'])) { + unset($item[$field]); + } } - } - - $locked = DI::lock()->acquire(self::LOCK_INSERT, 0); - if ($locked || DBA::lock('item')) { $condition = ['uri-id' => $item['uri-id'], 'uid' => $item['uid'], 'network' => $item['network']]; if (DBA::exists('item', $condition)) { - if ($locked) { - DI::lock()->release(self::LOCK_INSERT); - } else { - DBA::unlock(); - } Logger::notice('Item is already inserted - aborting', $condition); return 0; } @@ -1906,15 +1910,9 @@ class Item // When the item was successfully stored we fetch the ID of the item. $current_post = DBA::lastInsertId(); - if ($locked) { - DI::lock()->release(self::LOCK_INSERT); - } else { - DBA::unlock(); - } } else { - Logger::warning('Item lock had not been acquired'); - $result = false; - $current_post = 0; + Logger::notice('Post-User is already inserted - aborting', ['uid' => $item['uid'], 'uri-id' => $item['uri-id']]); + return 0; } if (empty($current_post) || !DBA::isResult($result)) { @@ -1994,6 +1992,9 @@ class Item // Distribute items to users who subscribed to their tags self::distributeByTags($item); + // Automatically reshare the item if the "remote_self" option is selected + self::autoReshare($item); + $transmit = $notify || ($item['visible'] && ($parent_origin || $item['origin'])); if ($transmit) { @@ -2798,6 +2799,31 @@ class Item return false; } + /** + * Automatically reshare the item if the "remote_self" option is selected + * + * @param array $item + * @return void + */ + private static function autoReshare(array $item) + { + if ($item['gravity'] != GRAVITY_PARENT) { + return; + } + + if (!DBA::exists('contact', ['id' => $item['contact-id'], 'remote_self' => Contact::MIRROR_NATIVE_RESHARE])) { + return; + } + + if (!in_array($item['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN])) { + return; + } + + Logger::info('Automatically reshare item', ['uid' => $item['uid'], 'id' => $item['id'], 'guid' => $item['guid'], 'uri-id' => $item['uri-id']]); + + Item::performActivity($item['id'], 'announce', $item['uid']); + } + public static function isRemoteSelf($contact, &$datarray) { if (!$contact['remote_self']) { @@ -2829,7 +2855,7 @@ class Item $datarray2 = $datarray; Logger::info('remote-self start', ['contact' => $contact['url'], 'remote_self'=> $contact['remote_self'], 'item' => $datarray]); - if ($contact['remote_self'] == 2) { + if ($contact['remote_self'] == Contact::MIRROR_OWN_POST) { $self = DBA::selectFirst('contact', ['id', 'name', 'url', 'thumb'], ['uid' => $contact['uid'], 'self' => true]); if (DBA::isResult($self)) { @@ -3041,7 +3067,7 @@ class Item return $recipients; } - public static function expire($uid, $days, $network = "", $force = false) + public static function expire(int $uid, int $days, string $network = "", bool $force = false) { if (!$uid || ($days < 1)) { return; @@ -3087,6 +3113,8 @@ class Item $expired = 0; + $priority = DI::config()->get('system', 'expire-notify-priority'); + while ($item = Item::fetch($items)) { // don't expire filed items @@ -3106,7 +3134,7 @@ class Item continue; } - self::markForDeletionById($item['id'], PRIORITY_LOW); + self::markForDeletionById($item['id'], $priority); ++$expired; } @@ -3501,20 +3529,21 @@ class Item */ public static function putInCache(&$item, $update = false) { - $body = $item["body"]; + // Save original body to prevent addons to modify it + $body = $item['body']; $rendered_hash = $item['rendered-hash'] ?? ''; $rendered_html = $item['rendered-html'] ?? ''; if ($rendered_hash == '' - || $rendered_html == "" - || $rendered_hash != hash("md5", $item["body"]) - || DI::config()->get("system", "ignore_cache") + || $rendered_html == '' + || $rendered_hash != hash('md5', BBCode::VERSION . '::' . $body) + || DI::config()->get('system', 'ignore_cache') ) { self::addRedirToImageTags($item); - $item["rendered-html"] = BBCode::convert($item["body"]); - $item["rendered-hash"] = hash("md5", $item["body"]); + $item['rendered-html'] = BBCode::convert($item['body']); + $item['rendered-hash'] = hash('md5', BBCode::VERSION . '::' . $body); $hook_data = ['item' => $item, 'rendered-html' => $item['rendered-html'], 'rendered-hash' => $item['rendered-hash']]; Hook::callAll('put_item_in_cache', $hook_data); @@ -3523,27 +3552,27 @@ class Item unset($hook_data); // Force an update if the generated values differ from the existing ones - if ($rendered_hash != $item["rendered-hash"]) { + if ($rendered_hash != $item['rendered-hash']) { $update = true; } // Only compare the HTML when we forcefully ignore the cache - if (DI::config()->get("system", "ignore_cache") && ($rendered_html != $item["rendered-html"])) { + if (DI::config()->get('system', 'ignore_cache') && ($rendered_html != $item['rendered-html'])) { $update = true; } - if ($update && !empty($item["id"])) { + if ($update && !empty($item['id'])) { self::update( [ - 'rendered-html' => $item["rendered-html"], - 'rendered-hash' => $item["rendered-hash"] + 'rendered-html' => $item['rendered-html'], + 'rendered-hash' => $item['rendered-hash'] ], - ['id' => $item["id"]] + ['id' => $item['id']] ); } } - $item["body"] = $body; + $item['body'] = $body; } /** @@ -3980,12 +4009,12 @@ class Item // The causer is set during a thread completion, for example because of a reshare. It countains the responsible actor. if (!empty($item['causer-id']) && Contact\User::isBlocked($item['causer-id'], $user_id)) { - Logger::notice('Causer is blocked by user', ['causer-link' => $item['causer-link'], 'uid' => $user_id, 'item-uri' => $item['uri']]); + Logger::notice('Causer is blocked by user', ['causer-link' => $item['causer-link'] ?? $item['causer-id'], 'uid' => $user_id, 'item-uri' => $item['uri']]); return false; } if (!empty($item['causer-id']) && ($item['gravity'] === GRAVITY_PARENT) && Contact\User::isIgnored($item['causer-id'], $user_id)) { - Logger::notice('Causer is ignored by user', ['causer-link' => $item['causer-link'], 'uid' => $user_id, 'item-uri' => $item['uri']]); + Logger::notice('Causer is ignored by user', ['causer-link' => $item['causer-link'] ?? $item['causer-id'], 'uid' => $user_id, 'item-uri' => $item['uri']]); return false; }