X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;ds=sidebyside;f=src%2FModel%2FItem.php;h=3f3de8f8f1f405e11a497508c3da3b3232dd567e;hb=e93fba51362a8cc212cb42542fd013ffd28b1164;hp=9501c8e5d240519caed3781703baaa95e3a2e0bf;hpb=73b448c82926e384c110434de793c8cc55c5ddbc;p=friendica.git diff --git a/src/Model/Item.php b/src/Model/Item.php index 9501c8e5d2..3f3de8f8f1 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -6,7 +6,6 @@ namespace Friendica\Model; -use Friendica\BaseObject; use Friendica\Content\Text\BBCode; use Friendica\Content\Text\HTML; use Friendica\Core\Config; @@ -21,11 +20,11 @@ use Friendica\Core\Session; use Friendica\Core\System; use Friendica\Core\Worker; use Friendica\Database\DBA; +use Friendica\DI; use Friendica\Protocol\Activity; use Friendica\Protocol\ActivityPub; use Friendica\Protocol\Diaspora; use Friendica\Protocol\OStatus; -use Friendica\Util\ACLFormatter; use Friendica\Util\DateTimeFormat; use Friendica\Util\Map; use Friendica\Util\Network; @@ -35,7 +34,7 @@ use Friendica\Util\XML; use Friendica\Worker\Delivery; use Text_LanguageDetect; -class Item extends BaseObject +class Item { // Posting types, inspired by https://www.w3.org/TR/activitystreams-vocabulary/#object-types const PT_ARTICLE = 0; @@ -58,7 +57,7 @@ class Item extends BaseObject 'author-id', 'author-link', 'author-name', 'author-avatar', 'author-network', 'owner-id', 'owner-link', 'owner-name', 'owner-avatar', 'owner-network', 'contact-id', 'contact-uid', 'contact-link', 'contact-name', 'contact-avatar', - 'writable', 'self', 'cid', 'alias', + 'writable', 'self', 'cid', 'alias', 'pinned', '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', @@ -114,6 +113,80 @@ class Item extends BaseObject return self::$legacy_mode; } + /** + * Set the pinned state of an item + * + * @param integer $iid Item ID + * @param integer $uid User ID + * @param boolean $pinned Pinned state + */ + public static function setPinned(int $iid, int $uid, bool $pinned) + { + DBA::update('user-item', ['pinned' => $pinned], ['iid' => $iid, 'uid' => $uid], true); + } + + /** + * Get the pinned state + * + * @param integer $iid Item ID + * @param integer $uid User ID + * + * @return boolean pinned state + */ + public static function getPinned(int $iid, int $uid) + { + $useritem = DBA::selectFirst('user-item', ['pinned'], ['iid' => $iid, 'uid' => $uid]); + if (!DBA::isResult($useritem)) { + return false; + } + return (bool)$useritem['pinned']; + } + + /** + * @brief Select pinned 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 + * @throws \Exception + */ + public static function selectPinned(int $uid, array $selected = [], array $condition = [], $params = []) + { + $useritems = DBA::select('user-item', ['iid'], ['uid' => $uid, 'pinned' => true]); + if (!DBA::isResult($useritems)) { + return $useritems; + } + + $pinned = []; + while ($useritem = self::fetch($useritems)) { + $pinned[] = $useritem['iid']; + } + DBA::close($useritems); + + if (empty($pinned)) { + return []; + } + + if (empty($condition) || !is_array($condition)) { + $condition = ['iid' => $pinned]; + } else { + reset($condition); + $first_key = key($condition); + if (!is_int($first_key)) { + $condition['iid'] = $pinned; + } else { + $values_string = substr(str_repeat("?, ", count($pinned)), 0, -2); + $condition[0] = '(' . $condition[0] . ") AND `iid` IN (" . $values_string . ")"; + $condition = array_merge($condition, $pinned); + } + } + + return self::selectThreadForUser($uid, $selected, $condition, $params); + } + /** * @brief returns an activity index from an activity string * @@ -217,12 +290,10 @@ class Item extends BaseObject $row['object-type'] = Activity\ObjectType::NOTE; } } elseif (array_key_exists('verb', $row) && in_array($row['verb'], ['', Activity::POST, Activity::SHARE])) { - // Posts don't have an object or target - but having tags or files. + // Posts don't have a target - but having tags or files. // We safe some performance by building tag and file strings only here. - // We remove object and target since they aren't used for this type. - if (array_key_exists('object', $row)) { - $row['object'] = ''; - } + // We remove the target since they aren't used for this type. + // In mail posts we do store some mail header data in the object. if (array_key_exists('target', $row)) { $row['target'] = ''; } @@ -585,7 +656,7 @@ class Item extends BaseObject 'iaid' => 'internal-iaid']; if ($usermode) { - $fields['user-item'] = ['ignored' => 'internal-user-ignored']; + $fields['user-item'] = ['pinned', 'ignored' => 'internal-user-ignored']; } $fields['item-activity'] = ['activity', 'activity' => 'internal-activity']; @@ -1011,6 +1082,9 @@ class Item extends BaseObject // "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::deleteById($item['id'], PRIORITY_HIGH); } else { @@ -1101,6 +1175,9 @@ class Item extends BaseObject // Delete tags that had been attached to other items self::deleteTagsFromItem($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']]); @@ -1363,8 +1440,7 @@ class Item extends BaseObject $item['parent-uri'] = $item['thr-parent']; } - /** @var Activity $activity */ - $activity = self::getClass(Activity::class); + $activity = DI::activity(); if (isset($item['gravity'])) { $item['gravity'] = intval($item['gravity']); @@ -2435,7 +2511,7 @@ class Item extends BaseObject $guid = System::createUUID(); } - return self::getApp()->getBaseURL() . '/objects/' . $guid; + return DI::app()->getBaseURL() . '/objects/' . $guid; } /** @@ -2543,7 +2619,7 @@ class Item extends BaseObject "#$2", $item["body"]); foreach ($tags as $tag) { - if ((strpos($tag, '#') !== 0) || strpos($tag, '[url=') || $tag[1] == '#') { + if ((strpos($tag, '#') !== 0) || strpos($tag, '[url=') || strlen($tag) < 2 || $tag[1] == '#') { continue; } @@ -2901,8 +2977,7 @@ class Item extends BaseObject */ public static function enumeratePermissions(array $obj, bool $check_dead = false) { - /** @var ACLFormatter $aclFormater */ - $aclFormater = self::getClass(ACLFormatter::class); + $aclFormater = DI::aclFormatter(); $allow_people = $aclFormater->expand($obj['allow_cid']); $allow_groups = Group::expand($obj['uid'], $aclFormater->expand($obj['allow_gid']), $check_dead); @@ -3396,7 +3471,7 @@ class Item extends BaseObject */ private static function addRedirToImageTags(array &$item) { - $app = self::getApp(); + $app = DI::app(); $matches = []; $cnt = preg_match_all('|\[img\](http[^\[]*?/photo/[a-fA-F0-9]+?(-[0-9]\.[\w]+?)?)\[\/img\]|', $item['body'], $matches, PREG_SET_ORDER); @@ -3431,7 +3506,7 @@ class Item extends BaseObject */ public static function prepareBody(array &$item, $attach = false, $is_preview = false) { - $a = self::getApp(); + $a = DI::app(); Hook::callAll('prepare_body_init', $item); // In order to provide theme developers more possibilities, event items @@ -3569,7 +3644,7 @@ class Item extends BaseObject */ public static function getPlink($item) { - $a = self::getApp(); + $a = DI::app(); if ($a->user['nickname'] != "") { $ret = [ @@ -3580,7 +3655,7 @@ class Item extends BaseObject ]; if (!empty($item['plink'])) { - $ret["href"] = $a->removeBaseURL($item['plink']); + $ret["href"] = DI::baseUrl()->remove($item['plink']); $ret["title"] = L10n::t('link to source'); } @@ -3687,4 +3762,83 @@ class Item extends BaseObject return 0; } + + /** + * Return share data from an item array (if the item is shared item) + * We are providing the complete Item array, because at some time in the future + * we hopefully will define these values not in the body anymore but in some item fields. + * This function is meant to replace all similar functions in the system. + * + * @param array $item + * + * @return array with share information + */ + public static function getShareArray($item) + { + if (!preg_match("/(.*?)\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism", $item['body'], $matches)) { + return []; + } + + $attribute_string = $matches[2]; + $attributes = ['comment' => trim($matches[1]), 'shared' => trim($matches[3])]; + foreach (['author', 'profile', 'avatar', 'guid', 'posted', 'link'] as $field) { + if (preg_match("/$field=(['\"])(.+?)\\1/ism", $attribute_string, $matches)) { + $attributes[$field] = trim(html_entity_decode($matches[2] ?? '', ENT_QUOTES, 'UTF-8')); + } + } + return $attributes; + } + + /** + * Fetch item information for shared items from the original items and adds it. + * + * @param array $item + * + * @return array item array with data from the original item + */ + public static function addShareDataFromOriginal($item) + { + $shared = self::getShareArray($item); + if (empty($shared)) { + return $item; + } + + // Real reshares always have got a GUID. + if (empty($shared['guid'])) { + return $item; + } + + $uid = $item['uid'] ?? 0; + + // first try to fetch the item via the GUID. This will work for all reshares that had been created on this system + $shared_item = self::selectFirst(['title', 'body', 'attach'], ['guid' => $shared['guid'], 'uid' => [0, $uid]]); + if (!DBA::isResult($shared_item)) { + // Otherwhise try to find (and possibly fetch) the item via the link. This should work for Diaspora and ActivityPub posts + $id = self::fetchByLink($shared['link'], $uid); + if (empty($id)) { + Logger::info('Original item not found', ['url' => $shared['link'], 'callstack' => System::callstack()]); + return $item; + } + + $shared_item = self::selectFirst(['title', 'body', 'attach'], ['id' => $id]); + if (!DBA::isResult($shared_item)) { + return $item; + } + Logger::info('Got shared data from url', ['url' => $shared['link'], 'callstack' => System::callstack()]); + } else { + Logger::info('Got shared data from guid', ['guid' => $shared['guid'], 'callstack' => System::callstack()]); + } + + if (!empty($shared_item['title'])) { + $body = '[h3]' . $shared_item['title'] . "[/h3]\n" . $shared_item['body']; + unset($shared_item['title']); + } else { + $body = $shared_item['body']; + } + + $item['body'] = preg_replace("/\[share ([^\[\]]*)\].*\[\/share\]/ism", '[share $1]' . $body . '[/share]', $item['body']); + unset($shared_item['body']); + + return array_merge($item, $shared_item); + } }