X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FModel%2FContact%2FRelation.php;h=d68555eb3e854f2df8e5e6423dd47439e47176a1;hb=6dbbd081795fa1c8fe57db2248ac162efeeada88;hp=a98f2bfd9a2af35bbcb4ba7cbf63dd8528e83996;hpb=fc8acf13c835bd85ec44e427dec4f50beb3eb723;p=friendica.git diff --git a/src/Model/Contact/Relation.php b/src/Model/Contact/Relation.php index a98f2bfd9a..d68555eb3e 100644 --- a/src/Model/Contact/Relation.php +++ b/src/Model/Contact/Relation.php @@ -1,6 +1,6 @@ $interaction_date], ['cid' => $target, 'relation-cid' => $actor], true); + DBA::insert('contact-relation', ['last-interaction' => $interaction_date, 'cid' => $target, 'relation-cid' => $actor], Database::INSERT_UPDATE); } /** @@ -73,29 +78,43 @@ class Relation { $contact = Contact::getByURL($url); if (empty($contact)) { + Logger::info('Contact not found', ['url' => $url]); return; } if (!self::isDiscoverable($url, $contact)) { + Logger::info('Contact is not discoverable', ['url' => $url]); return; } - $apcontact = APContact::getByURL($url, false); + $uid = User::getIdForURL($url); + if (!empty($uid)) { + Logger::info('Fetch the followers/followings locally', ['url' => $url]); + $followers = self::getContacts($uid, [Contact::FOLLOWER, Contact::FRIEND]); + $followings = self::getContacts($uid, [Contact::SHARING, Contact::FRIEND]); + } elseif (!Contact::isLocal($url)) { + Logger::info('Fetch the followers/followings by polling the endpoints', ['url' => $url]); + $apcontact = APContact::getByURL($url, false); - if (!empty($apcontact['followers']) && is_string($apcontact['followers'])) { - $followers = ActivityPub::fetchItems($apcontact['followers']); - } else { - $followers = []; - } + if (!empty($apcontact['followers']) && is_string($apcontact['followers'])) { + $followers = ActivityPub::fetchItems($apcontact['followers']); + } else { + $followers = []; + } - if (!empty($apcontact['following']) && is_string($apcontact['following'])) { - $followings = ActivityPub::fetchItems($apcontact['following']); + if (!empty($apcontact['following']) && is_string($apcontact['following'])) { + $followings = ActivityPub::fetchItems($apcontact['following']); + } else { + $followings = []; + } } else { + Logger::notice('Contact seems to be local but could not be found here', ['url' => $url]); + $followers = []; $followings = []; } if (empty($followers) && empty($followings)) { - DBA::update('contact', ['last-discovery' => DateTimeFormat::utcNow()], ['id' => $contact['id']]); + 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']]); return; } @@ -125,14 +144,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++; } } @@ -143,11 +162,38 @@ class Relation DBA::delete('contact-relation', ['cid' => $target, 'follows' => false, 'last-interaction' => DBA::NULL_DATETIME]); } - DBA::update('contact', ['last-discovery' => DateTimeFormat::utcNow()], ['id' => $target]); + Contact::update(['last-discovery' => DateTimeFormat::utcNow()], ['id' => $target]); Logger::info('Contacts discovery finished', ['id' => $target, 'url' => $url, 'follower' => $follower_counter, 'following' => $following_counter]); return; } + /** + * Fetch contact url list from the given local user + * + * @param integer $uid + * @param array $rel + * @return array contact list + */ + private static function getContacts(int $uid, array $rel) + { + $list = []; + $profile = Profile::getByUID($uid); + if (!empty($profile['hide-friends'])) { + return $list; + } + + $condition = ['rel' => $rel, 'uid' => $uid, 'self' => false, 'deleted' => false, + 'hidden' => false, 'archive' => false, 'pending' => false]; + $condition = DBA::mergeConditions($condition, ["`url` IN (SELECT `url` FROM `apcontact`)"]); + $contacts = DBA::select('contact', ['url'], $condition); + while ($contact = DBA::fetch($contacts)) { + $list[] = $contact['url']; + } + DBA::close($contacts); + + return $list; + } + /** * Tests if a given contact url is discoverable * @@ -164,7 +210,7 @@ class Relation } if (empty($contact)) { - $contact = Contact::getByURL($url); + $contact = Contact::getByURL($url, false); } if (empty($contact)) { @@ -207,6 +253,110 @@ class Relation return true; } + /** + * @param int $uid user + * @param int $start optional, default 0 + * @param int $limit optional, default 80 + * @return array + */ + static public function getSuggestions(int $uid, int $start = 0, int $limit = 80) + { + $cid = Contact::getPublicIdByUserId($uid); + $totallimit = $start + $limit; + $contacts = []; + + Logger::info('Collecting suggestions', ['uid' => $uid, 'cid' => $cid, 'start' => $start, 'limit' => $limit]); + + $diaspora = DI::config()->get('system', 'diaspora_enabled') ? Protocol::DIASPORA : Protocol::ACTIVITYPUB; + $ostatus = !DI::config()->get('system', 'ostatus_disabled') ? Protocol::OSTATUS : Protocol::ACTIVITYPUB; + + // The query returns contacts where contacts interacted with whom the given user follows. + // Contacts who already are in the user's contact table are ignored. + $results = DBA::select('contact', [], + ["`id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` IN + (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ?) + AND NOT `cid` IN (SELECT `id` FROM `contact` WHERE `uid` = ? AND `nurl` IN + (SELECT `nurl` FROM `contact` WHERE `uid` = ? AND `rel` IN (?, ?)))) + AND NOT `hidden` AND `network` IN (?, ?, ?, ?)", + $cid, 0, $uid, Contact::FRIEND, Contact::SHARING, + Protocol::ACTIVITYPUB, Protocol::DFRN, $diaspora, $ostatus], + ['order' => ['last-item' => true], 'limit' => $totallimit] + ); + + while ($contact = DBA::fetch($results)) { + $contacts[$contact['id']] = $contact; + } + DBA::close($results); + + Logger::info('Contacts of contacts who are followed by the given user', ['uid' => $uid, 'cid' => $cid, 'count' => count($contacts)]); + + if (count($contacts) >= $totallimit) { + return array_slice($contacts, $start, $limit); + } + + // The query returns contacts where contacts interacted with whom also interacted with the given user. + // Contacts who already are in the user's contact table are ignored. + $results = DBA::select('contact', [], + ["`id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` IN + (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ?) + AND NOT `cid` IN (SELECT `id` FROM `contact` WHERE `uid` = ? AND `nurl` IN + (SELECT `nurl` FROM `contact` WHERE `uid` = ? AND `rel` IN (?, ?)))) + AND NOT `hidden` AND `network` IN (?, ?, ?, ?)", + $cid, 0, $uid, Contact::FRIEND, Contact::SHARING, + Protocol::ACTIVITYPUB, Protocol::DFRN, $diaspora, $ostatus], + ['order' => ['last-item' => true], 'limit' => $totallimit] + ); + + while ($contact = DBA::fetch($results)) { + $contacts[$contact['id']] = $contact; + } + DBA::close($results); + + Logger::info('Contacts of contacts who are following the given user', ['uid' => $uid, 'cid' => $cid, 'count' => count($contacts)]); + + if (count($contacts) >= $totallimit) { + return array_slice($contacts, $start, $limit); + } + + // The query returns contacts that follow the given user but aren't followed by that user. + $results = DBA::select('contact', [], + ["`nurl` IN (SELECT `nurl` FROM `contact` WHERE `uid` = ? AND `rel` = ?) + AND NOT `hidden` AND `uid` = ? AND `network` IN (?, ?, ?, ?)", + $uid, Contact::FOLLOWER, 0, + Protocol::ACTIVITYPUB, Protocol::DFRN, $diaspora, $ostatus], + ['order' => ['last-item' => true], 'limit' => $totallimit] + ); + + while ($contact = DBA::fetch($results)) { + $contacts[$contact['id']] = $contact; + } + DBA::close($results); + + Logger::info('Followers that are not followed by the given user', ['uid' => $uid, 'cid' => $cid, 'count' => count($contacts)]); + + if (count($contacts) >= $totallimit) { + return array_slice($contacts, $start, $limit); + } + + // The query returns any contact that isn't followed by that user. + $results = DBA::select('contact', [], + ["NOT `nurl` IN (SELECT `nurl` FROM `contact` WHERE `uid` = ? AND `rel` IN (?, ?)) + AND NOT `hidden` AND `uid` = ? AND `network` IN (?, ?, ?, ?)", + $uid, Contact::FRIEND, Contact::SHARING, 0, + Protocol::ACTIVITYPUB, Protocol::DFRN, $diaspora, $ostatus], + ['order' => ['last-item' => true], 'limit' => $totallimit] + ); + + while ($contact = DBA::fetch($results)) { + $contacts[$contact['id']] = $contact; + } + DBA::close($results); + + Logger::info('Any contact', ['uid' => $uid, 'cid' => $cid, 'count' => count($contacts)]); + + return array_slice($contacts, $start, $limit); + } + /** * Counts all the known follows of the provided public contact * @@ -218,12 +368,8 @@ class Relation public static function countFollows(int $cid, array $condition = []) { $condition = DBA::mergeConditions($condition, - ['`id` IN ( - SELECT `relation-cid` - FROM `contact-relation` - WHERE `cid` = ? - AND `follows` -)', $cid] + ['`id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`)', + $cid] ); return DI::dba()->count('contact', $condition); @@ -243,24 +389,13 @@ class Relation public static function listFollows(int $cid, array $condition = [], int $count = 30, int $offset = 0, bool $shuffle = false) { $condition = DBA::mergeConditions($condition, - ['`id` IN ( - SELECT `relation-cid` - FROM `contact-relation` - WHERE `cid` = ? - AND `follows` -)', $cid] + ['`id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`)', + $cid] ); - $follows = DI::dba()->selectToArray( - 'contact', - $condition, - [ - 'limit' => [$offset, $count], - 'order' => [$shuffle ? 'RAND()' : 'name'] - ] + return DI::dba()->selectToArray('contact', [], $condition, + ['limit' => [$offset, $count], 'order' => [$shuffle ? 'RAND()' : 'name']] ); - - return $follows; } /** @@ -274,12 +409,8 @@ class Relation public static function countFollowers(int $cid, array $condition = []) { $condition = DBA::mergeConditions($condition, - ['`id` IN ( - SELECT `cid` - FROM `contact-relation` - WHERE `relation-cid` = ? - AND `follows` -)', $cid] + ['`id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`)', + $cid] ); return DI::dba()->count('contact', $condition); @@ -299,24 +430,99 @@ class Relation public static function listFollowers(int $cid, array $condition = [], int $count = 30, int $offset = 0, bool $shuffle = false) { $condition = DBA::mergeConditions($condition, - ['`id` IN ( - SELECT `cid` - FROM `contact-relation` - WHERE `relation-cid` = ? - AND `follows` -)', $cid] + ['`id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`)', $cid] + ); + + return DI::dba()->selectToArray('contact', [], $condition, + ['limit' => [$offset, $count], 'order' => [$shuffle ? 'RAND()' : 'name']] + ); + } + + /** + * Counts the number of contacts that are known mutuals with the provided public contact. + * + * @param int $cid Public contact id + * @param array $condition Additional condition array on the contact table + * @return int + * @throws Exception + */ + public static function countMutuals(int $cid, array $condition = []) + { + $condition = DBA::mergeConditions($condition, + ['`id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`) + AND `id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`)', + $cid, $cid] + ); + + return DI::dba()->count('contact', $condition); + } + + /** + * Returns a paginated list of contacts that are known mutuals with the provided public contact. + * + * @param int $cid Public contact id + * @param array $condition Additional condition on the contact table + * @param int $count + * @param int $offset + * @param bool $shuffle + * @return array + * @throws Exception + */ + public static function listMutuals(int $cid, array $condition = [], int $count = 30, int $offset = 0, bool $shuffle = false) + { + $condition = DBA::mergeConditions($condition, + ['`id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`) + AND `id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`)', + $cid, $cid] + ); + + return DI::dba()->selectToArray('contact', [], $condition, + ['limit' => [$offset, $count], 'order' => [$shuffle ? 'RAND()' : 'name']] + ); + } + + + /** + * Counts the number of contacts with any relationship with the provided public contact. + * + * @param int $cid Public contact id + * @param array $condition Additional condition array on the contact table + * @return int + * @throws Exception + */ + public static function countAll(int $cid, array $condition = []) + { + $condition = DBA::mergeConditions($condition, + ['(`id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`) + OR `id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`))', + $cid, $cid] ); - $followers = DI::dba()->selectToArray( - 'contact', - $condition, - [ - 'limit' => [$offset, $count], - 'order' => [$shuffle ? 'RAND()' : 'name'] - ] + return DI::dba()->count('contact', $condition); + } + + /** + * Returns a paginated list of contacts with any relationship with the provided public contact. + * + * @param int $cid Public contact id + * @param array $condition Additional condition on the contact table + * @param int $count + * @param int $offset + * @param bool $shuffle + * @return array + * @throws Exception + */ + public static function listAll(int $cid, array $condition = [], int $count = 30, int $offset = 0, bool $shuffle = false) + { + $condition = DBA::mergeConditions($condition, + ['(`id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`) + OR `id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`))', + $cid, $cid] ); - return $followers; + return DI::dba()->selectToArray('contact', [], $condition, + ['limit' => [$offset, $count], 'order' => [$shuffle ? 'RAND()' : 'name']] + ); } /** @@ -332,21 +538,12 @@ class Relation public static function countCommon(int $sourceId, int $targetId, array $condition = []) { $condition = DBA::mergeConditions($condition, - ['`id` IN ( - SELECT `relation-cid` - FROM `contact-relation` - WHERE `cid` = ? -) - AND `id` IN ( - SELECT `relation-cid` - FROM `contact-relation` - WHERE `cid` = ? -)', $sourceId, $targetId] + ['`id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ?) + AND `id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ?)', + $sourceId, $targetId] ); - $total = DI::dba()->count('contact', $condition); - - return $total; + return DI::dba()->count('contact', $condition); } /** @@ -365,33 +562,16 @@ class Relation public static function listCommon(int $sourceId, int $targetId, array $condition = [], int $count = 30, int $offset = 0, bool $shuffle = false) { $condition = DBA::mergeConditions($condition, - ["`id` IN ( - SELECT `relation-cid` - FROM `contact-relation` - WHERE `cid` = ? - AND `follows` -) - AND `id` IN ( - SELECT `relation-cid` - FROM `contact-relation` - WHERE `cid` = ? - AND `follows` -)", $sourceId, $targetId] + ["`id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ?) + AND `id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ?)", + $sourceId, $targetId] ); - $contacts = DI::dba()->selectToArray( - 'contact', - $condition, - [ - 'limit' => [$offset, $count], - 'order' => [$shuffle ? 'name' : 'RAND()'], - ] + return DI::dba()->selectToArray('contact', [], $condition, + ['limit' => [$offset, $count], 'order' => [$shuffle ? 'RAND()' : 'name']] ); - - return $contacts; } - /** * Counts the number of contacts that are followed by both provided public contacts. * @@ -404,23 +584,12 @@ class Relation public static function countCommonFollows(int $sourceId, int $targetId, array $condition = []) { $condition = DBA::mergeConditions($condition, - ['`id` IN ( - SELECT `relation-cid` - FROM `contact-relation` - WHERE `cid` = ? - AND `follows` -) - AND `id` IN ( - SELECT `relation-cid` - FROM `contact-relation` - WHERE `cid` = ? - AND `follows` -)', $sourceId, $targetId] + ['`id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`) + AND `id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`)', + $sourceId, $targetId] ); - $total = DI::dba()->count('contact', $condition); - - return $total; + return DI::dba()->count('contact', $condition); } /** @@ -438,30 +607,14 @@ class Relation public static function listCommonFollows(int $sourceId, int $targetId, array $condition = [], int $count = 30, int $offset = 0, bool $shuffle = false) { $condition = DBA::mergeConditions($condition, - ["`id` IN ( - SELECT `relation-cid` - FROM `contact-relation` - WHERE `cid` = ? - AND `follows` -) - AND `id` IN ( - SELECT `relation-cid` - FROM `contact-relation` - WHERE `cid` = ? - AND `follows` -)", $sourceId, $targetId] + ["`id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`) + AND `id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`)", + $sourceId, $targetId] ); - $contacts = DI::dba()->selectToArray( - 'contact', - $condition, - [ - 'limit' => [$offset, $count], - 'order' => [$shuffle ? 'name' : 'RAND()'], - ] + return DI::dba()->selectToArray('contact', [], $condition, + ['limit' => [$offset, $count], 'order' => [$shuffle ? 'RAND()' : 'name']] ); - - return $contacts; } /** @@ -476,23 +629,12 @@ class Relation public static function countCommonFollowers(int $sourceId, int $targetId, array $condition = []) { $condition = DBA::mergeConditions($condition, - ['`id` IN ( - SELECT `cid` - FROM `contact-relation` - WHERE `relation-cid` = ? - AND `follows` -) - AND `id` IN ( - SELECT `cid` - FROM `contact-relation` - WHERE `relation-cid` = ? - AND `follows` -)', $sourceId, $targetId] + ["`id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`) + AND `id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`)", + $sourceId, $targetId] ); - $total = DI::dba()->count('contact', $condition); - - return $total; + return DI::dba()->count('contact', $condition); } /** @@ -510,29 +652,13 @@ class Relation public static function listCommonFollowers(int $sourceId, int $targetId, array $condition = [], int $count = 30, int $offset = 0, bool $shuffle = false) { $condition = DBA::mergeConditions($condition, - ["`id` IN ( - SELECT `cid` - FROM `contact-relation` - WHERE `relation-cid` = ? - AND `follows` -) - AND `id` IN ( - SELECT `cid` - FROM `contact-relation` - WHERE `relation-cid` = ? - AND `follows` -)", $sourceId, $targetId] + ["`id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`) + AND `id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`)", + $sourceId, $targetId] ); - $contacts = DI::dba()->selectToArray( - 'contact', - $condition, - [ - 'limit' => [$offset, $count], - 'order' => [$shuffle ? 'name' : 'RAND()'], - ] + return DI::dba()->selectToArray('contact', [], $condition, + ['limit' => [$offset, $count], 'order' => [$shuffle ? 'RAND()' : 'name']] ); - - return $contacts; } }