From e4dda7d2ca53326d83a67a86d0dbb8eb40decff0 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 3 Dec 2022 13:49:29 +0000 Subject: [PATCH] Issue 11553: Reliably return the user's contacts --- src/Model/Contact.php | 16 ++++ src/Model/Contact/Relation.php | 36 ++++++++- .../Api/Mastodon/Accounts/Followers.php | 74 +++++++++++++------ .../Api/Mastodon/Accounts/Following.php | 74 +++++++++++++------ src/Worker/ContactDiscoveryForUser.php | 36 +++++++++ 5 files changed, 189 insertions(+), 47 deletions(-) create mode 100644 src/Worker/ContactDiscoveryForUser.php diff --git a/src/Model/Contact.php b/src/Model/Contact.php index eabe378a46..34ce8e684e 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -136,6 +136,18 @@ class Contact return $contact; } + /** + * @param array $fields 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 selectAccountToArray(array $fields = [], array $condition = [], array $params = []): array + { + return DBA::selectToArray('account-user-view', $fields, $condition, $params); + } + /** * @param array $fields Array of selected fields, empty for all * @param array $condition Array of fields for condition @@ -3154,6 +3166,8 @@ class Contact return; } + Worker::add(Worker::PRIORITY_LOW, 'ContactDiscoveryForUser', $contact['uid']); + self::clearFollowerFollowingEndpointCache($contact['uid']); $cdata = self::getPublicAndUserContactID($contact['id'], $contact['uid']); @@ -3177,6 +3191,8 @@ class Contact } else { self::update(['rel' => self::FOLLOWER], ['id' => $contact['id']]); } + + Worker::add(Worker::PRIORITY_LOW, 'ContactDiscoveryForUser', $contact['uid']); } /** diff --git a/src/Model/Contact/Relation.php b/src/Model/Contact/Relation.php index 5616148fa0..3db882bb94 100644 --- a/src/Model/Contact/Relation.php +++ b/src/Model/Contact/Relation.php @@ -68,6 +68,26 @@ class Relation DBA::insert('contact-relation', ['last-interaction' => $interaction_date, 'cid' => $target, 'relation-cid' => $actor], Database::INSERT_UPDATE); } + /** + * Fetch the followers of a given user + * + * @param integer $uid User ID + * @return void + */ + public static function discoverByUser(int $uid) + { + $contact = Contact::selectFirst(['id', 'url'], ['uid' => $uid, 'self' => true]); + if (empty($contact)) { + Logger::warning('Self contact for user not found', ['uid' => $uid]); + return; + } + + $followers = self::getContacts($uid, [Contact::FOLLOWER, Contact::FRIEND]); + $followings = self::getContacts($uid, [Contact::SHARING, Contact::FRIEND]); + + self::updateFollowersFollowings($contact, $followers, $followings); + } + /** * Fetches the followers of a given profile and adds them * @@ -113,13 +133,27 @@ class Relation $followings = []; } + self::updateFollowersFollowings($contact, $followers, $followings); + } + + /** + * Update followers and followings for the given contact + * + * @param array $contact + * @param array $followers + * @param array $followings + * @return void + */ + private static function updateFollowersFollowings(array $contact, array $followers, array $followings) + { if (empty($followers) && empty($followings)) { Contact::update(['last-discovery' => DateTimeFormat::utcNow()], ['id' => $contact['id']]); - Logger::info('The contact does not offer discoverable data', ['id' => $contact['id'], 'url' => $url, 'network' => $contact['network']]); + Logger::info('The contact does not offer discoverable data', ['id' => $contact['id'], 'url' => $contact['url'], 'network' => $contact['network']]); return; } $target = $contact['id']; + $url = $contact['url']; if (!empty($followers)) { // Clear the follower list, since it will be recreated in the next step diff --git a/src/Module/Api/Mastodon/Accounts/Followers.php b/src/Module/Api/Mastodon/Accounts/Followers.php index 58d1f7d834..cd03a0176a 100644 --- a/src/Module/Api/Mastodon/Accounts/Followers.php +++ b/src/Module/Api/Mastodon/Accounts/Followers.php @@ -24,6 +24,7 @@ namespace Friendica\Module\Api\Mastodon\Accounts; use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\DI; +use Friendica\Model\Contact; use Friendica\Module\BaseApi; /** @@ -55,33 +56,60 @@ class Followers extends BaseApi 'limit' => 40, // Maximum number of results to return. Defaults to 40. ], $request); - $params = ['order' => ['relation-cid' => true], 'limit' => $request['limit']]; + if (false && $id == Contact::getPublicIdByUserId($uid)) { + $params = ['order' => ['pid' => true], 'limit' => $request['limit']]; - $condition = ['cid' => $id, 'follows' => true]; + $condition = ['uid' => $uid, 'self' => false, 'rel' => [Contact::FOLLOWER, Contact::FRIEND]]; + + if (!empty($request['max_id'])) { + $condition = DBA::mergeConditions($condition, ["`pid` < ?", $request['max_id']]); + } + + if (!empty($request['since_id'])) { + $condition = DBA::mergeConditions($condition, ["`pid` > ?", $request['since_id']]); + } + + if (!empty($request['min_id'])) { + $condition = DBA::mergeConditions($condition, ["`pid` > ?", $request['min_id']]); + + $params['order'] = ['pid']; + } + + $accounts = []; + + foreach (Contact::selectAccountToArray(['pid'], $condition, $params) as $follower) { + self::setBoundaries($follower['pid']); + $accounts[] = DI::mstdnAccount()->createFromContactId($follower['pid'], $uid); + } + } else { + $params = ['order' => ['relation-cid' => true], 'limit' => $request['limit']]; - if (!empty($request['max_id'])) { - $condition = DBA::mergeConditions($condition, ["`relation-cid` < ?", $request['max_id']]); + $condition = ['cid' => $id, 'follows' => true]; + + if (!empty($request['max_id'])) { + $condition = DBA::mergeConditions($condition, ["`relation-cid` < ?", $request['max_id']]); + } + + if (!empty($request['since_id'])) { + $condition = DBA::mergeConditions($condition, ["`relation-cid` > ?", $request['since_id']]); + } + + if (!empty($request['min_id'])) { + $condition = DBA::mergeConditions($condition, ["`relation-cid` > ?", $request['min_id']]); + + $params['order'] = ['relation-cid']; + } + + $accounts = []; + + $followers = DBA::select('contact-relation', ['relation-cid'], $condition, $params); + while ($follower = DBA::fetch($followers)) { + self::setBoundaries($follower['relation-cid']); + $accounts[] = DI::mstdnAccount()->createFromContactId($follower['relation-cid'], $uid); + } + DBA::close($followers); } - if (!empty($request['since_id'])) { - $condition = DBA::mergeConditions($condition, ["`relation-cid` > ?", $request['since_id']]); - } - - if (!empty($request['min_id'])) { - $condition = DBA::mergeConditions($condition, ["`relation-cid` > ?", $request['min_id']]); - - $params['order'] = ['cid']; - } - - $accounts = []; - - $followers = DBA::select('contact-relation', ['relation-cid'], $condition, $params); - while ($follower = DBA::fetch($followers)) { - self::setBoundaries($follower['relation-cid']); - $accounts[] = DI::mstdnAccount()->createFromContactId($follower['relation-cid'], $uid); - } - DBA::close($followers); - if (!empty($request['min_id'])) { $accounts = array_reverse($accounts); } diff --git a/src/Module/Api/Mastodon/Accounts/Following.php b/src/Module/Api/Mastodon/Accounts/Following.php index 8e05a9b717..aca6bd5a88 100644 --- a/src/Module/Api/Mastodon/Accounts/Following.php +++ b/src/Module/Api/Mastodon/Accounts/Following.php @@ -24,6 +24,7 @@ namespace Friendica\Module\Api\Mastodon\Accounts; use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\DI; +use Friendica\Model\Contact; use Friendica\Module\BaseApi; /** @@ -55,33 +56,60 @@ class Following extends BaseApi 'limit' => 40, // Maximum number of results to return. Defaults to 40. ], $request); - $params = ['order' => ['cid' => true], 'limit' => $request['limit']]; + if ($id == Contact::getPublicIdByUserId($uid)) { + $params = ['order' => ['pid' => true], 'limit' => $request['limit']]; - $condition = ['relation-cid' => $id, 'follows' => true]; + $condition = ['uid' => $uid, 'self' => false, 'rel' => [Contact::SHARING, Contact::FRIEND]]; + + if (!empty($request['max_id'])) { + $condition = DBA::mergeConditions($condition, ["`pid` < ?", $request['max_id']]); + } + + if (!empty($request['since_id'])) { + $condition = DBA::mergeConditions($condition, ["`pid` > ?", $request['since_id']]); + } + + if (!empty($request['min_id'])) { + $condition = DBA::mergeConditions($condition, ["`pid` > ?", $request['min_id']]); + + $params['order'] = ['pid']; + } + + $accounts = []; + + foreach (Contact::selectAccountToArray(['pid'], $condition, $params) as $follower) { + self::setBoundaries($follower['pid']); + $accounts[] = DI::mstdnAccount()->createFromContactId($follower['pid'], $uid); + } + } else { + $params = ['order' => ['cid' => true], 'limit' => $request['limit']]; - if (!empty($request['max_id'])) { - $condition = DBA::mergeConditions($condition, ["`cid` < ?", $request['max_id']]); + $condition = ['relation-cid' => $id, 'follows' => true]; + + if (!empty($request['max_id'])) { + $condition = DBA::mergeConditions($condition, ["`cid` < ?", $request['max_id']]); + } + + if (!empty($request['since_id'])) { + $condition = DBA::mergeConditions($condition, ["`cid` > ?", $request['since_id']]); + } + + if (!empty($request['min_id'])) { + $condition = DBA::mergeConditions($condition, ["`cid` > ?", $request['min_id']]); + + $params['order'] = ['cid']; + } + + $accounts = []; + + $followers = DBA::select('contact-relation', ['cid'], $condition, $params); + while ($follower = DBA::fetch($followers)) { + self::setBoundaries($follower['cid']); + $accounts[] = DI::mstdnAccount()->createFromContactId($follower['cid'], $uid); + } + DBA::close($followers); } - if (!empty($request['since_id'])) { - $condition = DBA::mergeConditions($condition, ["`cid` > ?", $request['since_id']]); - } - - if (!empty($request['min_id'])) { - $condition = DBA::mergeConditions($condition, ["`cid` > ?", $request['min_id']]); - - $params['order'] = ['cid']; - } - - $accounts = []; - - $followers = DBA::select('contact-relation', ['cid'], $condition, $params); - while ($follower = DBA::fetch($followers)) { - self::setBoundaries($follower['cid']); - $accounts[] = DI::mstdnAccount()->createFromContactId($follower['cid'], $uid); - } - DBA::close($followers); - if (!empty($request['min_id'])) { $accounts = array_reverse($accounts); } diff --git a/src/Worker/ContactDiscoveryForUser.php b/src/Worker/ContactDiscoveryForUser.php new file mode 100644 index 0000000000..d02f430e24 --- /dev/null +++ b/src/Worker/ContactDiscoveryForUser.php @@ -0,0 +1,36 @@ +. + * + */ + +namespace Friendica\Worker; + +use Friendica\Model\Contact; + +class ContactDiscoveryForUser +{ + /** + * Discover contact relations + * @param string $url + */ + public static function execute(int $uid) + { + Contact\Relation::discoverByUser($uid); + } +}