From ce69026964ffe2eb514223c3458ac27cf6d049b9 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 15 Sep 2021 00:36:01 -0400 Subject: [PATCH] [Database version 1499] Create new paradigm classes for Diaspora Contact - Switch table fcontact for dcontact in views --- database.sql | 32 +- src/Console/Relocate.php | 6 +- src/DI.php | 5 + src/Database/Database.php | 18 +- src/Model/Contact.php | 4 +- src/Network/Probe.php | 39 ++- src/Protocol/DFRN.php | 7 +- src/Protocol/Diaspora.php | 231 +++++++------- .../Diaspora/Entity/DiasporaContact.php | 140 +++++++++ .../Diaspora/Factory/DiasporaContact.php | 102 +++++++ .../Diaspora/Repository/DiasporaContact.php | 283 ++++++++++++++++++ src/Worker/Delivery.php | 5 +- src/Worker/ExpirePosts.php | 2 +- static/dbview.config.php | 30 +- 14 files changed, 734 insertions(+), 170 deletions(-) create mode 100644 src/Protocol/Diaspora/Entity/DiasporaContact.php create mode 100644 src/Protocol/Diaspora/Factory/DiasporaContact.php create mode 100644 src/Protocol/Diaspora/Repository/DiasporaContact.php diff --git a/database.sql b/database.sql index 358318134..ae2f8096b 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2022.12-dev (Giant Rhubarb) --- DB_UPDATE_VERSION 1498 +-- DB_UPDATE_VERSION 1499 -- ------------------------------------------ @@ -2836,11 +2836,11 @@ CREATE VIEW `account-view` AS SELECT `contact`.`blocked` AS `blocked`, `contact`.`notify` AS `dfrn-notify`, `contact`.`poll` AS `dfrn-poll`, - `fcontact`.`guid` AS `diaspora-guid`, - `fcontact`.`batch` AS `diaspora-batch`, - `fcontact`.`notify` AS `diaspora-notify`, - `fcontact`.`poll` AS `diaspora-poll`, - `fcontact`.`alias` AS `diaspora-alias`, + `item-uri`.`guid` AS `diaspora-guid`, + `diaspora-contact`.`batch` AS `diaspora-batch`, + `diaspora-contact`.`notify` AS `diaspora-notify`, + `diaspora-contact`.`poll` AS `diaspora-poll`, + `diaspora-contact`.`alias` AS `diaspora-alias`, `apcontact`.`uuid` AS `ap-uuid`, `apcontact`.`type` AS `ap-type`, `apcontact`.`following` AS `ap-following`, @@ -2858,7 +2858,7 @@ CREATE VIEW `account-view` AS SELECT FROM `contact` LEFT JOIN `item-uri` ON `item-uri`.`id` = `contact`.`uri-id` LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `contact`.`uri-id` - LEFT JOIN `fcontact` ON `fcontact`.`uri-id` = contact.`uri-id` + LEFT JOIN `diaspora-contact` ON `diaspora-contact`.`uri-id` = contact.`uri-id` LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid` WHERE `contact`.`uid` = 0; @@ -2937,14 +2937,14 @@ CREATE VIEW `account-user-view` AS SELECT `ucontact`.`reason` AS `reason`, `contact`.`notify` AS `dfrn-notify`, `contact`.`poll` AS `dfrn-poll`, - `fcontact`.`guid` AS `diaspora-guid`, - `fcontact`.`batch` AS `diaspora-batch`, - `fcontact`.`notify` AS `diaspora-notify`, - `fcontact`.`poll` AS `diaspora-poll`, - `fcontact`.`alias` AS `diaspora-alias`, - `fcontact`.`interacting_count` AS `diaspora-interacting_count`, - `fcontact`.`interacted_count` AS `diaspora-interacted_count`, - `fcontact`.`post_count` AS `diaspora-post_count`, + `item-uri`.`guid` AS `diaspora-guid`, + `diaspora-contact`.`batch` AS `diaspora-batch`, + `diaspora-contact`.`notify` AS `diaspora-notify`, + `diaspora-contact`.`poll` AS `diaspora-poll`, + `diaspora-contact`.`alias` AS `diaspora-alias`, + `diaspora-contact`.`interacting_count` AS `diaspora-interacting_count`, + `diaspora-contact`.`interacted_count` AS `diaspora-interacted_count`, + `diaspora-contact`.`post_count` AS `diaspora-post_count`, `apcontact`.`uuid` AS `ap-uuid`, `apcontact`.`type` AS `ap-type`, `apcontact`.`following` AS `ap-following`, @@ -2963,7 +2963,7 @@ CREATE VIEW `account-user-view` AS SELECT INNER JOIN `contact` ON `contact`.`uri-id` = `ucontact`.`uri-id` AND `contact`.`uid` = 0 LEFT JOIN `item-uri` ON `item-uri`.`id` = `ucontact`.`uri-id` LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `ucontact`.`uri-id` - LEFT JOIN `fcontact` ON `fcontact`.`uri-id` = `ucontact`.`uri-id` AND `fcontact`.`network` = 'dspr' + LEFT JOIN `diaspora-contact` ON `diaspora-contact`.`uri-id` = `ucontact`.`uri-id` LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid`; -- diff --git a/src/Console/Relocate.php b/src/Console/Relocate.php index 729bee392..a90802d81 100644 --- a/src/Console/Relocate.php +++ b/src/Console/Relocate.php @@ -131,9 +131,9 @@ HELP; $this->out('Updating event table fields'); $this->database->replaceInTableFields('event', ['uri'], $old_url, $new_url); - $this->out('Updating fcontact table fields'); - $this->database->replaceInTableFields('fcontact', ['url', 'photo', 'request', 'batch', 'poll', 'confirm', 'alias'], $old_url, $new_url); - $this->database->replaceInTableFields('fcontact', ['addr'], $old_host, $new_host); + $this->out('Updating diaspora-contact table fields'); + $this->database->replaceInTableFields('diaspora-contact', ['alias', 'photo', 'photo-medium', 'photo-small', 'batch', 'notify', 'poll', 'subscribe'], $old_url, $new_url); + $this->database->replaceInTableFields('diaspora-contact', ['addr'], $old_host, $new_host); $this->out('Updating fsuggest table fields'); $this->database->replaceInTableFields('fsuggest', ['url', 'request', 'photo'], $old_url, $new_url); diff --git a/src/DI.php b/src/DI.php index 3a8a9b6d2..59f48fcb5 100644 --- a/src/DI.php +++ b/src/DI.php @@ -599,6 +599,11 @@ abstract class DI return self::$dice->create(Protocol\Activity::class); } + public static function dsprContact(): Protocol\Diaspora\Repository\DiasporaContact + { + return self::$dice->create(Protocol\Diaspora\Repository\DiasporaContact::class); + } + // // "Security" namespace instances // diff --git a/src/Database/Database.php b/src/Database/Database.php index 036e6ec2c..b5d496392 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -833,7 +833,7 @@ class Database /** * Check if data exists * - * @param string $table Table name in format schema.table (while scheme is optiona) + * @param string $table Table name in format [schema.]table * @param array $condition Array of fields for condition * * @return boolean Are there rows for that condition? @@ -1017,7 +1017,7 @@ class Database /** * Insert a row into a table. Field value objects will be cast as string. * - * @param string $table Table name in format schema.table (while scheme is optiona) + * @param string $table Table name in format [schema.]table * @param array $param parameter array * @param int $duplicate_mode What to do on a duplicated entry * @@ -1068,7 +1068,7 @@ class Database * Inserts a row with the provided data in the provided table. * If the data corresponds to an existing row through a UNIQUE or PRIMARY index constraints, it updates the row instead. * - * @param string $table Table name in format schema.table (while scheme is optiona) + * @param string $table Table name in format [schema.]table * @param array $param parameter array * @return boolean was the insert successful? * @throws \Exception @@ -1116,7 +1116,7 @@ class Database * * This function can be extended in the future to accept a table array as well. * - * @param string $table Table name in format schema.table (while scheme is optiona) + * @param string $table Table name in format [schema.]table * @return boolean was the lock successful? * @throws \Exception */ @@ -1314,7 +1314,7 @@ class Database * Only set $old_fields to a boolean value when you are sure that you will update a single row. * When you set $old_fields to "true" then $fields must contain all relevant fields! * - * @param string $table Table name in format schema.table (while scheme is optiona) + * @param string $table Table name in format [schema.]table * @param array $fields contains the fields that are updated * @param array $condition condition array with the key values * @param array|boolean $old_fields array with the old field values that are about to be replaced (true = update on duplicate, false = don't update identical fields) @@ -1380,7 +1380,7 @@ class Database /** * Retrieve a single record from a table and returns it in an associative array * - * @param string $table Table name in format schema.table (while scheme is optiona) + * @param string $table Table name in format [schema.]table * @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 @@ -1406,7 +1406,7 @@ class Database /** * Select rows from a table and fills an array with the data * - * @param string $table Table name in format schema.table (while scheme is optiona) + * @param string $table Table name in format [schema.]table * @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 @@ -1479,7 +1479,7 @@ class Database * * $data = DBA::select($table, $fields, $condition, $params); * - * @param string $table Table name in format schema.table (while scheme is optiona) + * @param string $table Table name in format [schema.]table * @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 @@ -1519,7 +1519,7 @@ class Database /** * Counts the rows from a table satisfying the provided condition * - * @param string $table Table name in format schema.table (while scheme is optiona) + * @param string $table Table name in format [schema.]table * @param array $condition Array of fields for condition * @param array $params Array of several parameters * diff --git a/src/Model/Contact.php b/src/Model/Contact.php index e8a718f05..e80f0752e 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -1395,7 +1395,7 @@ class Contact } if ($data['network'] == Protocol::DIASPORA) { - FContact::updateFromProbeArray($data); + DI::dsprContact()->updateFromProbeArray($data); } self::updateFromProbeArray($contact_id, $data); @@ -2486,7 +2486,7 @@ class Contact $ret = Probe::uri($contact['url'], $network, $contact['uid']); if ($ret['network'] == Protocol::DIASPORA) { - FContact::updateFromProbeArray($ret); + DI::dsprContact()->updateFromProbeArray($ret); } return self::updateFromProbeArray($id, $ret); diff --git a/src/Network/Probe.php b/src/Network/Probe.php index 5c188f252..d79cd4055 100644 --- a/src/Network/Probe.php +++ b/src/Network/Probe.php @@ -110,7 +110,8 @@ class Probe */ private static function rearrangeData(array $data): array { - $fields = ['name', 'nick', 'guid', 'url', 'addr', 'alias', 'photo', 'header', + $fields = ['name', 'given_name', 'family_name', 'nick', 'guid', 'url', 'addr', 'alias', + 'photo', 'photo_medium', 'photo_small', 'header', 'account-type', 'community', 'keywords', 'location', 'about', 'xmpp', 'matrix', 'hide', 'batch', 'notify', 'poll', 'request', 'confirm', 'subscribe', 'poco', 'following', 'followers', 'inbox', 'outbox', 'sharedinbox', @@ -124,7 +125,7 @@ class Probe if (in_array($field, $numeric_fields)) { $newdata[$field] = (int)$data[$field]; } else { - $newdata[$field] = $data[$field]; + $newdata[$field] = trim($data[$field]); } } elseif (!in_array($field, $numeric_fields)) { $newdata[$field] = ''; @@ -1290,9 +1291,19 @@ class Probe $data['name'] = $search->item(0)->nodeValue; } + $search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' given_name ')]", $vcard); // */ + if ($search->length > 0) { + $data["given_name"] = $search->item(0)->nodeValue; + } + + $search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' family_name ')]", $vcard); // */ + if ($search->length > 0) { + $data["family_name"] = $search->item(0)->nodeValue; + } + $search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' searchable ')]", $vcard); // */ if ($search->length > 0) { - $data['searchable'] = $search->item(0)->nodeValue; + $data['hide'] = (strtolower($search->item(0)->nodeValue) != 'true'); } $search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' key ')]", $vcard); // */ @@ -1309,7 +1320,7 @@ class Probe } } - $avatar = []; + $avatars = []; if (!empty($vcard)) { $photos = $xpath->query("//*[contains(concat(' ', @class, ' '), ' photo ') or contains(concat(' ', @class, ' '), ' avatar ')]", $vcard); // */ foreach ($photos as $photo) { @@ -1319,20 +1330,27 @@ class Probe } if (isset($attr['src']) && isset($attr['width'])) { - $avatar[$attr['width']] = $attr['src']; + $avatars[$attr['width']] = self::fixAvatar($attr['src'], $data['baseurl']); } // We don't have a width. So we just take everything that we got. // This is a Hubzilla workaround which doesn't send a width. - if ((sizeof($avatar) == 0) && !empty($attr['src'])) { - $avatar[] = $attr['src']; + if (!$avatars && !empty($attr['src'])) { + $avatars[] = self::fixAvatar($attr['src'], $data['baseurl']); } } } - if (sizeof($avatar)) { - ksort($avatar); - $data['photo'] = self::fixAvatar(array_pop($avatar), $data['baseurl']); + if ($avatars) { + ksort($avatars); + $data['photo'] = array_pop($avatars); + if ($avatars) { + $data['photo_medium'] = array_pop($avatars); + } + + if ($avatars) { + $data['photo_small'] = array_pop($avatars); + } } if ($dfrn) { @@ -1356,7 +1374,6 @@ class Probe } } - return $data; } diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index a86cf2094..621cc2e3c 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -45,6 +45,7 @@ use Friendica\Model\Post; use Friendica\Model\Profile; use Friendica\Model\Tag; use Friendica\Model\User; +use Friendica\Network\HTTPException; use Friendica\Network\Probe; use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; @@ -981,12 +982,12 @@ class DFRN } } - $fcontact = FContact::getByURL($contact['addr']); - if (empty($fcontact)) { + try { + $pubkey = DI::dsprContact()->getByAddr(WebFingerUri::fromString($contact['addr']))->pubKey; + } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) { Logger::notice('Unable to find contact details for ' . $contact['id'] . ' - ' . $contact['addr']); return -22; } - $pubkey = $fcontact['pubkey'] ?? ''; } else { $pubkey = ''; } diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index 7fd899f8d..bb955dca1 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -33,7 +33,6 @@ use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Conversation; -use Friendica\Model\FContact; use Friendica\Model\GServer; use Friendica\Model\Item; use Friendica\Model\ItemURI; @@ -42,7 +41,7 @@ use Friendica\Model\Post; use Friendica\Model\Tag; use Friendica\Model\User; use Friendica\Network\HTTPClient\Client\HttpClientAccept; -use Friendica\Network\HTTPException\InternalServerErrorException; +use Friendica\Network\HTTPException; use Friendica\Network\Probe; use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; @@ -455,9 +454,8 @@ class Diaspora } // Once we have the author URI, go to the web and try to find their public key - // (first this will look it up locally if it is in the fcontact cache) + // (first this will look it up locally if it is in the diaspora-contact cache) // This will also convert diaspora public key from pkcs#1 to pkcs#8 - Logger::notice('Fetching key for ' . $author); $key = self::key($author); if (!$key) { @@ -795,13 +793,11 @@ class Diaspora private static function key(WebFingerUri $uri): string { Logger::notice('Fetching diaspora key', ['handle' => $uri->getAddr(), 'callstack' => System::callstack(20)]); - - $fcontact = FContact::getByURL($uri); - if (!empty($fcontact['pubkey'])) { - return $fcontact['pubkey']; + try { + return DI::dsprContact()->getByAddr($uri)->pubKey; + } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) { + return ''; } - - return ''; } /** @@ -822,15 +818,17 @@ class Diaspora /** * Checks if the given contact url does support ActivityPub * - * @param string $url profile url - * @param boolean $update true = always update, false = never update, null = update when not found or outdated + * @param string $url profile url or WebFinger address + * @param boolean|null $update true = always update, false = never update, null = update when not found or outdated * @return boolean * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - public static function isSupportedByContactUrl(string $url, $update = null) + public static function isSupportedByContactUrl(string $url, ?bool $update = null): bool { - return !empty(FContact::getByURL($url, $update)); + $contact = Contact::getByURL($url, $update); + + return DI::dsprContact()->existsByUriId($contact['uri-id'] ?? 0); } /** @@ -977,7 +975,7 @@ class Diaspora // 0 => '[url=/people/0123456789abcdef]Foo Bar[/url]' // 1 => '0123456789abcdef' // 2 => 'Foo Bar' - $handle = FContact::getUrlByGuid($match[1]); + $handle = DI::dsprContact()->getUrlByGuid($match[1]); if ($handle) { $return = '@[url=' . $handle . ']' . $match[2] . '[/url]'; @@ -1188,18 +1186,21 @@ class Diaspora $item = Post::selectFirst($fields, $condition); if (!DBA::isResult($item)) { - $person = FContact::getByURL($author); - $result = self::storeByGuid($guid, $person['url'], false); + try { + $result = self::storeByGuid($guid, DI::dsprContact()->getByAddr($author)->url, false); - // We don't have an url for items that arrived at the public dispatcher - if (!$result && !empty($contact['url'])) { - $result = self::storeByGuid($guid, $contact['url'], false); - } + // We don't have an url for items that arrived at the public dispatcher + if (!$result && !empty($contact['url'])) { + $result = self::storeByGuid($guid, $contact['url'], false); + } - if ($result) { - Logger::info('Fetched missing item ' . $guid . ' - result: ' . $result); + if ($result) { + Logger::info('Fetched missing item ' . $guid . ' - result: ' . $result); - $item = Post::selectFirst($fields, $condition); + $item = Post::selectFirst($fields, $condition); + } + } catch (HTTPException\NotFoundException $e) { + Logger::notice('Unable to retrieve author details', ['author' => $author->getAddr()]); } } @@ -1422,16 +1423,14 @@ class Diaspora private static function getUriFromGuid(string $guid, WebFingerUri $person_uri = null): string { $item = Post::selectFirst(['uri'], ['guid' => $guid]); - if (DBA::isResult($item)) { + if ($item) { return $item['uri']; } elseif ($person_uri) { - $person = FContact::getByURL($person_uri); - - $parts = parse_url($person['url']); - unset($parts['path']); - $host_url = (string)Uri::fromParts($parts); - - return $host_url . '/objects/' . $guid; + try { + return DI::dsprContact()->selectOneByAddr($person_uri)->baseurl . '/objects/' . $guid; + } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) { + return ''; + } } return ''; @@ -1462,12 +1461,12 @@ class Diaspora continue; } - $person = FContact::getByURL($match[3]); - if (empty($person)) { - continue; - } + try { + $contact = DI::dsprContact()->getByUrl(new Uri($match[3])); + Tag::storeByHash($uriid, $match[1], $contact->name ?: $contact->nick, $contact->url); + } catch (\Throwable $e) { - Tag::storeByHash($uriid, $match[1], $person['name'] ?: $person['nick'], $person['url']); + } } } @@ -1523,14 +1522,15 @@ class Diaspora return false; } - $person = FContact::getByURL($author); - if (!is_array($person)) { - Logger::notice('Unable to find author details'); + try { + $author_url = (string)DI::dsprContact()->getByAddr($author)->url; + } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) { + Logger::notice('Unable to find author details', ['author' => $author->getAddr()]); return false; } // Fetch the contact id - if we know this contact - $author_contact = self::authorContactByUrl($contact, $person['url'], $importer['uid']); + $author_contact = self::authorContactByUrl($contact, $author_url, $importer['uid']); $datarray = []; @@ -1538,11 +1538,11 @@ class Diaspora $datarray['contact-id'] = $author_contact['cid']; $datarray['network'] = $author_contact['network']; - $datarray['author-link'] = $person['url']; - $datarray['author-id'] = Contact::getIdForURL($person['url'], 0); + $datarray['author-link'] = $author_url; + $datarray['author-id'] = Contact::getIdForURL($author_url); $datarray['owner-link'] = $contact['url']; - $datarray['owner-id'] = Contact::getIdForURL($contact['url'], 0); + $datarray['owner-id'] = Contact::getIdForURL($contact['url']); // Will be overwritten for sharing accounts in Item::insert $datarray = self::setDirection($datarray, $direction); @@ -1569,7 +1569,7 @@ class Diaspora $datarray['plink'] = self::plink($author, $guid, $toplevel_parent_item['guid']); $body = Markdown::toBBCode($text); - $datarray['body'] = self::replacePeopleGuid($body, $person['url']); + $datarray['body'] = self::replacePeopleGuid($body, $author_url); self::storeMentions($datarray['uri-id'], $text); Tag::storeRawTagsFromBody($datarray['uri-id'], $datarray['body']); @@ -1633,6 +1633,12 @@ class Diaspora return false; } + try { + $msg_author_uri = WebFingerUri::fromString($msg_author_handle); + } catch (\InvalidArgumentException $e) { + return false; + } + $msg_guid = XML::unescape($mesg->guid); $msg_conversation_guid = XML::unescape($mesg->conversation_guid); $msg_text = XML::unescape($mesg->text); @@ -1643,20 +1649,18 @@ class Diaspora return false; } - $body = Markdown::toBBCode($msg_text); - - $person = FContact::getByURL($msg_author_handle); + $msg_author = DI::dsprContact()->getByAddr($msg_author_uri); return Mail::insert([ 'uid' => $importer['uid'], 'guid' => $msg_guid, 'convid' => $conversation['id'], - 'from-name' => $person['name'], - 'from-photo' => $person['photo'], - 'from-url' => $person['url'], + 'from-name' => $msg_author->name, + 'from-photo' => (string)$msg_author->photo, + 'from-url' => (string)$msg_author->url, 'contact-id' => $contact['id'], 'title' => $subject, - 'body' => $body, + 'body' => Markdown::toBBCode($msg_text), 'uri' => $msg_author_handle . ':' . $msg_guid, 'parent-uri' => $author_handle . ':' . $guid, 'created' => $msg_created_at @@ -1770,14 +1774,15 @@ class Diaspora return false; } - $person = FContact::getByURL($author); - if (!is_array($person)) { - Logger::notice('Unable to find author details'); + try { + $author_url = (string)DI::dsprContact()->getByAddr($author)->url; + } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) { + Logger::notice('Unable to find author details', ['author' => $author->getAddr()]); return false; } // Fetch the contact id - if we know this contact - $author_contact = self::authorContactByUrl($contact, $person['url'], $importer['uid']); + $author_contact = self::authorContactByUrl($contact, $author_url, $importer['uid']); // "positive" = "false" would be a Dislike - wich isn't currently supported by Diaspora // We would accept this anyhow. @@ -1797,8 +1802,8 @@ class Diaspora $datarray = self::setDirection($datarray, $direction); - $datarray['owner-link'] = $datarray['author-link'] = $person['url']; - $datarray['owner-id'] = $datarray['author-id'] = Contact::getIdForURL($person['url'], 0); + $datarray['owner-link'] = $datarray['author-link'] = $author_url; + $datarray['owner-id'] = $datarray['author-id'] = Contact::getIdForURL($author_url); $datarray['guid'] = $guid; $datarray['uri'] = self::getUriFromGuid($guid, $author); @@ -1860,13 +1865,13 @@ class Diaspora */ private static function receiveMessage(array $importer, SimpleXMLElement $data): bool { - $author = WebFingerUri::fromString(XML::unescape($data->author)); + $author_uri = WebFingerUri::fromString(XML::unescape($data->author)); $guid = XML::unescape($data->guid); $conversation_guid = XML::unescape($data->conversation_guid); $text = XML::unescape($data->text); $created_at = DateTimeFormat::utc(XML::unescape($data->created_at)); - $contact = self::allowedContactByHandle($importer, $author, true); + $contact = self::allowedContactByHandle($importer, $author_uri, true); if (!$contact) { return false; } @@ -1882,29 +1887,30 @@ class Diaspora return false; } - $person = FContact::getByURL($author); - if (!$person) { - Logger::notice('Unable to find author details'); + try { + $author = DI::dsprContact()->getByAddr($author_uri); + } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) { + Logger::notice('Unable to find author details', ['author' => $author_uri->getAddr()]); return false; } $body = Markdown::toBBCode($text); - $body = self::replacePeopleGuid($body, $person['url']); + $body = self::replacePeopleGuid($body, $author->url); return Mail::insert([ 'uid' => $importer['uid'], 'guid' => $guid, 'convid' => $conversation['id'], - 'from-name' => $person['name'], - 'from-photo' => $person['photo'], - 'from-url' => $person['url'], + 'from-name' => $author->name, + 'from-photo' => (string)$author->photo, + 'from-url' => (string)$author->url, 'contact-id' => $contact['id'], 'title' => $conversation['subject'], 'body' => $body, 'reply' => 1, - 'uri' => $author . ':' . $guid, - 'parent-uri' => $author . ':' . $conversation['guid'], + 'uri' => $author_uri . ':' . $guid, + 'parent-uri' => $author_uri . ':' . $conversation['guid'], 'created' => $created_at ]); } @@ -1953,13 +1959,14 @@ class Diaspora return false; } - $person = FContact::getByURL($author); - if (!is_array($person)) { - Logger::notice('Person not found: ' . $author); + try { + $author_url = (string)DI::dsprContact()->getByAddr($author)->url; + } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) { + Logger::notice('unable to find author details', ['author' => $author->getAddr()]); return false; } - $author_contact = self::authorContactByUrl($contact, $person['url'], $importer['uid']); + $author_contact = self::authorContactByUrl($contact, $author_url, $importer['uid']); // Store participation $datarray = []; @@ -1972,8 +1979,8 @@ class Diaspora $datarray = self::setDirection($datarray, $direction); - $datarray['owner-link'] = $datarray['author-link'] = $person['url']; - $datarray['owner-id'] = $datarray['author-id'] = Contact::getIdForURL($person['url'], 0); + $datarray['owner-link'] = $datarray['author-link'] = $author_url; + $datarray['owner-id'] = $datarray['author-id'] = Contact::getIdForURL($author_url); $datarray['guid'] = $guid; $datarray['uri'] = self::getUriFromGuid($guid, $author); @@ -2233,22 +2240,24 @@ class Diaspora Logger::info("Author " . $author . " wants to listen to us."); } - $ret = FContact::getByURL($author); - - if (!$ret || ($ret['network'] != Protocol::DIASPORA)) { - Logger::notice("Cannot resolve diaspora handle " . $author . " for ".$recipient); + try { + $author_url = (string)DI::dsprContact()->getByAddr($author)->url; + } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) { + Logger::notice('Cannot resolve diaspora handle for recipient', ['author' => $author->getAddr(), 'recipient' => $recipient]); return false; } - $cid = Contact::getIdForURL($ret['url'], $importer['uid']); + $cid = Contact::getIdForURL($author_url, $importer['uid']); if (!empty($cid)) { $contact = DBA::selectFirst('contact', [], ['id' => $cid, 'network' => Protocol::NATIVE_SUPPORT]); } else { $contact = []; } - $item = ['author-id' => Contact::getIdForURL($ret['url']), - 'author-link' => $ret['url']]; + $item = [ + 'author-id' => Contact::getIdForURL($author_url), + 'author-link' => $author_url + ]; $result = Contact::addRelationship($importer, $contact, $item, false); if ($result === true) { @@ -2346,7 +2355,12 @@ class Diaspora $author = WebFingerUri::fromString(XML::unescape($data->author)); $guid = XML::unescape($data->guid); $created_at = DateTimeFormat::utc(XML::unescape($data->created_at)); - $root_author = XML::unescape($data->root_author); + try { + $root_author = WebFingerUri::fromString(XML::unescape($data->root_author)); + } catch (\InvalidArgumentException $e) { + return false; + } + $root_guid = XML::unescape($data->root_guid); /// @todo handle unprocessed property "provider_display_name" $public = XML::unescape($data->public); @@ -2365,8 +2379,9 @@ class Diaspora return true; } - $original_person = FContact::getByURL($root_author); - if (!$original_person) { + try { + $original_person = DI::dsprContact()->getByAddr($root_author); + } catch (HTTPException\NotFoundException $e) { return false; } @@ -2394,7 +2409,7 @@ class Diaspora $datarray = self::setDirection($datarray, $direction); - $datarray['quote-uri-id'] = self::getQuoteUriId($root_guid, $importer['uid'], $original_person['url']); + $datarray['quote-uri-id'] = self::getQuoteUriId($root_guid, $importer['uid'], $original_person->url); if (empty($datarray['quote-uri-id'])) { return false; } @@ -2462,19 +2477,18 @@ class Diaspora */ private static function itemRetraction(array $importer, array $contact, SimpleXMLElement $data): bool { - $author_handle = XML::unescape($data->author); + $author_uri = WebFingerUri::fromString(XML::unescape($data->author)); $target_guid = XML::unescape($data->target_guid); $target_type = XML::unescape($data->target_type); - $person = FContact::getByURL($author_handle); - if (!is_array($person)) { - Logger::notice('Unable to find author detail for ' . $author_handle); + try { + $author = DI::dsprContact()->getByAddr($author_uri); + } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) { + Logger::notice('Unable to find details for author', ['author' => $author_uri->getAddr()]); return false; } - if (empty($contact['url'])) { - $contact['url'] = $person['url']; - } + $contact_url = $contact['url'] ?? '' ?: (string)$author->url; // Fetch items that are about to be deleted $fields = ['uid', 'id', 'parent', 'author-link', 'uri-id']; @@ -2502,8 +2516,8 @@ class Diaspora $parent = Post::selectFirst(['author-link'], ['id' => $item['parent']]); // Only delete it if the parent author really fits - if (!Strings::compareLink($parent['author-link'], $contact['url']) && !Strings::compareLink($item['author-link'], $contact['url'])) { - Logger::info("Thread author " . $parent['author-link'] . " and item author " . $item['author-link'] . " don't fit to expected contact " . $contact['url']); + if (!Strings::compareLink($parent['author-link'], $contact_url) && !Strings::compareLink($item['author-link'], $contact_url)) { + Logger::info("Thread author " . $parent['author-link'] . " and item author " . $item['author-link'] . " don't fit to expected contact " . $contact_url); continue; } @@ -2968,13 +2982,13 @@ class Diaspora $logid = Strings::getRandomHex(4); - // We always try to use the data from the fcontact table. + // We always try to use the data from the diaspora-contact table. // This is important for transmitting data to Friendica servers. - if (!empty($contact['addr'])) { - $fcontact = FContact::getByURL($contact['addr']); - if (!empty($fcontact)) { - $dest_url = ($public_batch ? $fcontact['batch'] : $fcontact['notify']); - } + try { + $target = DI::dsprContact()->getByAddr(WebFingerUri::fromString($contact['addr'])); + $dest_url = $public_batch ? $target->batch : $target->notify; + } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) { + } if (empty($dest_url)) { @@ -3043,18 +3057,19 @@ class Diaspora } // When sending content to Friendica contacts using the Diaspora protocol - // we have to fetch the public key from the fcontact. + // we have to fetch the public key from the diaspora-contact. // This is due to the fact that legacy DFRN had unique keys for every contact. $pubkey = $contact['pubkey']; if (!empty($contact['addr'])) { - $fcontact = FContact::getByURL($contact['addr']); - if (!empty($fcontact)) { - $pubkey = $fcontact['pubkey']; + try { + $pubkey = DI::dsprContact()->getByAddr(WebFingerUri::fromString($contact['addr']))->pubKey; + } catch (HTTPException\NotFoundException|\InvalidArgumentException $e) { + } } else { // The "addr" field should always be filled. // If this isn't the case, it will raise a notice some lines later. - // And in the log we will see where it came from and we can handle it there. + // And in the log we will see where it came from, and we can handle it there. Logger::notice('Empty addr', ['contact' => $contact ?? [], 'callstack' => System::callstack(20)]); } @@ -4038,6 +4053,8 @@ class Diaspora * * @param integer $parent_id * @return boolean + * @throws InternalServerErrorException + * @throws \ImagickException */ private static function parentSupportDiaspora(int $parent_id): bool { @@ -4047,7 +4064,7 @@ class Diaspora return false; } - if (empty(FContact::getByURL($parent_post['author-link'], false))) { + if (!self::isSupportedByContactUrl($parent_post['author-link'], false)) { Logger::info('Parent author is no Diaspora contact.', ['parent-id' => $parent_id]); return false; } diff --git a/src/Protocol/Diaspora/Entity/DiasporaContact.php b/src/Protocol/Diaspora/Entity/DiasporaContact.php new file mode 100644 index 000000000..7fbd2831f --- /dev/null +++ b/src/Protocol/Diaspora/Entity/DiasporaContact.php @@ -0,0 +1,140 @@ +. + * + */ + +namespace Friendica\Protocol\Diaspora\Entity; + +use Psr\Http\Message\UriInterface; + +/** + * @property-read $uriId + * @property-read $url + * @property-read $guid + * @property-read $addr + * @property-read $alias + * @property-read $nick + * @property-read $name + * @property-read $givenName + * @property-read $familyName + * @property-read $photo + * @property-read $photoMedium + * @property-read $photoSmall + * @property-read $batch + * @property-read $notify + * @property-read $poll + * @property-read $subscribe + * @property-read $searchable + * @property-read $pubKey + * @property-read $baseurl + * @property-read $gsid + * @property-read $created + * @property-read $updated + * @property-read $interacting_count + * @property-read $interacted_count + * @property-read $post_count + */ +class DiasporaContact extends \Friendica\BaseEntity +{ + /** @var int */ + protected $uriId; + /** @var UriInterface */ + protected $url; + /** @var string */ + protected $guid; + /** @var string */ + protected $addr; + /** @var UriInterface */ + protected $alias; + /** @var string */ + protected $nick; + /** @var string */ + protected $name; + /** @var string */ + protected $givenName; + /** @var string */ + protected $familyName; + /** @var UriInterface */ + protected $photo; + /** @var UriInterface */ + protected $photoMedium; + /** @var UriInterface */ + protected $photoSmall; + /** @var UriInterface */ + protected $batch; + /** @var UriInterface */ + protected $notify; + /** @var UriInterface */ + protected $poll; + /** @var UriInterface */ + protected $subscribe; + /** @var bool */ + protected $searchable; + /** @var string */ + protected $pubKey; + /** @var UriInterface */ + protected $baseurl; + /** @var int */ + protected $gsid; + /** @var \DateTime */ + protected $created; + /** @var \DateTime */ + protected $updated; + /** @var int */ + protected $interacting_count; + /** @var int */ + protected $interacted_count; + /** @var int */ + protected $post_count; + + public function __construct( + UriInterface $url, \DateTime $created, string $guid = null, string $addr = null, UriInterface $alias = null, + string $nick = null, string $name = null, string $givenName = null, string $familyName = null, + UriInterface $photo = null, UriInterface $photoMedium = null, UriInterface $photoSmall = null, + UriInterface $batch = null, UriInterface $notify = null, UriInterface $poll = null, UriInterface $subscribe = null, + bool $searchable = null, string $pubKey = null, UriInterface $baseurl = null, int $gsid = null, + \DateTime $updated = null, int $interacting_count = 0, int $interacted_count = 0, int $post_count = 0, int $uriId = null + ) { + $this->uriId = $uriId; + $this->url = $url; + $this->guid = $guid; + $this->addr = $addr; + $this->alias = $alias; + $this->nick = $nick; + $this->name = $name; + $this->givenName = $givenName; + $this->familyName = $familyName; + $this->photo = $photo; + $this->photoMedium = $photoMedium; + $this->photoSmall = $photoSmall; + $this->batch = $batch; + $this->notify = $notify; + $this->poll = $poll; + $this->subscribe = $subscribe; + $this->searchable = $searchable; + $this->pubKey = $pubKey; + $this->baseurl = $baseurl; + $this->gsid = $gsid; + $this->created = $created; + $this->updated = $updated; + $this->interacting_count = $interacting_count; + $this->interacted_count = $interacted_count; + $this->post_count = $post_count; + } +} diff --git a/src/Protocol/Diaspora/Factory/DiasporaContact.php b/src/Protocol/Diaspora/Factory/DiasporaContact.php new file mode 100644 index 000000000..d5c91d200 --- /dev/null +++ b/src/Protocol/Diaspora/Factory/DiasporaContact.php @@ -0,0 +1,102 @@ +. + * + */ + +namespace Friendica\Protocol\Diaspora\Factory; + +use Friendica\Capabilities\ICanCreateFromTableRow; +use Friendica\Database\DBA; +use GuzzleHttp\Psr7\Uri; + +class DiasporaContact extends \Friendica\BaseFactory implements ICanCreateFromTableRow +{ + public function createFromTableRow(array $row): \Friendica\Protocol\Diaspora\Entity\DiasporaContact + { + return new \Friendica\Protocol\Diaspora\Entity\DiasporaContact( + new Uri($row['url']), + new \DateTime($row['created'], new \DateTimeZone('UTC')), + $row['guid'], + $row['addr'], + $row['alias'] ? new Uri($row['alias']) : null, + $row['nick'], + $row['name'], + $row['given-name'], + $row['family-name'], + $row['photo'] ? new Uri($row['photo']) : null, + $row['photo-medium'] ? new Uri($row['photo-medium']) : null, + $row['photo-small'] ? new Uri($row['photo-small']) : null, + $row['batch'] ? new Uri($row['batch']) : null, + $row['notify'] ? new Uri($row['notify']) : null, + $row['poll'] ? new Uri($row['poll']) : null, + $row['subscribe'] ? new Uri($row['subscribe']) : null, + $row['searchable'], + $row['pubkey'], + $row['baseurl'] ? new Uri($row['baseurl']) : null, + $row['gsid'], + $row['updated'] !== DBA::NULL_DATETIME ? new \DateTime($row['updated'], new \DateTimeZone('UTC')) : null, + $row['interacting_count'], + $row['interacted_count'], + $row['post_count'], + $row['uri-id'], + ); + } + + /** + * @param array $data Data returned by \Friendica\Network\Probe::uri() + * @param int $uriId The URI ID of the Diaspora contact URL + GUID + * @param \DateTime $created + * @param int $interacting_count + * @param int $interacted_count + * @param int $post_count + * @return \Friendica\Protocol\Diaspora\Entity\DiasporaContact + */ + public function createfromProbeData(array $data, int $uriId, \DateTime $created, int $interacting_count = 0, int $interacted_count = 0, int $post_count = 0): \Friendica\Protocol\Diaspora\Entity\DiasporaContact + { + $alias = $data['alias'] != $data['url'] ? $data['alias'] : null; + + return new \Friendica\Protocol\Diaspora\Entity\DiasporaContact( + new Uri($data['url']), + $created, + $data['guid'], + $data['addr'], + $alias ? new Uri($alias) : null, + $data['nick'], + $data['name'], + $data['given-name'] ?? '', + $data['family-name'] ?? '', + $data['photo'] ? new Uri($data['photo']) : null, + !empty($data['photo_medium']) ? new Uri($data['photo_medium']) : null, + !empty($data['photo_small']) ? new Uri($data['photo_small']) : null, + $data['batch'] ? new Uri($data['batch']) : null, + $data['notify'] ? new Uri($data['notify']) : null, + $data['poll'] ? new Uri($data['poll']) : null, + $data['subscribe'] ? new Uri($data['subscribe']) : null, + !$data['hide'], + $data['pubkey'], + $data['baseurl'] ? new Uri($data['baseurl']) : null, + $data['gsid'], + null, + $interacting_count, + $interacted_count, + $post_count, + $uriId, + ); + } +} diff --git a/src/Protocol/Diaspora/Repository/DiasporaContact.php b/src/Protocol/Diaspora/Repository/DiasporaContact.php new file mode 100644 index 000000000..ac8d200aa --- /dev/null +++ b/src/Protocol/Diaspora/Repository/DiasporaContact.php @@ -0,0 +1,283 @@ +. + * + */ + +namespace Friendica\Protocol\Diaspora\Repository; + +use Friendica\BaseRepository; +use Friendica\Core\System; +use Friendica\Database\Database; +use Friendica\Database\Definition\DbaDefinition; +use Friendica\Model\APContact; +use Friendica\Model\Contact; +use Friendica\Model\Item; +use Friendica\Model\ItemURI; +use Friendica\Network\HTTPException; +use Friendica\Protocol\Diaspora\Entity; +use Friendica\Protocol\Diaspora\Factory; +use Friendica\Protocol\WebFingerUri; +use Friendica\Util\DateTimeFormat; +use Psr\Http\Message\UriInterface; +use Psr\Log\LoggerInterface; + +class DiasporaContact extends BaseRepository +{ + const ALWAYS_UPDATE = true; + const NEVER_UPDATE = false; + const UPDATE_IF_MISSING_OR_OUTDATED = null; + + protected static $table_name = 'diaspora-contact-view'; + + /** @var Factory\DiasporaContact */ + protected $factory; + /** @var DbaDefinition */ + private $definition; + + public function __construct(DbaDefinition $definition, Database $database, LoggerInterface $logger, Factory\DiasporaContact $factory) + { + parent::__construct($database, $logger, $factory); + + $this->definition = $definition; + } + + /** + * @param array $condition + * @param array $params + * @return Entity\DiasporaContact + * @throws HTTPException\NotFoundException + */ + public function selectOne(array $condition, array $params = []): Entity\DiasporaContact + { + return parent::_selectOne($condition, $params); + } + + /** + * @param int $uriId + * @return Entity\DiasporaContact + * @throws HTTPException\NotFoundException + */ + public function selectOneByUriId(int $uriId): Entity\DiasporaContact + { + return $this->selectOne(['uri-id' => $uriId]); + } + + /** + * @param UriInterface $uri + * @return Entity\DiasporaContact + * @throws HTTPException\NotFoundException + */ + public function selectOneByUri(UriInterface $uri): Entity\DiasporaContact + { + try { + return $this->selectOne(['url' => (string) $uri]); + } catch (HTTPException\NotFoundException $e) { + } + + try { + return $this->selectOne(['addr' => (string) $uri]); + } catch (HTTPException\NotFoundException $e) { + } + + return $this->selectOne(['alias' => (string) $uri]); + } + + /** + * @param WebFingerUri $uri + * @return Entity\DiasporaContact + * @throws HTTPException\NotFoundException + */ + public function selectOneByAddr(WebFingerUri $uri): Entity\DiasporaContact + { + return $this->selectOne(['addr' => $uri->getAddr()]); + } + + /** + * @param int $uriId + * @return bool + * @throws \Exception + */ + public function existsByUriId(int $uriId): bool + { + return $this->db->exists(self::$table_name, ['uri-id' => $uriId]); + } + + public function save(Entity\DiasporaContact $DiasporaContact): Entity\DiasporaContact + { + $uriId = $DiasporaContact->uriId ?? ItemURI::insert(['uri' => $DiasporaContact->url, 'guid' => $DiasporaContact->guid]); + + $fields = [ + 'uri-id' => $uriId, + 'addr' => $DiasporaContact->addr, + 'alias' => (string)$DiasporaContact->alias, + 'nick' => $DiasporaContact->nick, + 'name' => $DiasporaContact->name, + 'given-name' => $DiasporaContact->givenName, + 'family-name' => $DiasporaContact->familyName, + 'photo' => (string)$DiasporaContact->photo, + 'photo-medium' => (string)$DiasporaContact->photoMedium, + 'photo-small' => (string)$DiasporaContact->photoSmall, + 'batch' => (string)$DiasporaContact->batch, + 'notify' => (string)$DiasporaContact->notify, + 'poll' => (string)$DiasporaContact->poll, + 'subscribe' => (string)$DiasporaContact->subscribe, + 'searchable' => $DiasporaContact->searchable, + 'pubkey' => $DiasporaContact->pubKey, + 'gsid' => $DiasporaContact->gsid, + 'created' => $DiasporaContact->created->format(DateTimeFormat::MYSQL), + 'updated' => DateTimeFormat::utcNow(), + 'interacting_count' => $DiasporaContact->interacting_count, + 'interacted_count' => $DiasporaContact->interacted_count, + 'post_count' => $DiasporaContact->post_count, + ]; + + // Limit the length on incoming fields + $fields = $this->definition->truncateFieldsForTable('diaspora-contact', $fields); + + $this->db->insert('diaspora-contact', $fields, Database::INSERT_UPDATE); + + return $this->selectOneByUriId($uriId); + } + + /** + * Fetch a Diaspora profile from a given WebFinger address and updates it depending on the mode + * + * @param WebFingerUri $uri Profile address + * @param boolean $update true = always update, false = never update, null = update when not found or outdated + * @return Entity\DiasporaContact + * @throws HTTPException\NotFoundException + */ + public function getByAddr(WebFingerUri $uri, ?bool $update = self::UPDATE_IF_MISSING_OR_OUTDATED): Entity\DiasporaContact + { + if ($update !== self::ALWAYS_UPDATE) { + try { + $dcontact = $this->selectOneByAddr($uri); + if ($update === self::NEVER_UPDATE) { + return $dcontact; + } + } catch (HTTPException\NotFoundException $e) { + if ($update === self::NEVER_UPDATE) { + throw $e; + } + + // This is necessary for Contact::getByURL in case the base contact record doesn't need probing, + // but we still need the result of a probe to create the missing diaspora-contact record. + $update = self::ALWAYS_UPDATE; + } + } + + $contact = Contact::getByURL($uri, $update, ['uri-id']); + if (empty($contact['uri-id'])) { + throw new HTTPException\NotFoundException('Diaspora profile with URI ' . $uri . ' not found'); + } + + return self::selectOneByUriId($contact['uri-id']); + } + + /** + * Fetch a Diaspora profile from a given profile URL and updates it depending on the mode + * + * @param UriInterface $uri Profile URL + * @param boolean $update true = always update, false = never update, null = update when not found or outdated + * @return Entity\DiasporaContact + * @throws HTTPException\NotFoundException + */ + public function getByUrl(UriInterface $uri, ?bool $update = self::UPDATE_IF_MISSING_OR_OUTDATED): Entity\DiasporaContact + { + if ($update !== self::ALWAYS_UPDATE) { + try { + $dcontact = $this->selectOneByUriId(ItemURI::getIdByURI($uri)); + if ($update === self::NEVER_UPDATE) { + return $dcontact; + } + } catch (HTTPException\NotFoundException $e) { + if ($update === self::NEVER_UPDATE) { + throw $e; + } + + // This is necessary for Contact::getByURL in case the base contact record doesn't need probing, + // but we still need the result of a probe to create the missing diaspora-contact record. + $update = self::ALWAYS_UPDATE; + } + } + + $contact = Contact::getByURL($uri, $update, ['uri-id']); + if (empty($contact['uri-id'])) { + throw new HTTPException\NotFoundException('Diaspora profile with URI ' . $uri . ' not found'); + } + + return self::selectOneByUriId($contact['uri-id']); + } + + /** + * Update or create a diaspora-contact entry via a probe array + * + * @param array $data Probe array + * @return Entity\DiasporaContact + * @throws \Exception + */ + public function updateFromProbeArray(array $data): Entity\DiasporaContact + { + $uriId = ItemURI::insert(['uri' => $data['url'], 'guid' => $data['guid']]); + + $contact = Contact::getByUriId($uriId, ['id', 'created']); + $apcontact = APContact::getByURL($data['url'], false); + if (!empty($apcontact)) { + $interacting_count = $apcontact['followers_count']; + $interacted_count = $apcontact['following_count']; + $post_count = $apcontact['statuses_count']; + } elseif (!empty($contact['id'])) { + $last_interaction = DateTimeFormat::utc('now - 180 days'); + + $interacting_count = $this->db->count('contact-relation', ["`relation-cid` = ? AND NOT `follows` AND `last-interaction` > ?", $contact['id'], $last_interaction]); + $interacted_count = $this->db->count('contact-relation', ["`cid` = ? AND NOT `follows` AND `last-interaction` > ?", $contact['id'], $last_interaction]); + $post_count = $this->db->count('post', ['author-id' => $contact['id'], 'gravity' => [Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT]]); + } + + $DiasporaContact = $this->factory->createfromProbeData( + $data, + $uriId, + new \DateTime($contact['created'] ?? 'now', new \DateTimeZone('UTC')), + $interacting_count ?? 0, + $interacted_count ?? 0, + $post_count ?? 0 + ); + + $DiasporaContact = $this->save($DiasporaContact); + + $this->logger->info('Updated diaspora-contact', ['url' => (string) $DiasporaContact->url, 'callstack' => System::callstack(20)]); + + return $DiasporaContact; + } + + /** + * get a url (scheme://domain.tld/u/user) from a given contact guid + * + * @param mixed $guid Hexadecimal string guid + * + * @return string the contact url or null + * @throws \Exception + */ + public function getUrlByGuid(string $guid): ?string + { + $diasporaContact = $this->db->selectFirst(self::$table_name, ['url'], ['guid' => $guid]); + + return $diasporaContact['url'] ?? null; + } +} diff --git a/src/Worker/Delivery.php b/src/Worker/Delivery.php index 706adb401..cec894480 100644 --- a/src/Worker/Delivery.php +++ b/src/Worker/Delivery.php @@ -29,7 +29,6 @@ use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; -use Friendica\Model\FContact; use Friendica\Model\GServer; use Friendica\Model\Item; use Friendica\Model\Post; @@ -94,7 +93,7 @@ class Delivery if ($item['verb'] == Activity::ANNOUNCE) { continue; } - + if ($item['id'] == $parent_id) { $parent = $item; } @@ -278,7 +277,7 @@ class Delivery private static function deliverDFRN(string $cmd, array $contact, array $owner, array $items, array $target_item, bool $public_message, bool $top_level, bool $followup, int $server_protocol = null) { // Transmit Diaspora reshares via Diaspora if the Friendica contact support Diaspora - if (Diaspora::getReshareDetails($target_item ?? []) && !empty(FContact::getByURL($contact['addr'], false))) { + if (Diaspora::getReshareDetails($target_item ?? []) && Diaspora::isSupportedByContactUrl($contact['addr'], false)) { Logger::info('Reshare will be transmitted via Diaspora', ['url' => $contact['url'], 'guid' => ($target_item['guid'] ?? '') ?: $target_item['id']]); self::deliverDiaspora($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup); return; diff --git a/src/Worker/ExpirePosts.php b/src/Worker/ExpirePosts.php index 6f75d1855..a2ef33f33 100644 --- a/src/Worker/ExpirePosts.php +++ b/src/Worker/ExpirePosts.php @@ -189,7 +189,7 @@ class ExpirePosts AND NOT EXISTS(SELECT `uri-id` FROM `user-contact` WHERE `uri-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `contact` WHERE `uri-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `apcontact` WHERE `uri-id` = `item-uri`.`id`) - AND NOT EXISTS(SELECT `uri-id` FROM `fcontact` WHERE `uri-id` = `item-uri`.`id`) + AND NOT EXISTS(SELECT `uri-id` FROM `diaspora-contact` WHERE `uri-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `inbox-status` WHERE `uri-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `post-delivery` WHERE `uri-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `post-delivery` WHERE `inbox-id` = `item-uri`.`id`) diff --git a/static/dbview.config.php b/static/dbview.config.php index ab18e80e5..c82e7dc77 100644 --- a/static/dbview.config.php +++ b/static/dbview.config.php @@ -990,11 +990,11 @@ "blocked" => ["contact", "blocked"], "dfrn-notify" => ["contact", "notify"], "dfrn-poll" => ["contact", "poll"], - "diaspora-guid" => ["fcontact", "guid"], - "diaspora-batch" => ["fcontact", "batch"], - "diaspora-notify" => ["fcontact", "notify"], - "diaspora-poll" => ["fcontact", "poll"], - "diaspora-alias" => ["fcontact", "alias"], + "diaspora-guid" => ["item-uri", "guid"], + "diaspora-batch" => ["diaspora-contact", "batch"], + "diaspora-notify" => ["diaspora-contact", "notify"], + "diaspora-poll" => ["diaspora-contact", "poll"], + "diaspora-alias" => ["diaspora-contact", "alias"], "ap-uuid" => ["apcontact", "uuid"], "ap-type" => ["apcontact", "type"], "ap-following" => ["apcontact", "following"], @@ -1013,7 +1013,7 @@ "query" => "FROM `contact` LEFT JOIN `item-uri` ON `item-uri`.`id` = `contact`.`uri-id` LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `contact`.`uri-id` - LEFT JOIN `fcontact` ON `fcontact`.`uri-id` = contact.`uri-id` + LEFT JOIN `diaspora-contact` ON `diaspora-contact`.`uri-id` = contact.`uri-id` LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid` WHERE `contact`.`uid` = 0" ], @@ -1089,14 +1089,14 @@ "reason" => ["ucontact", "reason"], "dfrn-notify" => ["contact", "notify"], "dfrn-poll" => ["contact", "poll"], - "diaspora-guid" => ["fcontact", "guid"], - "diaspora-batch" => ["fcontact", "batch"], - "diaspora-notify" => ["fcontact", "notify"], - "diaspora-poll" => ["fcontact", "poll"], - "diaspora-alias" => ["fcontact", "alias"], - "diaspora-interacting_count" => ["fcontact", "interacting_count"], - "diaspora-interacted_count" => ["fcontact", "interacted_count"], - "diaspora-post_count" => ["fcontact", "post_count"], + "diaspora-guid" => ["item-uri", "guid"], + "diaspora-batch" => ["diaspora-contact", "batch"], + "diaspora-notify" => ["diaspora-contact", "notify"], + "diaspora-poll" => ["diaspora-contact", "poll"], + "diaspora-alias" => ["diaspora-contact", "alias"], + "diaspora-interacting_count" => ["diaspora-contact", "interacting_count"], + "diaspora-interacted_count" => ["diaspora-contact", "interacted_count"], + "diaspora-post_count" => ["diaspora-contact", "post_count"], "ap-uuid" => ["apcontact", "uuid"], "ap-type" => ["apcontact", "type"], "ap-following" => ["apcontact", "following"], @@ -1116,7 +1116,7 @@ INNER JOIN `contact` ON `contact`.`uri-id` = `ucontact`.`uri-id` AND `contact`.`uid` = 0 LEFT JOIN `item-uri` ON `item-uri`.`id` = `ucontact`.`uri-id` LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `ucontact`.`uri-id` - LEFT JOIN `fcontact` ON `fcontact`.`uri-id` = `ucontact`.`uri-id` AND `fcontact`.`network` = 'dspr' + LEFT JOIN `diaspora-contact` ON `diaspora-contact`.`uri-id` = `ucontact`.`uri-id` LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid`" ], "pending-view" => [