diff --git a/database.sql b/database.sql index 2eec6fb809..329381b95b 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2023.09-dev (Giant Rhubarb) --- DB_UPDATE_VERSION 1533 +-- DB_UPDATE_VERSION 1534 -- ------------------------------------------ @@ -1310,6 +1310,7 @@ CREATE TABLE IF NOT EXISTS `post-engagement` ( `media-type` tinyint NOT NULL DEFAULT 0 COMMENT 'Type of media in a bit array (1 = image, 2 = video, 4 = audio', `language` varbinary(128) COMMENT 'Language information about this post', `created` datetime COMMENT '', + `restricted` boolean NOT NULL DEFAULT '0' COMMENT 'If true, this post is either unlisted or not from a federated network', `comments` mediumint unsigned COMMENT 'Number of comments', `activities` mediumint unsigned COMMENT 'Number of activities (like, dislike, ...)', PRIMARY KEY(`uri-id`), diff --git a/doc/database/db_post-engagement.md b/doc/database/db_post-engagement.md index 3409680e28..19cb64d54f 100644 --- a/doc/database/db_post-engagement.md +++ b/doc/database/db_post-engagement.md @@ -6,16 +6,17 @@ Engagement data per post Fields ------ -| Field | Description | Type | Null | Key | Default | Extra | -| ------------ | ------------------------------------------------------------- | ------------------ | ---- | --- | ------- | ----- | -| uri-id | Id of the item-uri table entry that contains the item uri | int unsigned | NO | PRI | NULL | | -| owner-id | Item owner | int unsigned | NO | | 0 | | -| contact-type | Person, organisation, news, community, relay | tinyint | NO | | 0 | | -| media-type | Type of media in a bit array (1 = image, 2 = video, 4 = audio | tinyint | NO | | 0 | | -| language | Language information about this post | varbinary(128) | YES | | NULL | | -| created | | datetime | YES | | NULL | | -| comments | Number of comments | mediumint unsigned | YES | | NULL | | -| activities | Number of activities (like, dislike, ...) | mediumint unsigned | YES | | NULL | | +| Field | Description | Type | Null | Key | Default | Extra | +| ------------ | --------------------------------------------------------------------- | ------------------ | ---- | --- | ------- | ----- | +| uri-id | Id of the item-uri table entry that contains the item uri | int unsigned | NO | PRI | NULL | | +| owner-id | Item owner | int unsigned | NO | | 0 | | +| contact-type | Person, organisation, news, community, relay | tinyint | NO | | 0 | | +| media-type | Type of media in a bit array (1 = image, 2 = video, 4 = audio | tinyint | NO | | 0 | | +| language | Language information about this post | varbinary(128) | YES | | NULL | | +| created | | datetime | YES | | NULL | | +| restricted | If true, this post is either unlisted or not from a federated network | boolean | NO | | 0 | | +| comments | Number of comments | mediumint unsigned | YES | | NULL | | +| activities | Number of activities (like, dislike, ...) | mediumint unsigned | YES | | NULL | | Indexes ------------ diff --git a/src/Model/Item.php b/src/Model/Item.php index e48bae77a6..8972d3b75e 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -1250,7 +1250,7 @@ class Item } } - Post::insert($item['uri-id'], $item); + $inserted = Post::insert($item['uri-id'], $item); if ($item['gravity'] == self::GRAVITY_PARENT) { Post\Thread::insert($item['uri-id'], $item); @@ -1405,7 +1405,9 @@ class Item self::updateDisplayCache($posted_item['uri-id']); } - Post\Engagement::storeFromItem($posted_item); + if ($inserted) { + Post\Engagement::storeFromItem($posted_item); + } return $post_user_id; } diff --git a/src/Model/Post.php b/src/Model/Post.php index 6f855867f6..fe48450221 100644 --- a/src/Model/Post.php +++ b/src/Model/Post.php @@ -36,10 +36,10 @@ class Post * * @param integer $uri_id * @param array $fields - * @return int ID of inserted post + * @return bool Success of the insert process * @throws \Exception */ - public static function insert(int $uri_id, array $data = []): int + public static function insert(int $uri_id, array $data = []): bool { if (empty($uri_id)) { throw new BadMethodCallException('Empty URI_id'); @@ -50,11 +50,7 @@ class Post // Additionally assign the key fields $fields['uri-id'] = $uri_id; - if (!DBA::insert('post', $fields, Database::INSERT_IGNORE)) { - return 0; - } - - return DBA::lastInsertId(); + return DBA::insert('post', $fields, Database::INSERT_IGNORE); } /** diff --git a/src/Model/Post/Engagement.php b/src/Model/Post/Engagement.php index 0e284bd41d..fad2b1080a 100644 --- a/src/Model/Post/Engagement.php +++ b/src/Model/Post/Engagement.php @@ -29,8 +29,10 @@ use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Item; use Friendica\Model\Post; +use Friendica\Model\Tag; use Friendica\Model\Verb; use Friendica\Protocol\Activity; +use Friendica\Protocol\Relay; use Friendica\Util\DateTimeFormat; // Channel @@ -45,26 +47,12 @@ class Engagement */ public static function storeFromItem(array $item) { - if (!in_array($item['network'], Protocol::FEDERATED)) { - Logger::debug('No federated network', ['uri-id' => $item['uri-id'], 'parent-uri-id' => $item['parent-uri-id'], 'network' => $item['network']]); - return; - } - - if (($item['uid'] != 0) && ($item['gravity'] == Item::GRAVITY_COMMENT)) { - Logger::debug('Non public comments are not stored', ['uri-id' => $item['uri-id'], 'parent-uri-id' => $item['parent-uri-id'], 'uid' => $item['uid']]); - return; - } - if (in_array($item['verb'], [Activity::FOLLOW, Activity::VIEW, Activity::READ])) { Logger::debug('Technical activities are not stored', ['uri-id' => $item['uri-id'], 'parent-uri-id' => $item['parent-uri-id'], 'verb' => $item['verb']]); return; } $parent = Post::selectFirst(['created', 'owner-id', 'uid', 'private', 'contact-contact-type', 'language'], ['uri-id' => $item['parent-uri-id']]); - if ($parent['private'] != Item::PUBLIC) { - Logger::debug('Non public posts are not stored', ['uri-id' => $item['uri-id'], 'parent-uri-id' => $item['parent-uri-id'], 'uid' => $parent['uid'], 'private' => $parent['private']]); - return; - } if ($parent['created'] < DateTimeFormat::utc('now - ' . DI::config()->get('channel', 'engagement_hours') . ' hour')) { Logger::debug('Post is too old', ['uri-id' => $item['uri-id'], 'parent-uri-id' => $item['parent-uri-id'], 'created' => $parent['created']]); @@ -77,6 +65,15 @@ class Engagement $store = Contact::hasFollowers($parent['owner-id']); } + if (!$store) { + $tagList = Relay::getSubscribedTags(); + foreach (array_column(Tag::getByURIId($item['parent-uri-id'], [Tag::HASHTAG]), 'name') as $tag) { + if (in_array($tag, $tagList)) { + $store = true; + } + } + } + $mediatype = self::getMediaType($item['parent-uri-id']); if (!$store) { @@ -90,6 +87,7 @@ class Engagement 'media-type' => $mediatype, 'language' => $parent['language'], 'created' => $parent['created'], + 'restricted' => !in_array($item['network'], Protocol::FEDERATED) || ($parent['private'] != Item::PUBLIC), 'comments' => DBA::count('post', ['parent-uri-id' => $item['parent-uri-id'], 'gravity' => Item::GRAVITY_COMMENT]), 'activities' => DBA::count('post', [ "`parent-uri-id` = ? AND `gravity` = ? AND NOT `vid` IN (?, ?, ?)", @@ -98,7 +96,7 @@ class Engagement ]) ]; if (!$store && ($engagement['comments'] == 0) && ($engagement['activities'] == 0)) { - Logger::debug('No media, follower, comments or activities. Engagement not stored', ['fields' => $engagement]); + Logger::debug('No media, follower, subscribed tags, comments or activities. Engagement not stored', ['fields' => $engagement]); return; } $ret = DBA::insert('post-engagement', $engagement, Database::INSERT_UPDATE); diff --git a/src/Module/Contact/Profile.php b/src/Module/Contact/Profile.php index d9faed7d97..6584d8fe6d 100644 --- a/src/Module/Contact/Profile.php +++ b/src/Module/Contact/Profile.php @@ -340,13 +340,8 @@ class Profile extends BaseModule ]; } - if (in_array($contact['network'], Protocol::FEDERATED)) { - $channel_settings_label = $this->t('Channel Settings'); - $channel_frequency = Contact\User::getChannelFrequency($contact['id'], $this->session->getLocalUserId()); - } else { - $channel_settings_label = null; - $channel_frequency = null; - } + $channel_settings_label = $this->t('Channel Settings'); + $channel_frequency = Contact\User::getChannelFrequency($contact['id'], $this->session->getLocalUserId()); $poll_interval = null; if ((($contact['network'] == Protocol::FEED) && !$this->config->get('system', 'adjust_poll_frequency')) || ($contact['network'] == Protocol::MAIL)) { diff --git a/src/Module/Conversation/Timeline.php b/src/Module/Conversation/Timeline.php index d685013ba4..1a4d98e61c 100644 --- a/src/Module/Conversation/Timeline.php +++ b/src/Module/Conversation/Timeline.php @@ -306,6 +306,8 @@ class Timeline extends BaseModule $condition = $this->addLanguageCondition($uid, $condition); } + $condition = DBA::mergeConditions($condition, ["(NOT `restricted` OR EXISTS(SELECT `id` FROM `post-user` WHERE `uid` = ? AND `uri-id` = `post-engagement`.`uri-id`))", $uid]); + $condition = DBA::mergeConditions($condition, ["NOT EXISTS(SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND `cid` = `post-engagement`.`owner-id` AND (`ignored` OR `blocked` OR `collapsed` OR `is-blocked` OR `channel-frequency` = ?))", $uid, Contact\User::FREQUENCY_NEVER]); if (($this->selectedTab != TimelineEntity::WHATSHOT) && !is_null($this->accountType)) { @@ -381,7 +383,7 @@ class Timeline extends BaseModule return $comments; } - $condition = ["`contact-type` != ? AND `comments` > ?", Contact::TYPE_COMMUNITY, 0]; + $condition = ["`contact-type` != ? AND `comments` > ? AND NOT `restricted`", Contact::TYPE_COMMUNITY, 0]; $condition = $this->addLanguageCondition($uid, $condition); $limit = $this->database->count('post-engagement', $condition) / $divider; @@ -405,7 +407,7 @@ class Timeline extends BaseModule return $activities; } - $condition = ["`contact-type` != ? AND `activities` > ?", Contact::TYPE_COMMUNITY, 0]; + $condition = ["`contact-type` != ? AND `activities` > ? AND NOT `restricted`", Contact::TYPE_COMMUNITY, 0]; $condition = $this->addLanguageCondition($uid, $condition); $limit = $this->database->count('post-engagement', $condition) / $divider; diff --git a/src/Protocol/Relay.php b/src/Protocol/Relay.php index 2002aa9bb8..57bd712557 100644 --- a/src/Protocol/Relay.php +++ b/src/Protocol/Relay.php @@ -86,24 +86,14 @@ class Relay $body = ActivityPub\Processor::normalizeMentionLinks($body); - $systemTags = []; - $userTags = []; $denyTags = []; if ($scope == self::SCOPE_TAGS) { - $server_tags = $config->get('system', 'relay_server_tags'); - $tagitems = explode(',', mb_strtolower($server_tags)); - foreach ($tagitems as $tag) { - $systemTags[] = trim($tag, '# '); - } - - if ($config->get('system', 'relay_user_tags')) { - $userTags = Search::getUserTags(); - } + $tagList = self::getSubscribedTags(); + } else { + $tagList = []; } - $tagList = array_unique(array_merge($systemTags, $userTags)); - $deny_tags = $config->get('system', 'relay_deny_tags'); $tagitems = explode(',', mb_strtolower($deny_tags)); foreach ($tagitems as $tag) { @@ -149,6 +139,29 @@ class Relay return false; } + /** + * Get a list of subscribed tags by both the users and the tags that are defined by the admin + * + * @return array + */ + public static function getSubscribedTags(): array + { + $systemTags = []; + $server_tags = DI::config()->get('system', 'relay_server_tags'); + + foreach (explode(',', mb_strtolower($server_tags)) as $tag) { + $systemTags[] = trim($tag, '# '); + } + + if (DI::config()->get('system', 'relay_user_tags')) { + $userTags = Search::getUserTags(); + } else { + $userTags = []; + } + + return array_unique(array_merge($systemTags, $userTags)); + } + /** * Detect the language of a post and decide if the post should be accepted * diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 89e234e7e9..606173b817 100644 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -56,7 +56,7 @@ use Friendica\Database\DBA; // This file is required several times during the test in DbaDefinition which justifies this condition if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1533); + define('DB_UPDATE_VERSION', 1534); } return [ @@ -1333,6 +1333,7 @@ return [ "media-type" => ["type" => "tinyint", "not null" => "1", "default" => "0", "comment" => "Type of media in a bit array (1 = image, 2 = video, 4 = audio"], "language" => ["type" => "varbinary(128)", "comment" => "Language information about this post"], "created" => ["type" => "datetime", "comment" => ""], + "restricted" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "If true, this post is either unlisted or not from a federated network"], "comments" => ["type" => "mediumint unsigned", "comment" => "Number of comments"], "activities" => ["type" => "mediumint unsigned", "comment" => "Number of activities (like, dislike, ...)"], ],