diff --git a/database.sql b/database.sql index 027242b609..601ed8e959 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2021.03-dev (Red Hot Poker) --- DB_UPDATE_VERSION 1391 +-- DB_UPDATE_VERSION 1392 -- ------------------------------------------ @@ -1580,18 +1580,18 @@ CREATE VIEW `post-view` AS SELECT `item`.`author-id` AS `author-id`, `author`.`url` AS `author-link`, `author`.`addr` AS `author-addr`, - IF (`contact`.`url` = `author`.`url`, `contact`.`name`, `author`.`name`) AS `author-name`, + IF (`contact`.`url` = `author`.`url` AND `contact`.`name` != '', `contact`.`name`, `author`.`name`) AS `author-name`, `author`.`nick` AS `author-nick`, - IF (`contact`.`url` = `author`.`url`, `contact`.`thumb`, `author`.`thumb`) AS `author-avatar`, + IF (`contact`.`url` = `author`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `author`.`thumb`) AS `author-avatar`, `author`.`network` AS `author-network`, `author`.`blocked` AS `author-blocked`, `author`.`hidden` AS `author-hidden`, `item`.`owner-id` AS `owner-id`, `owner`.`url` AS `owner-link`, `owner`.`addr` AS `owner-addr`, - IF (`contact`.`url` = `owner`.`url`, `contact`.`name`, `owner`.`name`) AS `owner-name`, + IF (`contact`.`url` = `owner`.`url` AND `contact`.`name` != '', `contact`.`name`, `owner`.`name`) AS `owner-name`, `owner`.`nick` AS `owner-nick`, - IF (`contact`.`url` = `owner`.`url`, `contact`.`thumb`, `owner`.`thumb`) AS `owner-avatar`, + IF (`contact`.`url` = `owner`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `owner`.`thumb`) AS `owner-avatar`, `owner`.`network` AS `owner-network`, `owner`.`blocked` AS `owner-blocked`, `owner`.`hidden` AS `owner-hidden`, @@ -1671,7 +1671,7 @@ CREATE VIEW `post-thread-view` AS SELECT `item`.`gravity` AS `gravity`, `item`.`extid` AS `extid`, `thread`.`created` AS `created`, - `thread`.`edited` AS `edited`, + `item`.`edited` AS `edited`, `thread`.`commented` AS `commented`, `thread`.`received` AS `received`, `thread`.`changed` AS `changed`, @@ -1741,18 +1741,18 @@ CREATE VIEW `post-thread-view` AS SELECT `thread`.`author-id` AS `author-id`, `author`.`url` AS `author-link`, `author`.`addr` AS `author-addr`, - IF (`contact`.`url` = `author`.`url`, `contact`.`name`, `author`.`name`) AS `author-name`, + IF (`contact`.`url` = `author`.`url` AND `contact`.`name` != '', `contact`.`name`, `author`.`name`) AS `author-name`, `author`.`nick` AS `author-nick`, - IF (`contact`.`url` = `author`.`url`, `contact`.`thumb`, `author`.`thumb`) AS `author-avatar`, + IF (`contact`.`url` = `author`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `author`.`thumb`) AS `author-avatar`, `author`.`network` AS `author-network`, `author`.`blocked` AS `author-blocked`, `author`.`hidden` AS `author-hidden`, `thread`.`owner-id` AS `owner-id`, `owner`.`url` AS `owner-link`, `owner`.`addr` AS `owner-addr`, - IF (`contact`.`url` = `owner`.`url`, `contact`.`name`, `owner`.`name`) AS `owner-name`, + IF (`contact`.`url` = `owner`.`url` AND `contact`.`name` != '', `contact`.`name`, `owner`.`name`) AS `owner-name`, `owner`.`nick` AS `owner-nick`, - IF (`contact`.`url` = `owner`.`url`, `contact`.`thumb`, `owner`.`thumb`) AS `owner-avatar`, + IF (`contact`.`url` = `owner`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `owner`.`thumb`) AS `owner-avatar`, `owner`.`network` AS `owner-network`, `owner`.`blocked` AS `owner-blocked`, `owner`.`hidden` AS `owner-hidden`, @@ -1806,7 +1806,7 @@ CREATE VIEW `post-thread-view` AS SELECT LEFT JOIN `item-content` ON `item-content`.`uri-id` = `thread`.`uri-id` LEFT JOIN `post-delivery-data` ON `post-delivery-data`.`uri-id` = `thread`.`uri-id` AND `thread`.`origin` LEFT JOIN `permissionset` ON `permissionset`.`id` = `item`.`psid` - STRAIGHT_JOIN `item` AS `parent-item` ON `parent-item`.`uri-id` = `item`.`parent-uri-id` AND `parent-item`.`uid` = `item`.`uid` + STRAIGHT_JOIN `item` AS `parent-item` ON `parent-item`.`id` = `item`.`parent` STRAIGHT_JOIN `contact` AS `parent-item-author` ON `parent-item-author`.`id` = `parent-item`.`author-id`; -- diff --git a/include/api.php b/include/api.php index bafdb5f515..1913a73c8f 100644 --- a/include/api.php +++ b/include/api.php @@ -1733,18 +1733,18 @@ function api_statuses_public_timeline($type) $start = max(0, ($page - 1) * $count); if ($exclude_replies && !$conversation_id) { - $condition = ["`gravity` IN (?, ?) AND `iid` > ? AND `private` = ? AND `wall` AND NOT `author`.`hidden`", + $condition = ["`gravity` IN (?, ?) AND `iid` > ? AND `private` = ? AND `wall` AND NOT `author-hidden`", GRAVITY_PARENT, GRAVITY_COMMENT, $since_id, Item::PUBLIC]; if ($max_id > 0) { - $condition[0] .= " AND `thread`.`iid` <= ?"; + $condition[0] .= " AND `iid` <= ?"; $condition[] = $max_id; } $params = ['order' => ['iid' => true], 'limit' => [$start, $count]]; - $statuses = Item::selectThreadForUser(api_user(), Item::DISPLAY_FIELDLIST, $condition, $params); + $statuses = Post::selectThreadForUser(api_user(), Item::DISPLAY_FIELDLIST, $condition, $params); - $r = Item::toArray($statuses); + $r = Post::toArray($statuses); } else { $condition = ["`gravity` IN (?, ?) AND `id` > ? AND `private` = ? AND `wall` AND `origin` AND NOT `author-hidden`", GRAVITY_PARENT, GRAVITY_COMMENT, $since_id, Item::PUBLIC]; @@ -1812,18 +1812,18 @@ function api_statuses_networkpublic_timeline($type) $start = max(0, ($page - 1) * $count); - $condition = ["`uid` = 0 AND `gravity` IN (?, ?) AND `thread`.`iid` > ? AND `private` = ?", + $condition = ["`uid` = 0 AND `gravity` IN (?, ?) AND `iid` > ? AND `private` = ?", GRAVITY_PARENT, GRAVITY_COMMENT, $since_id, Item::PUBLIC]; if ($max_id > 0) { - $condition[0] .= " AND `thread`.`iid` <= ?"; + $condition[0] .= " AND `iid` <= ?"; $condition[] = $max_id; } $params = ['order' => ['iid' => true], 'limit' => [$start, $count]]; - $statuses = Item::selectThreadForUser(api_user(), Item::DISPLAY_FIELDLIST, $condition, $params); + $statuses = Post::selectThreadForUser(api_user(), Item::DISPLAY_FIELDLIST, $condition, $params); - $ret = api_format_items(Item::toArray($statuses), $user_info, false, $type); + $ret = api_format_items(Post::toArray($statuses), $user_info, false, $type); bindComments($ret); diff --git a/include/enotify.php b/include/enotify.php index c3943e5f6d..9e59f63259 100644 --- a/include/enotify.php +++ b/include/enotify.php @@ -149,7 +149,7 @@ function notification($params) } if ($params['type'] == Notify\Type::COMMENT || $params['type'] == Notify\Type::TAG_SELF) { - $thread = Item::selectFirstThreadForUser($params['uid'], ['ignored'], ['iid' => $parent_id, 'deleted' => false]); + $thread = Post::selectFirstThreadForUser($params['uid'], ['ignored'], ['iid' => $parent_id, 'deleted' => false]); if (DBA::isResult($thread) && $thread['ignored']) { Logger::log('Thread ' . $parent_id . ' will be ignored', Logger::DEBUG); return false; diff --git a/mod/notes.php b/mod/notes.php index 7c65a4c07a..b0709eb952 100644 --- a/mod/notes.php +++ b/mod/notes.php @@ -25,6 +25,7 @@ use Friendica\Content\Pager; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Item; +use Friendica\Model\Post; use Friendica\Module\BaseProfile; function notes_init(App $a) @@ -81,12 +82,12 @@ function notes_content(App $a, $update = false) $params = ['order' => ['created' => true], 'limit' => [$pager->getStart(), $pager->getItemsPerPage()]]; - $r = Item::selectThreadForUser(local_user(), ['uri'], $condition, $params); + $r = Post::selectThreadForUser(local_user(), ['uri'], $condition, $params); $count = 0; if (DBA::isResult($r)) { - $notes = Item::toArray($r); + $notes = Post::toArray($r); $count = count($notes); diff --git a/src/Model/Contact/Relation.php b/src/Model/Contact/Relation.php index 3c95973238..f0f24774ef 100644 --- a/src/Model/Contact/Relation.php +++ b/src/Model/Contact/Relation.php @@ -24,6 +24,7 @@ namespace Friendica\Model\Contact; use Exception; use Friendica\Core\Logger; use Friendica\Core\Protocol; +use Friendica\Database\Database; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\APContact; @@ -64,7 +65,7 @@ class Relation return; } - DBA::update('contact-relation', ['last-interaction' => $interaction_date], ['cid' => $target, 'relation-cid' => $actor], true); + DBA::insert('contact-relation', ['last-interaction' => $interaction_date, 'cid' => $target, 'relation-cid' => $actor], Database::INSERT_UPDATE); } /** @@ -136,14 +137,14 @@ class Relation $actor = Contact::getIdForURL($contact); if (!empty($actor)) { if (in_array($contact, $followers)) { - $fields = ['cid' => $target, 'relation-cid' => $actor]; - DBA::update('contact-relation', ['follows' => true, 'follow-updated' => DateTimeFormat::utcNow()], $fields, true); + $fields = ['cid' => $target, 'relation-cid' => $actor, 'follows' => true, 'follow-updated' => DateTimeFormat::utcNow()]; + DBA::insert('contact-relation', $fields, Database::INSERT_UPDATE); $follower_counter++; } if (in_array($contact, $followings)) { - $fields = ['cid' => $actor, 'relation-cid' => $target]; - DBA::update('contact-relation', ['follows' => true, 'follow-updated' => DateTimeFormat::utcNow()], $fields, true); + $fields = ['cid' => $actor, 'relation-cid' => $target, 'follows' => true, 'follow-updated' => DateTimeFormat::utcNow()]; + DBA::insert('contact-relation', $fields, Database::INSERT_UPDATE); $following_counter++; } } diff --git a/src/Model/Item.php b/src/Model/Item.php index eb4c713646..50e9313a11 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -43,7 +43,6 @@ use Friendica\Util\Map; use Friendica\Util\Network; use Friendica\Util\Strings; use Friendica\Worker\Delivery; -use Friendica\Repository\PermissionSet as RepPermissionSet; use LanguageDetection\Language; class Item @@ -134,8 +133,6 @@ class Item const TABLES = ['item', 'user-item', 'item-content', 'post-delivery-data', 'diaspora-interaction']; - private static $legacy_mode = null; - private static function getItemFields() { $definition = DBStructure::definition('', false); @@ -177,629 +174,6 @@ class Item return (bool)$useritem['pinned']; } - /** - * 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 = DBA::fetch($useritems)) { - $pinned[] = $useritem['iid']; - } - DBA::close($useritems); - - if (empty($pinned)) { - return []; - } - - $condition = DBA::mergeConditions(['iid' => $pinned], $condition); - - return self::selectThreadForUser($uid, $selected, $condition, $params); - } - - /** - * Fetch a single item row - * - * @param mixed $stmt statement object - * @return array|false current row or false - * @throws \Exception - */ - public static function fetch($stmt) - { - $row = DBA::fetch($stmt); - - if (!is_array($row)) { - return $row; - } - - $row = DBA::castFields('item', $row); - - // ---------------------- Transform item structure data ---------------------- - - // We prefer the data from the user's contact over the public one - if (!empty($row['author-link']) && !empty($row['contact-link']) && - ($row['author-link'] == $row['contact-link'])) { - if (isset($row['author-avatar']) && !empty($row['contact-avatar'])) { - $row['author-avatar'] = $row['contact-avatar']; - } - if (isset($row['author-name']) && !empty($row['contact-name'])) { - $row['author-name'] = $row['contact-name']; - } - } - - if (!empty($row['owner-link']) && !empty($row['contact-link']) && - ($row['owner-link'] == $row['contact-link'])) { - if (isset($row['owner-avatar']) && !empty($row['contact-avatar'])) { - $row['owner-avatar'] = $row['contact-avatar']; - } - if (isset($row['owner-name']) && !empty($row['contact-name'])) { - $row['owner-name'] = $row['contact-name']; - } - } - - // We can always comment on posts from these networks - if (array_key_exists('writable', $row) && - in_array($row['internal-network'], Protocol::FEDERATED)) { - $row['writable'] = 1; - } - - // ---------------------- Transform item content data ---------------------- - - if (array_key_exists('verb', $row)) { - if (!is_null($row['internal-verb'])) { - $row['verb'] = $row['internal-verb']; - } - - if (in_array($row['verb'], self::ACTIVITIES)) { - if (array_key_exists('title', $row)) { - $row['title'] = ''; - } - if (array_key_exists('body', $row)) { - $row['body'] = $row['verb']; - } - if (array_key_exists('object', $row)) { - $row['object'] = ''; - } - if (array_key_exists('object-type', $row)) { - $row['object-type'] = Activity\ObjectType::NOTE; - } - } elseif (in_array($row['verb'], ['', Activity::POST, Activity::SHARE])) { - // Posts don't have a target - but having tags or files. - if (array_key_exists('target', $row)) { - $row['target'] = ''; - } - } - } - - if (array_key_exists('vid', $row) && is_null($row['vid']) && !empty($row['verb'])) { - $row['vid'] = Verb::getID($row['verb']); - } - - 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'] = Post\Category::getTextByURIId($row['internal-uri-id'], $row['internal-uid']); - } - } - - if ($row['internal-psid'] == RepPermissionSet::PUBLIC) { - if (array_key_exists('allow_cid', $row)) { - $row['allow_cid'] = ''; - } - if (array_key_exists('allow_gid', $row)) { - $row['allow_gid'] = ''; - } - if (array_key_exists('deny_cid', $row)) { - $row['deny_cid'] = ''; - } - if (array_key_exists('deny_gid', $row)) { - $row['deny_gid'] = ''; - } - } - - if (array_key_exists('ignored', $row) && array_key_exists('internal-user-ignored', $row) && !is_null($row['internal-user-ignored'])) { - $row['ignored'] = $row['internal-user-ignored']; - } - - // Remove internal fields - unset($row['internal-network']); - unset($row['internal-uri-id']); - unset($row['internal-uid']); - unset($row['internal-psid']); - unset($row['internal-verb']); - unset($row['internal-user-ignored']); - unset($row['interaction']); - - return $row; - } - - /** - * Fills an array with data from an item query - * - * @param object $stmt statement object - * @param bool $do_close - * @return array Data array - */ - public static function toArray($stmt, $do_close = true) { - if (is_bool($stmt)) { - return $stmt; - } - - $data = []; - while ($row = self::fetch($stmt)) { - $data[] = $row; - } - if ($do_close) { - DBA::close($stmt); - } - return $data; - } - - /** - * Check if item data exists - * - * @param array $condition array of fields for condition - * - * @return boolean Are there rows for that condition? - * @throws \Exception - */ - public static function exists($condition) { - $stmt = self::select(['id'], $condition, ['limit' => 1]); - - if (is_bool($stmt)) { - $retval = $stmt; - } else { - $retval = (DBA::numRows($stmt) > 0); - } - - DBA::close($stmt); - - return $retval; - } - - /** - * Select rows from the item table and returns them as an array - * - * @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 array - * @throws \Exception - */ - public static function selectToArray(array $fields = [], array $condition = [], $params = []) - { - $result = self::select($fields, $condition, $params); - - if (is_bool($result)) { - return []; - } - - $data = []; - while ($row = self::fetch($result)) { - $data[] = $row; - } - DBA::close($result); - - return $data; - } - - /** - * 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 - * @throws \Exception - */ - 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($usermode); - - $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, $usermode); - - $sql = "SELECT " . $select_fields . " FROM " . $table . $condition_string . $param_string; - - return DBA::p($sql, $condition); - } - - /** - * Select rows from the starting post in the item table - * - * @param integer $uid User ID - * @param array $selected - * @param array $condition Array of fields for condition - * @param array $params Array of several parameters - * - * @return boolean|object - * @throws \Exception - */ - public static function selectThreadForUser($uid, array $selected = [], array $condition = [], $params = []) - { - $params['uid'] = $uid; - - if (empty($selected)) { - $selected = self::DISPLAY_FIELDLIST; - } - - return self::selectThread($selected, $condition, $params); - } - - /** - * Retrieve a single record from the starting post in the item table and returns it in an associative array - * - * @param integer $uid User ID - * @param array $selected - * @param array $condition - * @param array $params - * @return bool|array - * @throws \Exception - * @see DBA::select - */ - public static function selectFirstThreadForUser($uid, array $selected = [], array $condition = [], $params = []) - { - $params['uid'] = $uid; - - if (empty($selected)) { - $selected = self::DISPLAY_FIELDLIST; - } - - return self::selectFirstThread($selected, $condition, $params); - } - - /** - * Retrieve a single record from the starting post in the item table and returns it in an associative array - * - * @param array $fields - * @param array $condition - * @param array $params - * @return bool|array - * @throws \Exception - * @see DBA::select - */ - public static function selectFirstThread(array $fields = [], array $condition = [], $params = []) - { - $params['limit'] = 1; - $result = self::selectThread($fields, $condition, $params); - - if (is_bool($result)) { - return $result; - } else { - $row = self::fetch($result); - DBA::close($result); - return $row; - } - } - - /** - * Select rows from the starting post in 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 - * @throws \Exception - */ - public static function selectThread(array $selected = [], array $condition = [], $params = []) - { - $uid = 0; - $usermode = false; - - if (isset($params['uid'])) { - $uid = $params['uid']; - $usermode = true; - } - - $fields = self::fieldlist($usermode); - - $fields['thread'] = ['mention', 'ignored', 'iid']; - - $threadfields = ['thread' => ['iid', 'uid', 'contact-id', 'owner-id', 'author-id', - 'created', 'edited', 'commented', 'received', 'changed', 'wall', 'private', - 'pubmail', 'moderated', 'visible', 'starred', 'ignored', 'post-type', - '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); - - if ($usermode) { - $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, $usermode); - - $sql = "SELECT " . $select_fields . " FROM " . $table . $condition_string . $param_string; - - return DBA::p($sql, $condition); - } - - /** - * Returns a list of fields that are associated with the item table - * - * @param $usermode - * @return array field list - */ - private static function fieldlist($usermode) - { - $fields = []; - - $fields['item'] = ['id', 'uid', 'parent', 'uri', 'parent-uri', 'thr-parent', - 'guid', 'uri-id', 'parent-uri-id', 'thr-parent-id', 'vid', 'causer-id', - 'contact-id', 'owner-id', 'author-id', 'type', 'wall', 'gravity', 'extid', - 'created', 'edited', 'commented', 'received', 'changed', 'psid', - 'resource-id', 'event-id', 'post-type', 'file', - 'private', 'pubmail', 'moderated', 'visible', 'starred', 'bookmark', - 'unseen', 'deleted', 'origin', 'forum_mode', 'mention', 'global', - 'id' => 'item_id', 'network', 'icid', 'event-id', - 'uri-id' => 'internal-uri-id', 'uid' => 'internal-uid', - 'network' => 'internal-network', 'psid' => 'internal-psid']; - - if ($usermode) { - $fields['user-item'] = ['pinned', 'notification-type', 'ignored' => 'internal-user-ignored']; - } - - $fields['item-content'] = array_merge(self::CONTENT_FIELDLIST, self::MIXED_CONTENT_FIELDLIST); - - $fields['post-delivery-data'] = array_merge(Post\DeliveryData::LEGACY_FIELD_LIST, Post\DeliveryData::FIELD_LIST); - - $fields['verb'] = ['name' => 'internal-verb']; - - $fields['permissionset'] = ['allow_cid', 'allow_gid', 'deny_cid', 'deny_gid']; - - $fields['author'] = ['url' => 'author-link', 'name' => 'author-name', 'addr' => 'author-addr', - 'thumb' => 'author-avatar', 'nick' => 'author-nick', 'network' => 'author-network']; - - $fields['owner'] = ['url' => 'owner-link', 'name' => 'owner-name', 'addr' => 'owner-addr', - 'thumb' => 'owner-avatar', 'nick' => 'owner-nick', 'network' => 'owner-network']; - - $fields['causer'] = ['url' => 'causer-link', 'name' => 'causer-name', 'addr' => 'causer-addr', - 'thumb' => 'causer-avatar', 'nick' => 'causer-nick', 'network' => 'causer-network', - 'contact-type' => 'causer-contact-type']; - - $fields['contact'] = ['url' => 'contact-link', 'name' => 'contact-name', 'thumb' => 'contact-avatar', - 'writable', 'self', 'id' => 'cid', 'alias', 'uid' => 'contact-uid', - 'photo', 'name-date', 'uri-date', 'avatar-date', 'thumb', 'dfrn-id']; - - $fields['parent-item'] = ['guid' => 'parent-guid', 'network' => 'parent-network', 'author-id' => 'parent-author-id']; - - $fields['parent-item-author'] = ['url' => 'parent-author-link', 'name' => 'parent-author-name', - 'network' => 'parent-author-network']; - - $fields['event'] = ['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']; - - $fields['diaspora-interaction'] = ['interaction', 'interaction' => 'signed_text']; - - return $fields; - } - - /** - * 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 sprintf("$master_table.`visible` AND NOT $master_table.`deleted` AND NOT $master_table.`moderated` - AND (`user-item`.`hidden` IS NULL OR NOT `user-item`.`hidden`) - AND (`user-author`.`blocked` IS NULL OR NOT `user-author`.`blocked`) - AND (`user-author`.`ignored` IS NULL OR NOT `user-author`.`ignored` OR `item`.`gravity` != %d) - AND (`user-owner`.`blocked` IS NULL OR NOT `user-owner`.`blocked`) - AND (`user-owner`.`ignored` IS NULL OR NOT `user-owner`.`ignored` OR `item`.`gravity` != %d) ", - GRAVITY_PARENT, GRAVITY_PARENT); - } - - /** - * 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) - * - * @param $user_mode - * @return string The SQL joins for the "select" functions - */ - private static function constructJoins($uid, $sql_commands, $thread_mode, $user_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 = ''; - } - - if ($user_mode) { - $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`.`gravity` != %d 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 - LEFT JOIN `user-contact` AS `user-author` ON `user-author`.`cid` = $master_table.`author-id` AND `user-author`.`uid` = %d - LEFT JOIN `user-contact` AS `user-owner` ON `user-owner`.`cid` = $master_table.`owner-id` AND `user-owner`.`uid` = %d", - Contact::SHARING, Contact::FRIEND, GRAVITY_PARENT, intval($uid), intval($uid), intval($uid)); - } else { - if (strpos($sql_commands, "`contact`.") !== false) { - $joins .= "LEFT JOIN `contact` ON `contact`.`id` = $master_table.`contact-id`"; - } - if (strpos($sql_commands, "`author`.") !== false) { - $joins .= " LEFT JOIN `contact` AS `author` ON `author`.`id` = $master_table.`author-id`"; - } - if (strpos($sql_commands, "`owner`.") !== false) { - $joins .= " LEFT JOIN `contact` AS `owner` ON `owner`.`id` = $master_table.`owner-id`"; - } - } - if (strpos($sql_commands, "`causer`.") !== false) { - $joins .= " LEFT JOIN `contact` AS `causer` ON `causer`.`id` = `item`.`causer-id`"; - } - - 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, "`diaspora-interaction`.") !== false) { - $joins .= " LEFT JOIN `diaspora-interaction` ON `diaspora-interaction`.`uri-id` = `item`.`uri-id`"; - } - - if (strpos($sql_commands, "`item-content`.") !== false) { - $joins .= " LEFT JOIN `item-content` ON `item-content`.`uri-id` = `item`.`uri-id`"; - } - - if (strpos($sql_commands, "`post-delivery-data`.") !== false) { - $joins .= " LEFT JOIN `post-delivery-data` ON `post-delivery-data`.`uri-id` = `item`.`uri-id` AND `item`.`origin`"; - } - - if (strpos($sql_commands, "`verb`.") !== false) { - $joins .= " LEFT JOIN `verb` ON `verb`.`id` = `item`.`vid`"; - } - - if (strpos($sql_commands, "`permissionset`.") !== false) { - $joins .= " LEFT JOIN `permissionset` ON `permissionset`.`id` = `item`.`psid`"; - } - - if ((strpos($sql_commands, "`parent-item`.") !== false) || (strpos($sql_commands, "`parent-item-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; - } - - /** - * 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(array $fields, array $selected) - { - if (!empty($selected)) { - $selected = array_merge($selected, ['internal-uri-id', 'internal-uid', 'internal-psid', 'internal-network']); - } - - if (in_array('verb', $selected)) { - $selected = array_merge($selected, ['internal-verb']); - } - - if (in_array('ignored', $selected)) { - $selected[] = 'internal-user-ignored'; - } - - $legacy_fields = array_merge(Post\DeliveryData::LEGACY_FIELD_LIST, self::MIXED_CONTENT_FIELDLIST); - - $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); - } - - /** - * 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; - } - /** * Update existing item entries * @@ -1178,21 +552,6 @@ class Item return $item['author-id']; } - // This function will finally cover most of the preparation functionality in mod/item.php - public static function prepare(&$item) - { - /* - * @TODO: Unused code triggering inspection errors - * - $data = BBCode::getAttachmentData($item['body']); - if ((preg_match_all("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", $item['body'], $match, PREG_SET_ORDER) || isset($data["type"])) - && ($posttype != self::PT_PERSONAL_NOTE)) { - $posttype = self::PT_PAGE; - $objecttype = ACTIVITY_OBJ_BOOKMARK; - } - */ - } - /** * Write an item array into a spool file to be inserted later. * This command is called whenever there are issues storing an item. diff --git a/src/Model/Post.php b/src/Model/Post.php index 9efdbfe14f..5107e71ab4 100644 --- a/src/Model/Post.php +++ b/src/Model/Post.php @@ -360,4 +360,63 @@ class Post return $row; } } + + /** + * Retrieve a single record from the starting post in the item table and returns it in an associative array + * + * @param integer $uid User ID + * @param array $selected + * @param array $condition + * @param array $params + * @return bool|array + * @throws \Exception + * @see DBA::select + */ + public static function selectFirstThreadForUser($uid, array $selected = [], array $condition = [], $params = []) + { + $params['limit'] = 1; + + $result = self::selectThreadForUser($uid, $selected, $condition, $params); + + if (is_bool($result)) { + return $result; + } else { + $row = self::fetch($result); + DBA::close($result); + return $row; + } + } + + /** + * 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 = DBA::fetch($useritems)) { + $pinned[] = $useritem['iid']; + } + DBA::close($useritems); + + if (empty($pinned)) { + return []; + } + + $condition = DBA::mergeConditions(['iid' => $pinned], $condition); + + return self::selectThreadForUser($uid, $selected, $condition, $params); + } } diff --git a/src/Model/UserItem.php b/src/Model/UserItem.php index 0cc3f61897..b4346c2215 100644 --- a/src/Model/UserItem.php +++ b/src/Model/UserItem.php @@ -92,7 +92,7 @@ class UserItem */ private static function setNotificationForUser(array $item, int $uid) { - $thread = Item::selectFirstThreadForUser($uid, ['ignored'], ['iid' => $item['parent'], 'deleted' => false]); + $thread = Post::selectFirstThreadForUser($uid, ['ignored'], ['iid' => $item['parent'], 'deleted' => false]); if (!empty($thread['ignored'])) { return; } diff --git a/src/Module/Conversation/Community.php b/src/Module/Conversation/Community.php index cbcb508707..c9468d4520 100644 --- a/src/Module/Conversation/Community.php +++ b/src/Module/Conversation/Community.php @@ -356,9 +356,9 @@ class Community extends BaseModule } } - $r = Item::selectThreadForUser(0, ['uri', 'commented', 'author-link'], $condition, $params); + $r = Post::selectThreadForUser(0, ['uri', 'commented', 'author-link'], $condition, $params); - $items = DBA::toArray($r); + $items = Post::toArray($r); // Previous page case: once we get the relevant items closest to min_id, we need to restore the expected display order if (empty($item_id) && isset($min_id) && !isset($max_id)) { diff --git a/src/Module/Profile/Status.php b/src/Module/Profile/Status.php index 6ee60e8f51..786c3594a4 100644 --- a/src/Module/Profile/Status.php +++ b/src/Module/Profile/Status.php @@ -228,8 +228,8 @@ class Status extends BaseProfile $condition = []; } - $pinned_items = Item::selectPinned($a->profile['uid'], ['uri', 'pinned'], $condition); - $pinned = Item::toArray($pinned_items); + $pinned_items = Post::selectPinned($a->profile['uid'], ['uri', 'pinned'], $condition); + $pinned = Post::toArray($pinned_items); $items = array_merge($items, $pinned); } diff --git a/src/Module/Settings/UserExport.php b/src/Module/Settings/UserExport.php index a07116785f..42ee4eb1f4 100644 --- a/src/Module/Settings/UserExport.php +++ b/src/Module/Settings/UserExport.php @@ -28,6 +28,7 @@ use Friendica\Database\DBA; use Friendica\Database\DBStructure; use Friendica\DI; use Friendica\Model\Item; +use Friendica\Model\Post; use Friendica\Module\BaseSettings; /** @@ -245,7 +246,7 @@ class UserExport extends BaseSettings // chunk the output to avoid exhausting memory for ($x = 0; $x < $total; $x += 500) { - $items = Item::selectToArray(Item::ITEM_FIELDLIST, ['uid' => local_user()], ['limit' => [$x, 500]]); + $items = Post::selectToArray(Item::ITEM_FIELDLIST, ['uid' => local_user()], ['limit' => [$x, 500]]); $output = ['item' => $items]; echo json_encode($output, JSON_PARTIAL_OUTPUT_ON_ERROR). "\n"; } diff --git a/src/Object/Post.php b/src/Object/Post.php index 438d494f1d..00293d8ecb 100644 --- a/src/Object/Post.php +++ b/src/Object/Post.php @@ -301,7 +301,7 @@ class Post if ($this->isToplevel()) { if(local_user()) { - $thread = Item::selectFirstThreadForUser(local_user(), ['ignored'], ['iid' => $item['id']]); + $thread = PostModel::selectFirstThreadForUser(local_user(), ['ignored'], ['iid' => $item['id']]); if (DBA::isResult($thread)) { $ignore = [ 'do' => DI::l10n()->t("ignore thread"), diff --git a/src/Protocol/ActivityPub/Processor.php b/src/Protocol/ActivityPub/Processor.php index 9e8ea2976e..f281c7ee64 100644 --- a/src/Protocol/ActivityPub/Processor.php +++ b/src/Protocol/ActivityPub/Processor.php @@ -378,7 +378,7 @@ class Processor } foreach ($activity['receiver'] as $receiver) { - $item = Post::selectFirst(['id', 'uri-id', 'tag', 'origin', 'author-link'], ['uri' => $activity['target_id'], 'uid' => $receiver]); + $item = Post::selectFirst(['id', 'uri-id', 'origin', 'author-link'], ['uri' => $activity['target_id'], 'uid' => $receiver]); if (!DBA::isResult($item)) { // We don't fetch missing content for this purpose continue; diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index 4934e983b4..23a287a9a1 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -2060,7 +2060,7 @@ class DFRN $xt = XML::parseString($item["target"]); if ($xt->type == Activity\ObjectType::NOTE) { - $item_tag = Post::selectFirst(['id', 'uri-id', 'tag'], ['uri' => $xt->id, 'uid' => $importer["importer_uid"]]); + $item_tag = Post::selectFirst(['id', 'uri-id'], ['uri' => $xt->id, 'uid' => $importer["importer_uid"]]); if (!DBA::isResult($item_tag)) { Logger::log("Query failed to execute, no result returned in " . __FUNCTION__); diff --git a/src/Protocol/Feed.php b/src/Protocol/Feed.php index ba01e719ad..027efb2d47 100644 --- a/src/Protocol/Feed.php +++ b/src/Protocol/Feed.php @@ -887,12 +887,12 @@ class Feed $params = ['order' => ['received' => true], 'limit' => $max_items]; if ($filter === 'posts') { - $ret = Item::selectThread([], $condition, $params); + $ret = Post::selectThread([], $condition, $params); } else { - $ret = Item::select([], $condition, $params); + $ret = Post::select([], $condition, $params); } - $items = Item::toArray($ret); + $items = Post::toArray($ret); $doc = new DOMDocument('1.0', 'utf-8'); $doc->formatOutput = true; diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index 72008a1989..a8f4e24298 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -2124,12 +2124,12 @@ class OStatus $params = ['order' => ['received' => true], 'limit' => $max_items]; if ($filter === 'posts') { - $ret = Item::selectThread([], $condition, $params); + $ret = Post::selectThread([], $condition, $params); } else { - $ret = Item::select([], $condition, $params); + $ret = Post::select([], $condition, $params); } - $items = Item::toArray($ret); + $items = Post::toArray($ret); $doc = new DOMDocument('1.0', 'utf-8'); $doc->formatOutput = true; diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 18099826ec..e4b7e6e91f 100644 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -55,7 +55,7 @@ use Friendica\Database\DBA; if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1391); + define('DB_UPDATE_VERSION', 1392); } return [ diff --git a/static/dbview.config.php b/static/dbview.config.php index 11ebc2b46b..45780d267c 100644 --- a/static/dbview.config.php +++ b/static/dbview.config.php @@ -126,18 +126,18 @@ "author-id" => ["item", "author-id"], "author-link" => ["author", "url"], "author-addr" => ["author", "addr"], - "author-name" => "IF (`contact`.`url` = `author`.`url`, `contact`.`name`, `author`.`name`)", + "author-name" => "IF (`contact`.`url` = `author`.`url` AND `contact`.`name` != '', `contact`.`name`, `author`.`name`)", "author-nick" => ["author", "nick"], - "author-avatar" => "IF (`contact`.`url` = `author`.`url`, `contact`.`thumb`, `author`.`thumb`)", + "author-avatar" => "IF (`contact`.`url` = `author`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `author`.`thumb`)", "author-network" => ["author", "network"], "author-blocked" => ["author", "blocked"], "author-hidden" => ["author", "hidden"], "owner-id" => ["item", "owner-id"], "owner-link" => ["owner", "url"], "owner-addr" => ["owner", "addr"], - "owner-name" => "IF (`contact`.`url` = `owner`.`url`, `contact`.`name`, `owner`.`name`)", + "owner-name" => "IF (`contact`.`url` = `owner`.`url` AND `contact`.`name` != '', `contact`.`name`, `owner`.`name`)", "owner-nick" => ["owner", "nick"], - "owner-avatar" => "IF (`contact`.`url` = `owner`.`url`, `contact`.`thumb`, `owner`.`thumb`)", + "owner-avatar" => "IF (`contact`.`url` = `owner`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `owner`.`thumb`)", "owner-network" => ["owner", "network"], "owner-blocked" => ["owner", "blocked"], "owner-hidden" => ["owner", "hidden"], @@ -219,7 +219,6 @@ "commented" => ["thread", "commented"], "received" => ["thread", "received"], "changed" => ["thread", "changed"], - "edited" => ["thread", "edited"], "resource-id" => ["item", "resource-id"], "post-type" => ["thread", "post-type"], "private" => ["thread", "private"], @@ -286,18 +285,18 @@ "author-id" => ["thread", "author-id"], "author-link" => ["author", "url"], "author-addr" => ["author", "addr"], - "author-name" => "IF (`contact`.`url` = `author`.`url`, `contact`.`name`, `author`.`name`)", + "author-name" => "IF (`contact`.`url` = `author`.`url` AND `contact`.`name` != '', `contact`.`name`, `author`.`name`)", "author-nick" => ["author", "nick"], - "author-avatar" => "IF (`contact`.`url` = `author`.`url`, `contact`.`thumb`, `author`.`thumb`)", + "author-avatar" => "IF (`contact`.`url` = `author`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `author`.`thumb`)", "author-network" => ["author", "network"], "author-blocked" => ["author", "blocked"], "author-hidden" => ["author", "hidden"], "owner-id" => ["thread", "owner-id"], "owner-link" => ["owner", "url"], "owner-addr" => ["owner", "addr"], - "owner-name" => "IF (`contact`.`url` = `owner`.`url`, `contact`.`name`, `owner`.`name`)", + "owner-name" => "IF (`contact`.`url` = `owner`.`url` AND `contact`.`name` != '', `contact`.`name`, `owner`.`name`)", "owner-nick" => ["owner", "nick"], - "owner-avatar" => "IF (`contact`.`url` = `owner`.`url`, `contact`.`thumb`, `owner`.`thumb`)", + "owner-avatar" => "IF (`contact`.`url` = `owner`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `owner`.`thumb`)", "owner-network" => ["owner", "network"], "owner-blocked" => ["owner", "blocked"], "owner-hidden" => ["owner", "hidden"], @@ -352,7 +351,7 @@ LEFT JOIN `item-content` ON `item-content`.`uri-id` = `thread`.`uri-id` LEFT JOIN `post-delivery-data` ON `post-delivery-data`.`uri-id` = `thread`.`uri-id` AND `thread`.`origin` LEFT JOIN `permissionset` ON `permissionset`.`id` = `item`.`psid` - STRAIGHT_JOIN `item` AS `parent-item` ON `parent-item`.`uri-id` = `item`.`parent-uri-id` AND `parent-item`.`uid` = `item`.`uid` + STRAIGHT_JOIN `item` AS `parent-item` ON `parent-item`.`id` = `item`.`parent` STRAIGHT_JOIN `contact` AS `parent-item-author` ON `parent-item-author`.`id` = `parent-item`.`author-id`" ], "category-view" => [