diff --git a/database.sql b/database.sql index 9afb7cb0f..60dca5f5f 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2021.03-dev (Red Hot Poker) --- DB_UPDATE_VERSION 1396 +-- DB_UPDATE_VERSION 1397 -- ------------------------------------------ @@ -684,7 +684,6 @@ CREATE TABLE IF NOT EXISTS `item` ( `guid` varchar(255) NOT NULL DEFAULT '' COMMENT 'A unique identifier for this item', `uri` varchar(255) NOT NULL DEFAULT '' COMMENT '', `uri-id` int unsigned COMMENT 'Id of the item-uri table entry that contains the item uri', - `uri-hash` varchar(80) NOT NULL DEFAULT '' COMMENT 'RIPEMD-128 hash from uri', `parent` int unsigned COMMENT 'item.id of the parent to this item if it is a reply of some form; otherwise this must be set to the id of this item', `parent-uri` varchar(255) NOT NULL DEFAULT '' COMMENT 'uri of the top-level parent to this item', `parent-uri-id` int unsigned COMMENT 'Id of the item-uri table that contains the top-level parent uri', @@ -710,16 +709,19 @@ CREATE TABLE IF NOT EXISTS `item` ( `deleted` boolean NOT NULL DEFAULT '0' COMMENT 'item has been deleted', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner id which owns this copy of the item', `contact-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'contact.id', - `wall` boolean NOT NULL DEFAULT '0' COMMENT 'This item was posted to the wall of uid', - `origin` boolean NOT NULL DEFAULT '0' COMMENT 'item originated at this site', - `pubmail` boolean NOT NULL DEFAULT '0' COMMENT '', - `starred` boolean NOT NULL DEFAULT '0' COMMENT 'item has been favourited', `unseen` boolean NOT NULL DEFAULT '1' COMMENT 'item has not been seen', - `mention` boolean NOT NULL DEFAULT '0' COMMENT 'The owner of this item was mentioned in it', - `forum_mode` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', + `origin` boolean NOT NULL DEFAULT '0' COMMENT 'item originated at this site', `psid` int unsigned COMMENT 'ID of the permission set of this post', - `resource-id` varchar(32) NOT NULL DEFAULT '' COMMENT 'Used to link other tables to items, it identifies the linked resource (e.g. photo) and if set must also set resource_type', + `starred` boolean NOT NULL DEFAULT '0' COMMENT 'item has been favourited', + `wall` boolean NOT NULL DEFAULT '0' COMMENT 'This item was posted to the wall of uid', + `pubmail` boolean NOT NULL DEFAULT '0' COMMENT '', + `forum_mode` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', `event-id` int unsigned COMMENT 'Used to link to the event.id', + `type` varchar(20) COMMENT '', + `bookmark` boolean COMMENT '', + `mention` boolean NOT NULL DEFAULT '0' COMMENT 'The owner of this item was mentioned in it', + `resource-id` varchar(32) COMMENT 'Deprecated', + `uri-hash` varchar(80) COMMENT 'Deprecated', `iaid` int unsigned COMMENT 'Deprecated', `icid` int unsigned COMMENT 'Deprecated', `attach` mediumtext COMMENT 'Deprecated', @@ -729,8 +731,6 @@ CREATE TABLE IF NOT EXISTS `item` ( `deny_gid` mediumtext COMMENT 'Deprecated', `postopts` text COMMENT 'Deprecated', `inform` mediumtext COMMENT 'Deprecated', - `type` varchar(20) COMMENT 'Deprecated', - `bookmark` boolean COMMENT 'Deprecated', `file` mediumtext COMMENT 'Deprecated', `location` varchar(255) COMMENT 'Deprecated', `coord` varchar(255) COMMENT 'Deprecated', @@ -970,22 +970,6 @@ CREATE TABLE IF NOT EXISTS `parsed_url` ( INDEX `created` (`created`) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='cache for \'parse_url\' queries'; --- --- TABLE participation --- -CREATE TABLE IF NOT EXISTS `participation` ( - `iid` int unsigned NOT NULL COMMENT '', - `server` varchar(60) NOT NULL COMMENT '', - `cid` int unsigned NOT NULL COMMENT '', - `fid` int unsigned NOT NULL COMMENT '', - PRIMARY KEY(`iid`,`server`), - INDEX `cid` (`cid`), - INDEX `fid` (`fid`), - FOREIGN KEY (`iid`) REFERENCES `item` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE, - FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE, - FOREIGN KEY (`fid`) REFERENCES `fcontact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE -) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Storage for participation messages from Diaspora'; - -- -- TABLE pconfig -- @@ -1141,6 +1125,26 @@ CREATE TABLE IF NOT EXISTS `post-tag` ( FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='post relation to tags'; +-- +-- TABLE post-thread-user +-- +CREATE TABLE IF NOT EXISTS `post-thread-user` ( + `uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri', + `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner id which owns this copy of the item', + `pinned` boolean NOT NULL DEFAULT '0' COMMENT 'The thread is pinned on the profile page', + `starred` boolean NOT NULL DEFAULT '0' COMMENT '', + `ignored` boolean NOT NULL DEFAULT '0' COMMENT 'Ignore updates for this thread', + `wall` boolean NOT NULL DEFAULT '0' COMMENT 'This item was posted to the wall of uid', + `pubmail` boolean NOT NULL DEFAULT '0' COMMENT '', + `forum_mode` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', + PRIMARY KEY(`uid`,`uri-id`), + INDEX `uid_wall` (`uid`,`wall`), + INDEX `uid_pinned` (`uid`,`pinned`), + INDEX `uri-id` (`uri-id`), + FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE, + FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Thread related data per user'; + -- -- TABLE post-user -- @@ -1166,6 +1170,19 @@ CREATE TABLE IF NOT EXISTS `post-user` ( FOREIGN KEY (`psid`) REFERENCES `permissionset` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='User specific post data'; +-- +-- TABLE post-user-notification +-- +CREATE TABLE IF NOT EXISTS `post-user-notification` ( + `uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri', + `uid` mediumint unsigned NOT NULL COMMENT 'Owner id which owns this copy of the item', + `notification-type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', + PRIMARY KEY(`uid`,`uri-id`), + INDEX `uri-id` (`uri-id`), + FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE, + FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='User post notifications'; + -- -- TABLE process -- @@ -1429,23 +1446,6 @@ CREATE TABLE IF NOT EXISTS `user-contact` ( FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='User specific public contact data'; --- --- TABLE user-item --- -CREATE TABLE IF NOT EXISTS `user-item` ( - `iid` int unsigned NOT NULL DEFAULT 0 COMMENT 'Item id', - `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', - `hidden` boolean NOT NULL DEFAULT '0' COMMENT 'Marker to hide an item from the user', - `ignored` boolean COMMENT 'Ignore this thread if set', - `pinned` boolean COMMENT 'The item is pinned on the profile page', - `notification-type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', - PRIMARY KEY(`uid`,`iid`), - INDEX `uid_pinned` (`uid`,`pinned`), - INDEX `iid_uid` (`iid`,`uid`), - FOREIGN KEY (`iid`) REFERENCES `item` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE, - FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE -) DEFAULT COLLATE utf8mb4_general_ci COMMENT='User specific item data'; - -- -- TABLE worker-ipc -- @@ -1848,13 +1848,13 @@ CREATE VIEW `network-item-view` AS SELECT FROM `item` INNER JOIN `thread` ON `thread`.`iid` = `item`.`parent` STRAIGHT_JOIN `contact` ON `contact`.`id` = `thread`.`contact-id` - LEFT JOIN `user-item` ON `user-item`.`iid` = `item`.`id` AND `user-item`.`uid` = `thread`.`uid` + LEFT JOIN `post-user` ON `post-user`.`uri-id` = `item`.`uri-id` AND `post-user`.`uid` = `thread`.`uid` LEFT JOIN `user-contact` AS `author` ON `author`.`uid` = `thread`.`uid` AND `author`.`cid` = `thread`.`author-id` LEFT JOIN `user-contact` AS `owner` ON `owner`.`uid` = `thread`.`uid` AND `owner`.`cid` = `thread`.`owner-id` LEFT JOIN `contact` AS `ownercontact` ON `ownercontact`.`id` = `thread`.`owner-id` WHERE `thread`.`visible` AND NOT `thread`.`deleted` AND NOT `thread`.`moderated` AND (NOT `contact`.`readonly` AND NOT `contact`.`blocked` AND NOT `contact`.`pending`) - AND (`user-item`.`hidden` IS NULL OR NOT `user-item`.`hidden`) + AND (`post-user`.`hidden` IS NULL OR NOT `post-user`.`hidden`) AND (`author`.`blocked` IS NULL OR NOT `author`.`blocked`) AND (`owner`.`blocked` IS NULL OR NOT `owner`.`blocked`); @@ -1879,13 +1879,13 @@ CREATE VIEW `network-thread-view` AS SELECT FROM `thread` STRAIGHT_JOIN `contact` ON `contact`.`id` = `thread`.`contact-id` STRAIGHT_JOIN `item` ON `item`.`id` = `thread`.`iid` - LEFT JOIN `user-item` ON `user-item`.`iid` = `item`.`id` AND `user-item`.`uid` = `thread`.`uid` + LEFT JOIN `post-user` ON `post-user`.`uri-id` = `item`.`uri-id` AND `post-user`.`uid` = `thread`.`uid` LEFT JOIN `user-contact` AS `author` ON `author`.`uid` = `thread`.`uid` AND `author`.`cid` = `thread`.`author-id` LEFT JOIN `user-contact` AS `owner` ON `owner`.`uid` = `thread`.`uid` AND `owner`.`cid` = `thread`.`owner-id` LEFT JOIN `contact` AS `ownercontact` ON `ownercontact`.`id` = `thread`.`owner-id` WHERE `thread`.`visible` AND NOT `thread`.`deleted` AND NOT `thread`.`moderated` AND (NOT `contact`.`readonly` AND NOT `contact`.`blocked` AND NOT `contact`.`pending`) - AND (`user-item`.`hidden` IS NULL OR NOT `user-item`.`hidden`) + AND (`post-user`.`hidden` IS NULL OR NOT `post-user`.`hidden`) AND (`author`.`blocked` IS NULL OR NOT `author`.`blocked`) AND (`owner`.`blocked` IS NULL OR NOT `owner`.`blocked`); diff --git a/doc/database.md b/doc/database.md index e135b19ae..447607f8d 100644 --- a/doc/database.md +++ b/doc/database.md @@ -32,7 +32,6 @@ Database Tables | [notify-threads](help/database/db_notify-threads) | | | [oembed](help/database/db_oembed) | cache for OEmbed queries | | [parsed_url](help/database/db_parsed_url) | cache for "parse_url" queries | -| [participation](help/database/db_participation) | Storage for participation messages from Diaspora | | [pconfig](help/database/db_pconfig) | personal (per user) configuration storage | | [photo](help/database/db_photo) | photo storage | | [poll](help/database/db_poll) | data for polls | diff --git a/doc/database/db_particiation.md b/doc/database/db_particiation.md deleted file mode 100644 index 27f063241..000000000 --- a/doc/database/db_particiation.md +++ /dev/null @@ -1,10 +0,0 @@ -Table participation -=================== - -| Field | Description | Type | Null | Key | Default | Extra | -|-------------|------------------|------------------|------|-----|---------------------|-------| -| iid | item id | int(10) unsigned | NO | PRI | | | -| server | Name of server | varchar(60) | NO | PRI | | | -| cid | contact id | int(10) unsigned | NO | | | | - -Return to [database documentation](help/database) diff --git a/include/api.php b/include/api.php index bcf14d6a8..4c90ab80d 100644 --- a/include/api.php +++ b/include/api.php @@ -43,7 +43,6 @@ use Friendica\Model\Notification; use Friendica\Model\Photo; use Friendica\Model\Post; use Friendica\Model\User; -use Friendica\Model\UserItem; use Friendica\Model\Verb; use Friendica\Network\HTTPException; use Friendica\Network\HTTPException\BadRequestException; @@ -2170,24 +2169,21 @@ function api_statuses_mentions($type) $start = max(0, ($page - 1) * $count); - $query = "`gravity` IN (?, ?) AND `id` IN (SELECT `iid` FROM `user-item` - WHERE (`hidden` IS NULL OR NOT `hidden`) AND - `uid` = ? AND `notification-type` & ? != 0 - AND `iid` > ?"; + $query = "`gravity` IN (?, ?) AND `uri-id` IN + (SELECT `uri-id` FROM `post-user-notification` WHERE `uid` = ? AND `notification-type` & ? != 0 ORDER BY `uri-id`) + AND (`uid` = 0 OR (`uid` = ? AND NOT `global`)) AND `id` > ?"; $condition = [GRAVITY_PARENT, GRAVITY_COMMENT, api_user(), - UserItem::NOTIF_EXPLICIT_TAGGED | UserItem::NOTIF_IMPLICIT_TAGGED | - UserItem::NOTIF_THREAD_COMMENT | UserItem::NOTIF_DIRECT_COMMENT | - UserItem::NOTIF_DIRECT_THREAD_COMMENT, - $since_id]; + Post\UserNotification::NOTIF_EXPLICIT_TAGGED | Post\UserNotification::NOTIF_IMPLICIT_TAGGED | + Post\UserNotification::NOTIF_THREAD_COMMENT | Post\UserNotification::NOTIF_DIRECT_COMMENT | + Post\UserNotification::NOTIF_DIRECT_THREAD_COMMENT, + api_user(), $since_id]; if ($max_id > 0) { - $query .= " AND `iid` <= ?"; + $query .= " AND `id` <= ?"; $condition[] = $max_id; } - $query .= ")"; - array_unshift($condition, $query); $params = ['order' => ['id' => true], 'limit' => [$start, $count]]; diff --git a/include/enotify.php b/include/enotify.php index eb5660327..8936cadbe 100644 --- a/include/enotify.php +++ b/include/enotify.php @@ -32,7 +32,6 @@ use Friendica\Model\Item; use Friendica\Model\Notification; use Friendica\Model\Post; use Friendica\Model\User; -use Friendica\Model\UserItem; use Friendica\Protocol\Activity; /** @@ -149,9 +148,8 @@ function notification($params) } if ($params['type'] == Notification\Type::COMMENT || $params['type'] == Notification\Type::TAG_SELF) { - $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); + if (Post\ThreadUser::getIgnored($parent_uri_id, $params['uid'])) { + Logger::info('Thread is ignored', ['parent' => $parent_id, 'parent-uri-id' => $parent_uri_id]); return false; } @@ -593,32 +591,39 @@ function notification($params) /** * Checks for users who should be notified * - * @param int $itemid ID of the item for which the check should be done + * @param int $uri_id URI ID of the item for which the check should be done + * @param int $uid User ID of the item * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ -function check_user_notification($itemid) { - // fetch all users with notifications - $useritems = DBA::select('user-item', ['uid', 'notification-type'], ['iid' => $itemid]); - while ($useritem = DBA::fetch($useritems)) { - check_item_notification($itemid, $useritem['uid'], $useritem['notification-type']); +function check_user_notification(int $uri_id, int $uid) { + $condition = ['uri-id' => $uri_id]; + + // fetch all users with notifications on public posts + if ($uid != 0) { + $condition['uid'] = $uid; } - DBA::close($useritems); + + $usernotifications = DBA::select('post-user-notification', ['uri-id', 'uid', 'notification-type'], $condition); + while ($usernotification = DBA::fetch($usernotifications)) { + check_item_notification($usernotification['uri-id'], $usernotification['uid'], $usernotification['notification-type']); + } + DBA::close($usernotifications); } /** * Checks for item related notifications and sends them * - * @param int $itemid ID of the item for which the check should be done + * @param int $uri_id URI ID of the item for which the check should be done * @param int $uid User ID * @param int $notification_type Notification bits * @return bool * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ -function check_item_notification($itemid, $uid, $notification_type) { +function check_item_notification(int $uri_id, int $uid, int $notification_type) { $fields = ['id', 'uri-id', 'mention', 'parent', 'parent-uri-id', 'thr-parent-id', 'title', 'body', 'author-link', 'author-name', 'author-avatar', 'author-id', 'gravity', 'guid', 'parent-uri', 'uri', 'contact-id', 'network']; - $condition = ['id' => $itemid, 'deleted' => false]; + $condition = ['uri-id' => $uri_id, 'uid' => $uid, 'deleted' => false]; $item = Post::selectFirstForUser($uid, $fields, $condition); if (!DBA::isResult($item)) { return false; @@ -638,19 +643,19 @@ function check_item_notification($itemid, $uid, $notification_type) { $params['link'] = DI::baseUrl() . '/display/' . urlencode($item['guid']); // Set the activity flags - $params['activity']['explicit_tagged'] = ($notification_type & UserItem::NOTIF_EXPLICIT_TAGGED); - $params['activity']['implicit_tagged'] = ($notification_type & UserItem::NOTIF_IMPLICIT_TAGGED); - $params['activity']['origin_comment'] = ($notification_type & UserItem::NOTIF_DIRECT_COMMENT); - $params['activity']['origin_thread'] = ($notification_type & UserItem::NOTIF_THREAD_COMMENT); - $params['activity']['thread_comment'] = ($notification_type & UserItem::NOTIF_COMMENT_PARTICIPATION); - $params['activity']['thread_activity'] = ($notification_type & UserItem::NOTIF_ACTIVITY_PARTICIPATION); + $params['activity']['explicit_tagged'] = ($notification_type & Post\UserNotification::NOTIF_EXPLICIT_TAGGED); + $params['activity']['implicit_tagged'] = ($notification_type & Post\UserNotification::NOTIF_IMPLICIT_TAGGED); + $params['activity']['origin_comment'] = ($notification_type & Post\UserNotification::NOTIF_DIRECT_COMMENT); + $params['activity']['origin_thread'] = ($notification_type & Post\UserNotification::NOTIF_THREAD_COMMENT); + $params['activity']['thread_comment'] = ($notification_type & Post\UserNotification::NOTIF_COMMENT_PARTICIPATION); + $params['activity']['thread_activity'] = ($notification_type & Post\UserNotification::NOTIF_ACTIVITY_PARTICIPATION); // Tagging a user in a direct post (first comment level) means a direct comment - if ($params['activity']['explicit_tagged'] && ($notification_type & UserItem::NOTIF_DIRECT_THREAD_COMMENT)) { + if ($params['activity']['explicit_tagged'] && ($notification_type & Post\UserNotification::NOTIF_DIRECT_THREAD_COMMENT)) { $params['activity']['origin_comment'] = true; } - if ($notification_type & UserItem::NOTIF_SHARED) { + if ($notification_type & Post\UserNotification::NOTIF_SHARED) { $params['type'] = Notification\Type::SHARE; $params['verb'] = Activity::POST; @@ -667,22 +672,22 @@ function check_item_notification($itemid, $uid, $notification_type) { $params['item'] = $parent_item; } } - } elseif ($notification_type & UserItem::NOTIF_EXPLICIT_TAGGED) { + } elseif ($notification_type & Post\UserNotification::NOTIF_EXPLICIT_TAGGED) { $params['type'] = Notification\Type::TAG_SELF; $params['verb'] = Activity::TAG; - } elseif ($notification_type & UserItem::NOTIF_IMPLICIT_TAGGED) { + } elseif ($notification_type & Post\UserNotification::NOTIF_IMPLICIT_TAGGED) { $params['type'] = Notification\Type::COMMENT; $params['verb'] = Activity::POST; - } elseif ($notification_type & UserItem::NOTIF_THREAD_COMMENT) { + } elseif ($notification_type & Post\UserNotification::NOTIF_THREAD_COMMENT) { $params['type'] = Notification\Type::COMMENT; $params['verb'] = Activity::POST; - } elseif ($notification_type & UserItem::NOTIF_DIRECT_COMMENT) { + } elseif ($notification_type & Post\UserNotification::NOTIF_DIRECT_COMMENT) { $params['type'] = Notification\Type::COMMENT; $params['verb'] = Activity::POST; - } elseif ($notification_type & UserItem::NOTIF_COMMENT_PARTICIPATION) { + } elseif ($notification_type & Post\UserNotification::NOTIF_COMMENT_PARTICIPATION) { $params['type'] = Notification\Type::COMMENT; $params['verb'] = Activity::POST; - } elseif ($notification_type & UserItem::NOTIF_ACTIVITY_PARTICIPATION) { + } elseif ($notification_type & Post\UserNotification::NOTIF_ACTIVITY_PARTICIPATION) { $params['type'] = Notification\Type::COMMENT; $params['verb'] = Activity::POST; } else { diff --git a/src/Database/DBStructure.php b/src/Database/DBStructure.php index 8a3d6cc80..b9e53be04 100644 --- a/src/Database/DBStructure.php +++ b/src/Database/DBStructure.php @@ -79,7 +79,8 @@ class DBStructure } $old_tables = ['fserver', 'gcign', 'gcontact', 'gcontact-relation', 'gfollower' ,'glink', 'item-delivery-data', - 'item-activity', 'item-content', 'item_id', 'poll', 'poll_result', 'queue', 'retriever_rule', 'sign', 'spam', 'term']; + 'item-activity', 'item-content', 'item_id', 'participation', 'poll', 'poll_result', 'queue', 'retriever_rule', + 'sign', 'spam', 'term', 'user-item']; $tables = DBA::selectToArray(['INFORMATION_SCHEMA' => 'TABLES'], ['TABLE_NAME'], ['TABLE_SCHEMA' => DBA::databaseName(), 'TABLE_TYPE' => 'BASE TABLE']); diff --git a/src/Database/PostUpdate.php b/src/Database/PostUpdate.php index 51ce0d2b5..3a1333780 100644 --- a/src/Database/PostUpdate.php +++ b/src/Database/PostUpdate.php @@ -31,7 +31,6 @@ use Friendica\Model\Photo; use Friendica\Model\Post; use Friendica\Model\Post\Category; use Friendica\Model\Tag; -use Friendica\Model\UserItem; use Friendica\Model\Verb; use Friendica\Util\Strings; @@ -168,7 +167,7 @@ class PostUpdate } /** - * update user-item data with notifications + * update user notification data * * @return bool "true" when the job is done * @throws \Friendica\Network\HTTPException\InternalServerErrorException @@ -188,7 +187,7 @@ class PostUpdate $rows = 0; $condition = ["`id` > ?", $id]; $params = ['order' => ['id'], 'limit' => 10000]; - $items = DBA::select('item', ['id'], $condition, $params); + $items = DBA::select('item', ['id', 'uri-id', 'uid'], $condition, $params); if (DBA::errorNo() != 0) { Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]); @@ -198,7 +197,7 @@ class PostUpdate while ($item = DBA::fetch($items)) { $id = $item['id']; - UserItem::setNotification($item['id']); + Post\UserNotification::setNotification($item['uri-id'], $item['uid']); ++$rows; } diff --git a/src/Factory/Api/Mastodon/Status.php b/src/Factory/Api/Mastodon/Status.php index cd611e8ee..6a3212c26 100644 --- a/src/Factory/Api/Mastodon/Status.php +++ b/src/Factory/Api/Mastodon/Status.php @@ -77,9 +77,9 @@ class Status extends BaseFactory $userAttributes = new \Friendica\Object\Api\Mastodon\Status\UserAttributes( Post::exists(['thr-parent-id' => $uriId, 'uid' => $uid, 'origin' => true, 'gravity' => GRAVITY_ACTIVITY, 'vid' => Verb::getID(Activity::LIKE)]), Post::exists(['thr-parent-id' => $uriId, 'uid' => $uid, 'origin' => true, 'gravity' => GRAVITY_ACTIVITY, 'vid' => Verb::getID(Activity::ANNOUNCE)]), - DBA::exists('thread', ['iid' => $item['id'], 'uid' => $item['uid'], 'ignored' => true]), + Post\ThreadUser::getIgnored($uriId, $item['uid']), (bool)$item['starred'], - DBA::exists('user-item', ['iid' => $item['id'], 'uid' => $item['uid'], 'pinned' => true]) + Post\ThreadUser::getPinned($uriId, $item['uid']) ); $sensitive = DBA::exists('tag-view', ['uri-id' => $uriId, 'name' => 'nsfw']); diff --git a/src/Model/Item.php b/src/Model/Item.php index 6d882463f..5041b7ef5 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -31,7 +31,6 @@ use Friendica\Core\Session; use Friendica\Core\System; use Friendica\Model\Tag; use Friendica\Core\Worker; -use Friendica\Database\Database; use Friendica\Database\DBA; use Friendica\Database\DBStructure; use Friendica\DI; @@ -99,14 +98,6 @@ class Item 'author-id', 'author-link', 'owner-link', 'contact-uid', 'signed_text', 'network']; - // Field list for "post-content" table that is mixed with the item table - const MIXED_CONTENT_FIELDLIST = ['title', 'content-warning', 'body', 'location', - 'coord', 'app', 'rendered-hash', 'rendered-html', 'verb', - 'object-type', 'object', 'target-type', 'target', 'plink']; - - // Field list for "post-content" table that is not present in the "item" table - const CONTENT_FIELDLIST = ['language', 'raw-body']; - // All fields in the item table const ITEM_FIELDLIST = ['id', 'uid', 'parent', 'uri', 'parent-uri', 'thr-parent', 'guid', 'uri-id', 'parent-uri-id', 'thr-parent-id', 'vid', @@ -121,6 +112,22 @@ class Item 'author-id', 'author-link', 'author-name', 'author-avatar', 'author-network', 'owner-id', 'owner-link', 'owner-name', 'owner-avatar', 'causer-id']; + // Item fiels that still are in use + const USED_FIELDLIST = ['id', 'parent', 'guid', 'uri', 'uri-id', 'parent-uri', 'parent-uri-id', + 'thr-parent', 'thr-parent-id', 'created', 'edited', 'commented', 'received', 'changed', + 'gravity', 'network', 'owner-id', 'author-id', 'causer-id', 'vid', 'extid', 'post-type', + 'global', 'private', 'visible', 'moderated', 'deleted', 'uid', 'contact-id', + 'wall', 'origin', 'pubmail', 'starred', 'unseen', 'mention', 'forum_mode', 'psid', + 'event-id', 'type', 'bookmark']; + + // Legacy item fields that aren't stored any more in the item table + const LEGACY_FIELDLIST = ['uri-hash', 'iaid', 'icid', 'attach', + 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'postopts', + 'resource-id', 'inform', 'file', 'location', 'coord', 'tag', 'plink', + 'title', 'content-warning', 'body', 'app', 'verb', 'object-type', 'object', + 'target-type', 'target', 'author-name', 'author-link', 'author-avatar', + 'owner-name', 'owner-link', 'owner-avatar', 'rendered-hash', 'rendered-html']; + // List of all verbs that don't need additional content data. // Never reorder or remove entries from this list. Just add new ones at the end, if needed. const ACTIVITIES = [ @@ -133,49 +140,6 @@ class Item const PRIVATE = 1; const UNLISTED = 2; - const TABLES = ['item', 'user-item', 'post-content', 'post-delivery-data', 'diaspora-interaction']; - - private static function getItemFields() - { - $definition = DBStructure::definition('', false); - - $postfields = []; - foreach (self::TABLES as $table) { - $postfields[$table] = array_keys($definition[$table]['fields']); - } - - return $postfields; - } - - /** - * 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']; - } - /** * Update existing item entries * @@ -285,12 +249,9 @@ class Item Post\User::update($item['uri-id'], $uid, ['hidden' => true], true); } - // "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) { + if ($item['uid'] == $uid) { self::markForDeletionById($item['id'], PRIORITY_HIGH); - } else { + } elseif ($item['uid'] != 0) { Logger::log('Wrong ownership. Not deleting item ' . $item['id']); } } @@ -393,12 +354,6 @@ class Item } } 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 = Post::selectFirst(['id'], ['uri-id' => $item['uri-id'], 'uid' => 0, 'deleted' => false]); - if (DBA::isResult($global_item)) { - DBA::update('user-item', ['hidden' => true], ['iid' => $global_item['id'], 'uid' => $item['uid']], true); - } } Logger::info('Item has been marked for deletion.', ['id' => $item_id]); @@ -755,8 +710,6 @@ class Item public static function insert($item, $notify = false, $dontcache = false) { - $structure = self::getItemFields(); - $orig_item = $item; $priority = PRIORITY_HIGH; @@ -952,26 +905,11 @@ class Item // Update the contact relations Contact\Relation::store($toplevel_parent['author-id'], $item['author-id'], $item['created']); - - unset($item['parent_origin']); } else { $parent_id = 0; $parent_origin = $item['origin']; } - // We don't store the causer link, only the id - unset($item['causer-link']); - - // We don't store these fields anymore in the item table - unset($item['author-link']); - unset($item['author-name']); - unset($item['author-avatar']); - unset($item['author-network']); - - unset($item['owner-link']); - unset($item['owner-name']); - unset($item['owner-avatar']); - $item['parent-uri-id'] = ItemURI::getIdByURI($item['parent-uri']); $item['thr-parent-id'] = ItemURI::getIdByURI($item['thr-parent']); @@ -994,14 +932,10 @@ class Item $item['edit'] = false; $item['parent'] = $parent_id; Hook::callAll('post_local', $item); - unset($item['edit']); } else { Hook::callAll('post_remote', $item); } - // Set after the insert because top-level posts are self-referencing - unset($item['parent']); - if (!empty($item['cancel'])) { Logger::log('post cancelled by addon.'); return 0; @@ -1020,15 +954,6 @@ class Item $item['deny_gid'] ); - unset($item['allow_cid']); - unset($item['allow_gid']); - unset($item['deny_cid']); - unset($item['deny_gid']); - - // This array field is used to trigger some automatic reactions - // It is mainly used in the "post_local" hook. - unset($item['api_source']); - if ($item['verb'] == Activity::ANNOUNCE) { self::setOwnerforResharedItem($item); } @@ -1040,10 +965,6 @@ class Item // Check for hashtags in the body and repair or add hashtag links $item['body'] = self::setHashtags($item['body']); - if (!empty($item['attach'])) { - Post\Media::insertFromAttachment($item['uri-id'], $item['attach']); - } - // Fill the cache field self::putInCache($item); @@ -1053,40 +974,27 @@ class Item $notify_type = Delivery::POST; } + // Filling item related side tables + if (!empty($item['attach'])) { + Post\Media::insertFromAttachment($item['uri-id'], $item['attach']); + } + if (!in_array($item['verb'], self::ACTIVITIES)) { Post\Content::insert($item['uri-id'], $item); } - $body = $item['body']; - $verb = $item['verb']; - - // We just remove everything that is content - foreach (array_merge(self::CONTENT_FIELDLIST, self::MIXED_CONTENT_FIELDLIST) as $field) { - unset($item[$field]); - } - - unset($item['activity']); - - // Filling item related side tables - // Diaspora signature if (!empty($item['diaspora_signed_text'])) { DBA::replace('diaspora-interaction', ['uri-id' => $item['uri-id'], 'interaction' => $item['diaspora_signed_text']]); } - unset($item['diaspora_signed_text']); - // Attached file links if (!empty($item['file'])) { Post\Category::storeTextByURIId($item['uri-id'], $item['uid'], $item['file']); } - unset($item['file']); - // Delivery relevant data $delivery_data = Post\DeliveryData::extractFields($item); - unset($item['postopts']); - unset($item['inform']); if (!empty($item['origin']) || !empty($item['wall']) || !empty($delivery_data['postopts']) || !empty($delivery_data['inform'])) { Post\DeliveryData::insert($item['uri-id'], $delivery_data); @@ -1094,33 +1002,38 @@ class Item // Store tags from the body if this hadn't been handled previously in the protocol classes if (!Tag::existsForPost($item['uri-id'])) { - Tag::storeFromBody($item['uri-id'], $body); + Tag::storeFromBody($item['uri-id'], $item['body']); } $id = Post\User::insert($item['uri-id'], $item['uid'], $item); - if ($id) { - // 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]); - } - } - - $condition = ['uri-id' => $item['uri-id'], 'uid' => $item['uid'], 'network' => $item['network']]; - if (Post::exists($condition)) { - Logger::notice('Item is already inserted - aborting', $condition); - return 0; - } - - $result = DBA::insert('item', $item); - - // When the item was successfully stored we fetch the ID of the item. - $current_post = DBA::lastInsertId(); - } else { + if (!$id) { Logger::notice('Post-User is already inserted - aborting', ['uid' => $item['uid'], 'uri-id' => $item['uri-id']]); return 0; } + if ($item['gravity'] == GRAVITY_PARENT) { + Post\ThreadUser::insert($item['uri-id'], $item['uid'], $item); + } + + $condition = ['uri-id' => $item['uri-id'], 'uid' => $item['uid'], 'network' => $item['network']]; + if (Post::exists($condition)) { + Logger::notice('Item is already inserted - aborting', $condition); + return 0; + } + + // Remove all fields that aren't part of the item table + $table_fields = DBStructure::getFieldsForTable('item', $item); + + // We remove all legacy fields that now are stored in other tables + foreach (self::LEGACY_FIELDLIST as $field) { + unset($table_fields[$field]); + } + + $result = DBA::insert('item', $table_fields); + + // When the item was successfully stored we fetch the ID of the item. + $current_post = DBA::lastInsertId(); + if (empty($current_post) || !DBA::isResult($result)) { // On failure store the data into a spool file so that the "SpoolPost" worker can try again later. Logger::warning('Could not store item. it will be spooled', ['result' => $result, 'id' => $current_post]); @@ -1146,7 +1059,7 @@ class Item $update_commented = in_array($item['gravity'], [GRAVITY_PARENT, GRAVITY_COMMENT]); } else { // Update when it isn't a follow or tag verb - $update_commented = !in_array($verb, [Activity::FOLLOW, Activity::TAG]); + $update_commented = !in_array($item['verb'], [Activity::FOLLOW, Activity::TAG]); } if ($update_commented) { @@ -1191,9 +1104,10 @@ class Item self::updateContact($item); - UserItem::setNotification($current_post); + Post\UserNotification::setNotification($item['uri-id'], $item['uid']); - check_user_notification($current_post); + check_user_notification($item['uri-id'], $item['uid']); + //check_user_notification($current_post); // Distribute items to users who subscribed to their tags self::distributeByTags($item); diff --git a/src/Model/Post.php b/src/Model/Post.php index 59e2f419c..73742b3b2 100644 --- a/src/Model/Post.php +++ b/src/Model/Post.php @@ -192,7 +192,7 @@ class Post $selected = array_merge(['author-addr', 'author-nick', 'owner-addr', 'owner-nick', 'causer-addr', 'causer-nick', 'causer-network', 'photo', 'name-date', 'uri-date', 'avatar-date', 'thumb', 'dfrn-id', 'parent-guid', 'parent-network', 'parent-author-id', 'parent-author-link', 'parent-author-name', - 'parent-author-network', 'signed_text'], Item::DISPLAY_FIELDLIST, Item::ITEM_FIELDLIST, Item::CONTENT_FIELDLIST); + 'parent-author-network', 'signed_text', 'language', 'raw-body'], Item::DISPLAY_FIELDLIST, Item::ITEM_FIELDLIST); if ($view == 'post-thread-view') { $selected = array_merge($selected, ['ignored', 'iid']); @@ -258,7 +258,7 @@ class Post AND (NOT `causer-blocked` OR `causer-id` = ?) AND NOT `contact-blocked` AND ((NOT `contact-readonly` AND NOT `contact-pending` AND (`contact-rel` IN (?, ?))) OR `self` OR `gravity` != ? OR `contact-uid` = ?) - AND NOT EXISTS (SELECT `iid` FROM `user-item` WHERE `hidden` AND `iid` = `id` AND `uid` = ?) + AND NOT EXISTS (SELECT `uri-id` FROM `post-user` WHERE `hidden` AND `uri-id` = `" . $view . "`.`uri-id` AND `uid` = ?) AND NOT EXISTS (SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND `cid` = `author-id` AND `blocked`) AND NOT EXISTS (SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND `cid` = `owner-id` AND `blocked`) AND NOT EXISTS (SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND `cid` = `author-id` AND `ignored` AND `gravity` = ?) @@ -272,7 +272,7 @@ class Post unset($selected['pinned']); $selected = array_flip($selected); - $select_string = "(SELECT `pinned` FROM `user-item` WHERE `iid` = `" . $view . "`.`id` AND uid=`" . $view . "`.`uid`) AS `pinned`, "; + $select_string = "(SELECT `pinned` FROM `post-thread-user` WHERE `uri-id` = `" . $view . "`.`uri-id` AND uid=`" . $view . "`.`uid`) AS `pinned`, "; } $select_string .= implode(', ', array_map([DBA::class, 'quoteIdentifier'], $selected)); @@ -344,32 +344,6 @@ class Post } } - /** - * 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 * @@ -383,24 +357,24 @@ class Post */ 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; + $postthreaduser = DBA::select('post-thread-user', ['uri-id'], ['uid' => $uid, 'pinned' => true]); + if (!DBA::isResult($postthreaduser)) { + return $postthreaduser; } - + $pinned = []; - while ($useritem = DBA::fetch($useritems)) { - $pinned[] = $useritem['iid']; + while ($useritem = DBA::fetch($postthreaduser)) { + $pinned[] = $useritem['uri-id']; } - DBA::close($useritems); + DBA::close($postthreaduser); if (empty($pinned)) { return []; } - $condition = DBA::mergeConditions(['iid' => $pinned], $condition); + $condition = DBA::mergeConditions(['uri-id' => $pinned, 'uid' => $uid, 'gravity' => GRAVITY_PARENT], $condition); - return self::selectThreadForUser($uid, $selected, $condition, $params); + return self::selectForUser($uid, $selected, $condition, $params); } /** @@ -431,6 +405,8 @@ class Post unset($fields['parent-uri']); unset($fields['parent-uri-id']); + $thread_condition = DBA::mergeConditions($condition, ['gravity' => GRAVITY_PARENT]); + // To ensure the data integrity we do it in an transaction DBA::transaction(); @@ -472,34 +448,44 @@ class Post $affected = max($affected, DBA::affectedRows()); } - $update_fields = DBStructure::getFieldsForTable('thread', $fields); + $update_fields = DBStructure::getFieldsForTable('post-thread-user', $fields); if (!empty($update_fields)) { - $rows = DBA::selectToArray('post-view', ['id'], $condition); - $ids = array_column($rows, 'id'); - if (!DBA::update('thread', $update_fields, ['iid' => $ids])) { + $rows = DBA::selectToArray('post-view', ['post-user-id'], $thread_condition); + $thread_puids = array_column($rows, 'post-user-id'); + + $post_thread_condition = DBA::collapseCondition(['id' => $thread_puids]); + + $post_thread_condition[0] = "EXISTS(SELECT `id` FROM `post-user` WHERE " . + $post_thread_condition[0] . " AND `uri-id` = `post-thread-user`.`uri-id` AND `uid` = `post-thread-user`.`uid`)"; + if (!DBA::update('post-thread-user', $update_fields, $post_thread_condition)) { DBA::rollback(); - Logger::notice('Updating thread failed', ['fields' => $update_fields, 'condition' => $condition]); + Logger::notice('Updating post-thread-user failed', ['fields' => $update_fields, 'condition' => $condition]); return false; } $affected = max($affected, DBA::affectedRows()); } - $item_fields = ['guid', 'type', 'wall', 'gravity', 'extid', 'created', 'edited', 'commented', 'received', 'changed', - 'resource-id', 'post-type', 'private', 'pubmail', 'moderated', 'visible', 'starred', 'bookmark', - 'unseen', 'deleted', 'origin', 'forum_mode', 'mention', 'global', 'network', 'vid', 'psid', - 'contact-id', 'author-id', 'owner-id', 'causer-id', 'event-id']; + $update_fields = DBStructure::getFieldsForTable('thread', $fields); + if (!empty($update_fields)) { + $rows = DBA::selectToArray('post-view', ['id'], $thread_condition); + $ids = array_column($rows, 'id'); + if (!DBA::update('thread', $update_fields, ['iid' => $ids])) { + DBA::rollback(); + Logger::notice('Updating thread failed', ['fields' => $update_fields, 'condition' => $thread_condition]); + return false; + } + $affected = max($affected, DBA::affectedRows()); + } $update_fields = []; - foreach ($item_fields as $field) { + foreach (Item::USED_FIELDLIST as $field) { if (array_key_exists($field, $fields)) { $update_fields[$field] = $fields[$field]; } } if (!empty($update_fields)) { - if (empty($ids)) { - $rows = DBA::selectToArray('post-view', ['id'], $condition, []); - $ids = array_column($rows, 'id'); - } + $rows = DBA::selectToArray('post-view', ['id'], $condition, []); + $ids = array_column($rows, 'id'); if (!DBA::update('item', $update_fields, ['id' => $ids])) { DBA::rollback(); Logger::notice('Updating item failed', ['fields' => $update_fields, 'condition' => $condition]); diff --git a/src/Model/Post/ThreadUser.php b/src/Model/Post/ThreadUser.php new file mode 100644 index 000000000..f0f2f17a0 --- /dev/null +++ b/src/Model/Post/ThreadUser.php @@ -0,0 +1,153 @@ +. + * + */ + +namespace Friendica\Model\Post; + +use \BadMethodCallException; +use Friendica\Database\Database; +use Friendica\Database\DBA; +use Friendica\Database\DBStructure; + +class ThreadUser +{ + /** + * Insert a new URI user entry + * + * @param integer $uri_id + * @param integer $uid + * @param array $fields + * @return bool success + * @throws \Exception + */ + public static function insert(int $uri_id, int $uid, array $data = []) + { + if (empty($uri_id)) { + throw new BadMethodCallException('Empty URI_id'); + } + + $fields = DBStructure::getFieldsForTable('post-thread-user', $data); + + // Additionally assign the key fields + $fields['uri-id'] = $uri_id; + $fields['uid'] = $uid; + + return DBA::insert('post-thread-user', $fields, Database::INSERT_IGNORE); + } + + /** + * Update a URI user entry + * + * @param integer $uri_id + * @param integer $uid + * @param array $data + * @param bool $insert_if_missing + * @return bool + * @throws \Exception + */ + public static function update(int $uri_id, int $uid, array $data = [], bool $insert_if_missing = false) + { + if (empty($uri_id)) { + throw new BadMethodCallException('Empty URI_id'); + } + + $fields = DBStructure::getFieldsForTable('post-thread-user', $data); + + // Remove the key fields + unset($fields['uri-id']); + unset($fields['uid']); + + if (empty($fields)) { + return true; + } + + return DBA::update('post-thread-user', $fields, ['uri-id' => $uri_id, 'uid' => $uid], $insert_if_missing ? true : []); + } + + /** + * Delete a row from the post-thread-user table + * + * @param array $conditions Field condition(s) + * @param array $options + * - cascade: If true we delete records in other tables that depend on the one we're deleting through + * relations (default: true) + * + * @return boolean was the delete successful? + * @throws \Exception + */ + public static function delete(array $conditions, array $options = []) + { + return DBA::delete('post-thread-user', $conditions, $options); + } + + /** + * @param int $uri_id + * @param int $uid + * @return bool + * @throws Exception + */ + public static function getIgnored(int $uri_id, int $uid) + { + $threaduser = DBA::selectFirst('post-thread-user', ['ignored'], ['uri-id' => $uri_id, 'uid' => $uid]); + if (empty($threaduser)) { + return false; + } + return (bool)$threaduser['ignored']; + } + + /** + * @param int $uri_id + * @param int $uid + * @param int $ignored + * @return void + * @throws Exception + */ + public static function setIgnored(int $uri_id, int $uid, int $ignored) + { + DBA::update('post-thread-user', ['ignored' => $ignored], ['uri-id' => $uri_id, 'uid' => $uid], true); + } + + /** + * @param int $uri_id + * @param int $uid + * @return bool + * @throws Exception + */ + public static function getPinned(int $uri_id, int $uid) + { + $threaduser = DBA::selectFirst('post-thread-user', ['pinned'], ['uri-id' => $uri_id, 'uid' => $uid]); + if (empty($threaduser)) { + return false; + } + return (bool)$threaduser['pinned']; + } + + /** + * @param int $uri_id + * @param int $uid + * @param int $pinned + * @return void + * @throws Exception + */ + public static function setPinned(int $uri_id, int $uid, int $pinned) + { + DBA::update('post-thread-user', ['pinned' => $pinned], ['uri-id' => $uri_id, 'uid' => $uid], true); + } +} diff --git a/src/Model/Post/User.php b/src/Model/Post/User.php index 6c2176656..dc53b726e 100644 --- a/src/Model/Post/User.php +++ b/src/Model/Post/User.php @@ -29,7 +29,7 @@ use Friendica\Database\DBStructure; class User { /** - * Insert a new URI user entry + * Insert a new post user entry * * @param integer $uri_id * @param integer $uid @@ -66,7 +66,7 @@ class User } /** - * Update a URI user entry + * Update a post user entry * * @param integer $uri_id * @param integer $uid diff --git a/src/Model/UserItem.php b/src/Model/Post/UserNotification.php similarity index 78% rename from src/Model/UserItem.php rename to src/Model/Post/UserNotification.php index b4346c221..9173cfbe2 100644 --- a/src/Model/UserItem.php +++ b/src/Model/Post/UserNotification.php @@ -19,17 +19,23 @@ * */ -namespace Friendica\Model; +namespace Friendica\Model\Post; +use \BadMethodCallException; use Friendica\Core\Logger; use Friendica\Core\Hook; +use Friendica\Database\Database; use Friendica\Database\DBA; +use Friendica\Database\DBStructure; use Friendica\DI; +use Friendica\Model\Contact; +use Friendica\Model\Post; use Friendica\Util\Strings; use Friendica\Model\Tag; use Friendica\Protocol\Activity; -class UserItem + +class UserNotification { // Notification types const NOTIF_NONE = 0; @@ -42,18 +48,89 @@ class UserItem const NOTIF_DIRECT_THREAD_COMMENT = 64; const NOTIF_SHARED = 128; + + /** + * Insert a new user notification entry + * + * @param integer $uri_id + * @param integer $uid + * @param array $fields + * @return bool success + * @throws \Exception + */ + public static function insert(int $uri_id, int $uid, array $data = []) + { + if (empty($uri_id)) { + throw new BadMethodCallException('Empty URI_id'); + } + + $fields = DBStructure::getFieldsForTable('post-user-notification', $data); + + // Additionally assign the key fields + $fields['uri-id'] = $uri_id; + $fields['uid'] = $uid; + + return DBA::insert('post-user-notification', $fields, Database::INSERT_IGNORE); + } + + /** + * Update a user notification entry + * + * @param integer $uri_id + * @param integer $uid + * @param array $data + * @param bool $insert_if_missing + * @return bool + * @throws \Exception + */ + public static function update(int $uri_id, int $uid, array $data = [], bool $insert_if_missing = false) + { + if (empty($uri_id)) { + throw new BadMethodCallException('Empty URI_id'); + } + + $fields = DBStructure::getFieldsForTable('post-user-notification', $data); + + // Remove the key fields + unset($fields['uri-id']); + unset($fields['uid']); + + if (empty($fields)) { + return true; + } + + return DBA::update('post-user-notification', $fields, ['uri-id' => $uri_id, 'uid' => $uid], $insert_if_missing ? true : []); + } + + /** + * Delete a row from the post-user-notification table + * + * @param array $conditions Field condition(s) + * @param array $options + * - cascade: If true we delete records in other tables that depend on the one we're deleting through + * relations (default: true) + * + * @return boolean was the delete successful? + * @throws \Exception + */ + public static function delete(array $conditions, array $options = []) + { + return DBA::delete('post-user-notification', $conditions, $options); + } + /** * Checks an item for notifications and sets the "notification-type" field * @ToDo: * - Check for mentions in posts with "uid=0" where the user hadn't interacted before * - * @param int $iid Item ID + * @param int $uri_id URI ID + * @param int $uid user ID */ - public static function setNotification(int $iid) + public static function setNotification(int $uri_id, int $uid) { $fields = ['id', 'uri-id', 'parent-uri-id', 'uid', 'body', 'parent', 'gravity', - 'private', 'contact-id', 'thr-parent', 'parent-uri', 'author-id', 'verb']; - $item = Post::selectFirst($fields, ['id' => $iid, 'origin' => false]); + 'private', 'contact-id', 'thr-parent', 'parent-uri-id', 'parent-uri', 'author-id', 'verb']; + $item = Post::selectFirst($fields, ['uri-id' => $uri_id, 'uid' => $uid, 'origin' => false]); if (!DBA::isResult($item)) { return; } @@ -73,7 +150,7 @@ class UserItem // Add every user who participated so far in this thread // This can only happen with participations on global items. (means: uid = 0) $users = DBA::p("SELECT DISTINCT(`contact-uid`) AS `uid` FROM `post-view` - WHERE `contact-uid` != 0 AND `parent` IN (SELECT `parent` FROM `post-view` WHERE `id` = ?)", $iid); + WHERE `contact-uid` != 0 AND `parent-uri-id` = ? AND `uid` = ?", $item['parent-uri-id'], $uid); while ($user = DBA::fetch($users)) { $uids[] = $user['uid']; } @@ -92,8 +169,7 @@ class UserItem */ private static function setNotificationForUser(array $item, int $uid) { - $thread = Post::selectFirstThreadForUser($uid, ['ignored'], ['iid' => $item['parent'], 'deleted' => false]); - if (!empty($thread['ignored'])) { + if (Post\ThreadUser::getIgnored($item['parent-uri-id'], $uid)) { return; } @@ -153,11 +229,11 @@ class UserItem return; } - Logger::info('Set notification', ['iid' => $item['id'], 'uid' => $uid, 'notification-type' => $notification_type]); + Logger::info('Set notification', ['iid' => $item['id'], 'uri-id' => $item['uri-id'], 'uid' => $uid, 'notification-type' => $notification_type]); $fields = ['notification-type' => $notification_type]; Post\User::update($item['uri-id'], $uid, $fields); - DBA::update('user-item', $fields, ['iid' => $item['id'], 'uid' => $uid], true); + self::update($item['uri-id'], $uid, $fields, true); } /** diff --git a/src/Module/Item/Ignore.php b/src/Module/Item/Ignore.php index 668e49310..22d676b22 100644 --- a/src/Module/Item/Ignore.php +++ b/src/Module/Item/Ignore.php @@ -49,27 +49,17 @@ class Ignore extends BaseModule $dba = DI::dba(); - $thread = Post::selectFirstThreadForUser(local_user(), ['uid', 'ignored'], ['iid' => $itemId]); + $thread = Post::selectFirst(['uri-id', 'uid'], ['id' => $itemId, 'gravity' => GRAVITY_PARENT]); if (!$dba->isResult($thread)) { throw new HTTPException\NotFoundException(); } - // Numeric values are needed for the json output further below - $ignored = !empty($thread['ignored']) ? 0 : 1; + $ignored = !Post\ThreadUser::getIgnored($thread['uri-id'], local_user()); - switch ($thread['uid'] ?? 0) { - // if the thread is from the current user - case local_user(): - $dba->update('thread', ['ignored' => $ignored], ['iid' => $itemId]); - break; - // 0 (null will get transformed to 0) => it's a public post - case 0: - $dba->update('user-item', ['ignored' => $ignored], ['iid' => $itemId, 'uid' => local_user()], true); - break; - // Throws a BadRequestException and not a ForbiddenException on purpose - // Avoids harvesting existing, but forbidden IIDs (security issue) - default: - throw new HTTPException\BadRequestException(); + if (in_array($thread['uid'], [0, local_user()])) { + Post\ThreadUser::setIgnored($thread['uri-id'], local_user(), $ignored); + } else { + throw new HTTPException\BadRequestException(); } // See if we've been passed a return path to redirect to diff --git a/src/Module/Item/Pin.php b/src/Module/Item/Pin.php index d99b1a345..08c751de2 100644 --- a/src/Module/Item/Pin.php +++ b/src/Module/Item/Pin.php @@ -24,8 +24,9 @@ namespace Friendica\Module\Item; use Friendica\BaseModule; use Friendica\Core\Session; use Friendica\Core\System; +use Friendica\Database\DBA; use Friendica\DI; -use Friendica\Model\Item; +use Friendica\Model\Post; use Friendica\Network\HTTPException; /** @@ -47,9 +48,18 @@ class Pin extends BaseModule $itemId = intval($parameters['id']); - $pinned = !Item::getPinned($itemId, local_user()); + $item = Post::selectFirst(['uri-id', 'uid'], ['id' => $itemId]); + if (!DBA::isResult($item)) { + throw new HTTPException\NotFoundException(); + } - Item::setPinned($itemId, local_user(), $pinned); + if (!in_array($item['uid'], [0, local_user()])) { + throw new HttpException\ForbiddenException($l10n->t('Access denied.')); + } + + $pinned = !Post\ThreadUser::getPinned($item['uri-id'], local_user()); + + Post\ThreadUser::setPinned($item['uri-id'], local_user(), $pinned); // See if we've been passed a return path to redirect to $return_path = $_REQUEST['return'] ?? ''; diff --git a/src/Object/Post.php b/src/Object/Post.php index 34f46f797..d2c0b31e1 100644 --- a/src/Object/Post.php +++ b/src/Object/Post.php @@ -307,17 +307,15 @@ class Post if ($this->isToplevel()) { if(local_user()) { - $thread = PostModel::selectFirstThreadForUser(local_user(), ['ignored'], ['iid' => $item['id']]); - if (DBA::isResult($thread)) { - $ignore = [ - 'do' => DI::l10n()->t("ignore thread"), - 'undo' => DI::l10n()->t("unignore thread"), - 'toggle' => DI::l10n()->t("toggle ignore status"), - 'classdo' => $thread['ignored'] ? "hidden" : "", - 'classundo' => $thread['ignored'] ? "" : "hidden", - 'ignored' => DI::l10n()->t('ignored'), - ]; - } + $ignored = PostModel\ThreadUser::getIgnored($item['uri-id'], local_user()); + $ignore = [ + 'do' => DI::l10n()->t("ignore thread"), + 'undo' => DI::l10n()->t("unignore thread"), + 'toggle' => DI::l10n()->t("toggle ignore status"), + 'classdo' => $ignored ? "hidden" : "", + 'classundo' => $ignored ? "" : "hidden", + 'ignored' => DI::l10n()->t('ignored'), + ]; if ($conv->getProfileOwner() == local_user() && ($item['uid'] != 0)) { if ($origin) { diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index d0c35e4f8..6b8aba3ac 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', 1396); + define('DB_UPDATE_VERSION', 1397); } return [ @@ -750,7 +750,6 @@ return [ "guid" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "A unique identifier for this item"], "uri" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], "uri-id" => ["type" => "int unsigned", "foreign" => ["item-uri" => "id"], "comment" => "Id of the item-uri table entry that contains the item uri"], - "uri-hash" => ["type" => "varchar(80)", "not null" => "1", "default" => "", "comment" => "RIPEMD-128 hash from uri"], "parent" => ["type" => "int unsigned", "relation" => ["item" => "id"], "comment" => "item.id of the parent to this item if it is a reply of some form; otherwise this must be set to the id of this item"], "parent-uri" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "uri of the top-level parent to this item"], "parent-uri-id" => ["type" => "int unsigned", "foreign" => ["item-uri" => "id"], "comment" => "Id of the item-uri table that contains the top-level parent uri"], @@ -774,21 +773,26 @@ return [ "visible" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], "moderated" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], "deleted" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "item has been deleted"], - // User specific fields. Eventually they will move to user-item + // Part of "post-user". Will be deprecated in a later step "uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "foreign" => ["user" => "uid"], "comment" => "Owner id which owns this copy of the item"], "contact-id" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "foreign" => ["contact" => "id"], "comment" => "contact.id"], - "wall" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "This item was posted to the wall of uid"], - "origin" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "item originated at this site"], - "pubmail" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], - "starred" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "item has been favourited"], "unseen" => ["type" => "boolean", "not null" => "1", "default" => "1", "comment" => "item has not been seen"], - "mention" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "The owner of this item was mentioned in it"], - "forum_mode" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "comment" => ""], + "origin" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "item originated at this site"], "psid" => ["type" => "int unsigned", "foreign" => ["permissionset" => "id", "on delete" => "restrict"], "comment" => "ID of the permission set of this post"], + // Part of "post-thread-user". Will be deprecated in a later step + "starred" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "item has been favourited"], + "wall" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "This item was posted to the wall of uid"], + "pubmail" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], + "forum_mode" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "comment" => ""], // It has to be decided whether these fields belong to the user or the structure - "resource-id" => ["type" => "varchar(32)", "not null" => "1", "default" => "", "comment" => "Used to link other tables to items, it identifies the linked resource (e.g. photo) and if set must also set resource_type"], "event-id" => ["type" => "int unsigned", "relation" => ["event" => "id"], "comment" => "Used to link to the event.id"], + // Check deprecation status + "type" => ["type" => "varchar(20)", "comment" => ""], + "bookmark" => ["type" => "boolean", "comment" => ""], + "mention" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "The owner of this item was mentioned in it"], // Deprecated fields. Will be removed in upcoming versions + "resource-id" => ["type" => "varchar(32)", "comment" => "Deprecated"], + "uri-hash" => ["type" => "varchar(80)", "comment" => "Deprecated"], "iaid" => ["type" => "int unsigned", "comment" => "Deprecated"], "icid" => ["type" => "int unsigned", "comment" => "Deprecated"], "attach" => ["type" => "mediumtext", "comment" => "Deprecated"], @@ -798,8 +802,6 @@ return [ "deny_gid" => ["type" => "mediumtext", "comment" => "Deprecated"], "postopts" => ["type" => "text", "comment" => "Deprecated"], "inform" => ["type" => "mediumtext", "comment" => "Deprecated"], - "type" => ["type" => "varchar(20)", "comment" => "Deprecated"], - "bookmark" => ["type" => "boolean", "comment" => "Deprecated"], "file" => ["type" => "mediumtext", "comment" => "Deprecated"], "location" => ["type" => "varchar(255)", "comment" => "Deprecated"], "coord" => ["type" => "varchar(255)", "comment" => "Deprecated"], @@ -1029,20 +1031,6 @@ return [ "created" => ["created"], ] ], - "participation" => [ - "comment" => "Storage for participation messages from Diaspora", - "fields" => [ - "iid" => ["type" => "int unsigned", "not null" => "1", "primary" => "1", "foreign" => ["item" => "id"], "comment" => ""], - "server" => ["type" => "varchar(60)", "not null" => "1", "primary" => "1", "comment" => ""], - "cid" => ["type" => "int unsigned", "not null" => "1", "foreign" => ["contact" => "id"], "comment" => ""], - "fid" => ["type" => "int unsigned", "not null" => "1", "foreign" => ["fcontact" => "id"], "comment" => ""], - ], - "indexes" => [ - "PRIMARY" => ["iid", "server"], - "cid" => ["cid"], - "fid" => ["fid"] - ] - ], "pconfig" => [ "comment" => "personal (per user) configuration storage", "fields" => [ @@ -1193,6 +1181,25 @@ return [ "cid" => ["cid"] ] ], + "post-thread-user" => [ + "comment" => "Thread related data per user", + "fields" => [ + "uri-id" => ["type" => "int unsigned", "not null" => "1", "primary" => "1", "foreign" => ["item-uri" => "id"], "comment" => "Id of the item-uri table entry that contains the item uri"], + "uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "primary" => "1", "foreign" => ["user" => "uid"], "comment" => "Owner id which owns this copy of the item"], + "pinned" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "The thread is pinned on the profile page"], + "starred" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], + "ignored" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Ignore updates for this thread"], + "wall" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "This item was posted to the wall of uid"], + "pubmail" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], + "forum_mode" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "comment" => ""] + ], + "indexes" => [ + "PRIMARY" => ["uid", "uri-id"], + "uid_wall" => ["uid", "wall"], + "uid_pinned" => ["uid", "pinned"], + "uri-id" => ["uri-id"], + ] + ], "post-user" => [ "comment" => "User specific post data", "fields" => [ @@ -1215,6 +1222,18 @@ return [ "psid" => ["psid"], ], ], + "post-user-notification" => [ + "comment" => "User post notifications", + "fields" => [ + "uri-id" => ["type" => "int unsigned", "not null" => "1", "primary" => "1", "foreign" => ["item-uri" => "id"], "comment" => "Id of the item-uri table entry that contains the item uri"], + "uid" => ["type" => "mediumint unsigned", "not null" => "1", "primary" => "1", "foreign" => ["user" => "uid"], "comment" => "Owner id which owns this copy of the item"], + "notification-type" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "comment" => ""], + ], + "indexes" => [ + "PRIMARY" => ["uid", "uri-id"], + "uri-id" => ["uri-id"], + ], + ], "process" => [ "comment" => "Currently running system processes", "fields" => [ @@ -1474,22 +1493,6 @@ return [ "cid" => ["cid"], ] ], - "user-item" => [ - "comment" => "User specific item data", - "fields" => [ - "iid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "primary" => "1", "foreign" => ["item" => "id"], "comment" => "Item id"], - "uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "primary" => "1", "foreign" => ["user" => "uid"], "comment" => "User id"], - "hidden" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Marker to hide an item from the user"], - "ignored" => ["type" => "boolean", "comment" => "Ignore this thread if set"], - "pinned" => ["type" => "boolean", "comment" => "The item is pinned on the profile page"], - "notification-type" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "comment" => ""], - ], - "indexes" => [ - "PRIMARY" => ["uid", "iid"], - "uid_pinned" => ["uid", "pinned"], - "iid_uid" => ["iid", "uid"] - ] - ], "worker-ipc" => [ "comment" => "Inter process communication between the frontend and the worker", "fields" => [ diff --git a/static/dbview.config.php b/static/dbview.config.php index c7f52a5cf..088e426ba 100644 --- a/static/dbview.config.php +++ b/static/dbview.config.php @@ -395,13 +395,13 @@ "query" => "FROM `item` INNER JOIN `thread` ON `thread`.`iid` = `item`.`parent` STRAIGHT_JOIN `contact` ON `contact`.`id` = `thread`.`contact-id` - LEFT JOIN `user-item` ON `user-item`.`iid` = `item`.`id` AND `user-item`.`uid` = `thread`.`uid` + LEFT JOIN `post-user` ON `post-user`.`uri-id` = `item`.`uri-id` AND `post-user`.`uid` = `thread`.`uid` LEFT JOIN `user-contact` AS `author` ON `author`.`uid` = `thread`.`uid` AND `author`.`cid` = `thread`.`author-id` LEFT JOIN `user-contact` AS `owner` ON `owner`.`uid` = `thread`.`uid` AND `owner`.`cid` = `thread`.`owner-id` LEFT JOIN `contact` AS `ownercontact` ON `ownercontact`.`id` = `thread`.`owner-id` WHERE `thread`.`visible` AND NOT `thread`.`deleted` AND NOT `thread`.`moderated` AND (NOT `contact`.`readonly` AND NOT `contact`.`blocked` AND NOT `contact`.`pending`) - AND (`user-item`.`hidden` IS NULL OR NOT `user-item`.`hidden`) + AND (`post-user`.`hidden` IS NULL OR NOT `post-user`.`hidden`) AND (`author`.`blocked` IS NULL OR NOT `author`.`blocked`) AND (`owner`.`blocked` IS NULL OR NOT `owner`.`blocked`)" ], @@ -424,13 +424,13 @@ "query" => "FROM `thread` STRAIGHT_JOIN `contact` ON `contact`.`id` = `thread`.`contact-id` STRAIGHT_JOIN `item` ON `item`.`id` = `thread`.`iid` - LEFT JOIN `user-item` ON `user-item`.`iid` = `item`.`id` AND `user-item`.`uid` = `thread`.`uid` + LEFT JOIN `post-user` ON `post-user`.`uri-id` = `item`.`uri-id` AND `post-user`.`uid` = `thread`.`uid` LEFT JOIN `user-contact` AS `author` ON `author`.`uid` = `thread`.`uid` AND `author`.`cid` = `thread`.`author-id` LEFT JOIN `user-contact` AS `owner` ON `owner`.`uid` = `thread`.`uid` AND `owner`.`cid` = `thread`.`owner-id` LEFT JOIN `contact` AS `ownercontact` ON `ownercontact`.`id` = `thread`.`owner-id` WHERE `thread`.`visible` AND NOT `thread`.`deleted` AND NOT `thread`.`moderated` AND (NOT `contact`.`readonly` AND NOT `contact`.`blocked` AND NOT `contact`.`pending`) - AND (`user-item`.`hidden` IS NULL OR NOT `user-item`.`hidden`) + AND (`post-user`.`hidden` IS NULL OR NOT `post-user`.`hidden`) AND (`author`.`blocked` IS NULL OR NOT `author`.`blocked`) AND (`owner`.`blocked` IS NULL OR NOT `owner`.`blocked`)" ], diff --git a/update.php b/update.php index 3e9292f2e..94029065b 100644 --- a/update.php +++ b/update.php @@ -466,7 +466,7 @@ function pre_update_1364() return Update::FAILED; } - if (!DBA::e("DELETE FROM `user-item` WHERE NOT `uid` IN (SELECT `uid` FROM `user`)")) { + if (DBStructure::existsTable('user-item') && !DBA::e("DELETE FROM `user-item` WHERE NOT `uid` IN (SELECT `uid` FROM `user`)")) { return Update::FAILED; } @@ -490,10 +490,6 @@ function pre_update_1364() return Update::FAILED; } - if (!DBA::e("DELETE FROM `participation` WHERE NOT `cid` IN (SELECT `id` FROM `contact`)")) { - return Update::FAILED; - } - if (!DBA::e("DELETE FROM `profile_check` WHERE NOT `cid` IN (SELECT `id` FROM `contact`)")) { return Update::FAILED; } @@ -502,10 +498,6 @@ function pre_update_1364() return Update::FAILED; } - if (!DBA::e("DELETE FROM `participation` WHERE NOT `fid` IN (SELECT `id` FROM `fcontact`)")) { - return Update::FAILED; - } - if (!DBA::e("DELETE FROM `group_member` WHERE NOT `gid` IN (SELECT `id` FROM `group`)")) { return Update::FAILED; } @@ -514,11 +506,7 @@ function pre_update_1364() return Update::FAILED; } - if (!DBA::e("DELETE FROM `participation` WHERE NOT `iid` IN (SELECT `id` FROM `item`)")) { - return Update::FAILED; - } - - if (!DBA::e("DELETE FROM `user-item` WHERE NOT `iid` IN (SELECT `id` FROM `item`)")) { + if (DBStructure::existsTable('user-item') && !DBA::e("DELETE FROM `user-item` WHERE NOT `iid` IN (SELECT `id` FROM `item`)")) { return Update::FAILED; } @@ -686,7 +674,7 @@ function update_1395() return Update::FAILED; } - if (!DBA::e("INSERT INTO `post-user`(`uri-id`, `uid`, `hidden`, `notification-type`) + if (DBStructure::existsTable('user-item') && !DBA::e("INSERT INTO `post-user`(`uri-id`, `uid`, `hidden`, `notification-type`) SELECT `uri-id`, `user-item`.`uid`, `hidden`,`notification-type` FROM `user-item` INNER JOIN `item` ON `item`.`id` = `user-item`.`iid` ON DUPLICATE KEY UPDATE `hidden` = `user-item`.`hidden`, `notification-type` = `user-item`.`notification-type`")) { @@ -713,4 +701,37 @@ function update_1396() return Update::FAILED; } return Update::SUCCESS; -} \ No newline at end of file +} + +function update_1397() +{ + if (!DBA::e("INSERT INTO `post-user-notification`(`uri-id`, `uid`, `notification-type`) + SELECT `uri-id`, `uid`, `notification-type` FROM `post-user` WHERE `notification-type` != 0 + ON DUPLICATE KEY UPDATE `uri-id` = `post-user`.`uri-id`, `uid` = `post-user`.`uid`, `notification-type` = `post-user`.`notification-type`")) { + return Update::FAILED; + } + + if (!DBStructure::existsTable('user-item')) { + return Update::SUCCESS; + } + + if (!DBA::e("INSERT INTO `post-user-notification`(`uri-id`, `uid`, `notification-type`) + SELECT `uri-id`, `user-item`.`uid`, `notification-type` FROM `user-item` + INNER JOIN `item` ON `item`.`id` = `user-item`.`iid` WHERE `notification-type` != 0 + ON DUPLICATE KEY UPDATE `notification-type` = `user-item`.`notification-type`")) { + return Update::FAILED; + } + + if (!DBStructure::existsTable('thread')) { + return Update::SUCCESS; + } + + if (!DBA::e("INSERT IGNORE INTO `post-thread-user`(`uri-id`, `uid`, `pinned`, `starred`, `ignored`, `wall`, `pubmail`, `forum_mode`) + SELECT `thread`.`uri-id`, `thread`.`uid`, `user-item`.`pinned`, `thread`.`starred`, + `thread`.`ignored`, `thread`.`wall`, `thread`.`pubmail`, `thread`.`forum_mode` + FROM `thread` LEFT JOIN `user-item` ON `user-item`.`iid` = `thread`.`iid`")) { + return Update::FAILED; + } + + return Update::SUCCESS; +}