diff --git a/database.sql b/database.sql index eff8f83faf..dd04a0e352 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ --- Friendica 2021.09-dev (Siberian Iris) --- DB_UPDATE_VERSION 1434 +-- Friendica 2021.09-rc (Siberian Iris) +-- DB_UPDATE_VERSION 1435 -- ------------------------------------------ @@ -1512,13 +1512,16 @@ CREATE TABLE IF NOT EXISTS `userd` ( CREATE TABLE IF NOT EXISTS `user-contact` ( `cid` int unsigned NOT NULL DEFAULT 0 COMMENT 'Contact id of the linked public contact', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', + `uri-id` int unsigned COMMENT 'Id of the item-uri table entry that contains the contact url', `blocked` boolean COMMENT 'Contact is completely blocked for this user', `ignored` boolean COMMENT 'Posts from this contact are ignored', `collapsed` boolean COMMENT 'Posts from this contact are collapsed', PRIMARY KEY(`uid`,`cid`), INDEX `cid` (`cid`), + UNIQUE INDEX `uri-id_uid` (`uri-id`,`uid`), FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE, - FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE + FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE, + FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='User specific public contact data'; -- diff --git a/doc/database/db_user-contact.md b/doc/database/db_user-contact.md index 35d140cfff..467ee86d90 100644 --- a/doc/database/db_user-contact.md +++ b/doc/database/db_user-contact.md @@ -6,21 +6,23 @@ User specific public contact data Fields ------ -| Field | Description | Type | Null | Key | Default | Extra | -| --------- | ------------------------------------------- | ------------------ | ---- | --- | ------- | ----- | -| cid | Contact id of the linked public contact | int unsigned | NO | PRI | 0 | | -| uid | User id | mediumint unsigned | NO | PRI | 0 | | -| blocked | Contact is completely blocked for this user | boolean | YES | | NULL | | -| ignored | Posts from this contact are ignored | boolean | YES | | NULL | | -| collapsed | Posts from this contact are collapsed | boolean | YES | | NULL | | +| Field | Description | Type | Null | Key | Default | Extra | +| --------- | ------------------------------------------------------------ | ------------------ | ---- | --- | ------- | ----- | +| cid | Contact id of the linked public contact | int unsigned | NO | PRI | 0 | | +| uid | User id | mediumint unsigned | NO | PRI | 0 | | +| uri-id | Id of the item-uri table entry that contains the contact url | int unsigned | YES | | NULL | | +| blocked | Contact is completely blocked for this user | boolean | YES | | NULL | | +| ignored | Posts from this contact are ignored | boolean | YES | | NULL | | +| collapsed | Posts from this contact are collapsed | boolean | YES | | NULL | | Indexes ------------ -| Name | Fields | -| ------- | -------- | -| PRIMARY | uid, cid | -| cid | cid | +| Name | Fields | +| ---------- | ------------------- | +| PRIMARY | uid, cid | +| cid | cid | +| uri-id_uid | UNIQUE, uri-id, uid | Foreign Keys ------------ @@ -29,5 +31,6 @@ Foreign Keys |-------|--------------|--------------| | cid | [contact](help/database/db_contact) | id | | uid | [user](help/database/db_user) | uid | +| uri-id | [item-uri](help/database/db_item-uri) | id | Return to [database documentation](help/database) diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 93e745456a..2cc1c091eb 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -175,7 +175,7 @@ class Contact * @param array $fields field array * @param int $duplicate_mode Do an update on a duplicate entry * - * @return boolean was the insert successful? + * @return int id of the created contact * @throws \Exception */ public static function insert(array $fields, int $duplicate_mode = Database::INSERT_DEFAULT) @@ -190,17 +190,22 @@ class Contact $fields['created'] = DateTimeFormat::utcNow(); } - $ret = DBA::insert('contact', $fields, $duplicate_mode); - $contact = DBA::selectFirst('contact', ['nurl', 'uid'], ['id' => DBA::lastInsertId()]); + DBA::insert('contact', $fields, $duplicate_mode); + $contact = DBA::selectFirst('contact', [], ['id' => DBA::lastInsertId()]); if (!DBA::isResult($contact)) { // Shouldn't happen - return $ret; + Logger::warning('Created contact could not be found', ['fields' => $fields]); + return 0; } - // Search for duplicated contacts and get rid of them - self::removeDuplicates($contact['nurl'], $contact['uid']); + Contact\User::insertForContactArray($contact); - return $ret; + // Search for duplicated contacts and get rid of them + if (!$contact['self']) { + self::removeDuplicates($contact['nurl'], $contact['uid']); + } + + return $contact['id']; } /** @@ -650,7 +655,7 @@ class Contact // Only create the entry if it doesn't exist yet if (!DBA::exists('contact', ['uid' => $uid, 'self' => true])) { - $return = DBA::insert('contact', $contact); + $return = (bool)self::insert($contact); } // Create the public contact @@ -659,7 +664,7 @@ class Contact $contact['uid'] = 0; $contact['prvkey'] = null; - DBA::insert('contact', $contact, Database::INSERT_IGNORE); + self::insert($contact, Database::INSERT_IGNORE); } return $return; @@ -1209,8 +1214,7 @@ class Contact $contact_id = $contact['id']; Logger::notice('Contact had been created (shortly) before', ['id' => $contact_id, 'url' => $url, 'uid' => $uid]); } else { - DBA::insert('contact', $fields); - $contact_id = DBA::lastInsertId(); + $contact_id = self::insert($fields); if ($contact_id) { Logger::info('Contact inserted', ['id' => $contact_id, 'url' => $url, 'uid' => $uid]); } @@ -1991,7 +1995,7 @@ class Contact */ public static function removeDuplicates(string $nurl, int $uid) { - $condition = ['nurl' => $nurl, 'uid' => $uid, 'deleted' => false, 'network' => Protocol::FEDERATED]; + $condition = ['nurl' => $nurl, 'uid' => $uid, 'self' => false, 'deleted' => false, 'network' => Protocol::FEDERATED]; $count = DBA::count('contact', $condition); if ($count <= 1) { return false; @@ -2355,7 +2359,7 @@ class Contact $probed = false; $ret = $arr['contact']; } else { - $probed = true; + $probed = true; $ret = Probe::uri($url, $network, $uid); } @@ -2684,7 +2688,7 @@ class Contact } // create contact record - DBA::insert('contact', [ + $contact_id = self::insert([ 'uid' => $importer['uid'], 'created' => DateTimeFormat::utcNow(), 'url' => $url, @@ -2699,8 +2703,6 @@ class Contact 'writable' => 1, ]); - $contact_id = DBA::lastInsertId(); - // Ensure to always have the correct network type, independent from the connection request method self::updateFromProbe($contact_id); diff --git a/src/Model/Contact/User.php b/src/Model/Contact/User.php index 26fd96303f..0283fae720 100644 --- a/src/Model/Contact/User.php +++ b/src/Model/Contact/User.php @@ -21,14 +21,56 @@ namespace Friendica\Model\Contact; +use Friendica\Core\Logger; +use Friendica\Core\System; +use Friendica\Database\Database; use Friendica\Database\DBA; use Friendica\Model\Contact; +use Friendica\Model\ItemURI; /** * This class provides information about user related contacts based on the "user-contact" table. */ class User { + /** + * Insert a user-contact for a given contact array + * + * @param array $contact + * @return void + */ + public static function insertForContactArray(array $contact) + { + if (!isset($contact['uid']) || (empty($contact['uri-id']) && empty($contact['url']))) { + Logger::info('Missing contact details', ['contact' => $contact, 'callstack' => System::callstack(20)]); + return false; + } + + if (empty($contact['uri-id'])) { + $contact['uri-id'] = ItemURI::getIdByURI($contact['url']); + } + + $pcontact = Contact::selectFirst(['id'], ['uri-id' => $contact['uri-id'], 'uid' => 0]); + if (!DBA::isResult($pcontact)) { + Logger::info('Public contact for user not found', ['uri-id' => $contact['uri-id'], 'uid' => $contact['uid'], 'cid' => $pcontact['id']]); + return false; + } + + $fields = [ + 'cid' => $pcontact['id'], + 'uid' => $contact['uid'], + 'uri-id' => $contact['uri-id'], + 'blocked' => $contact['blocked'] ?? false, + 'ignored' => $contact['readonly'] ?? false, + ]; + + $ret = DBA::insert('user-contact', $fields, Database::INSERT_IGNORE); + + Logger::info('Inserted user contact', ['uid' => $contact['uid'], 'cid' => $pcontact['id'], 'uri-id' => $contact['uri-id'], 'ret' => $ret]); + + return $ret; + } + /** * Block contact id for user id * diff --git a/src/Model/User.php b/src/Model/User.php index 4f63b38198..b2282fc188 100644 --- a/src/Model/User.php +++ b/src/Model/User.php @@ -234,7 +234,7 @@ class User $system['closeness'] = 0; $system['baseurl'] = DI::baseUrl(); $system['gsid'] = GServer::getID($system['baseurl']); - DBA::insert('contact', $system); + Contact::insert($system); } /** @@ -1585,8 +1585,8 @@ class User /** * Check if the given user id has delegations or is delegated * - * @param int $uid - * @return bool + * @param int $uid + * @return bool */ public static function hasIdentities(int $uid):bool { diff --git a/src/Worker/ExpirePosts.php b/src/Worker/ExpirePosts.php index bd1addf11a..8d7b8c6eea 100644 --- a/src/Worker/ExpirePosts.php +++ b/src/Worker/ExpirePosts.php @@ -184,6 +184,7 @@ class ExpirePosts AND NOT EXISTS(SELECT `external-id` FROM `post-user` WHERE `external-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `mail` WHERE `uri-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `event` WHERE `uri-id` = `item-uri`.`id`) + 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`) diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 6f455d14e9..fd6c4c798f 100644 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -55,7 +55,7 @@ use Friendica\Database\DBA; if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1434); + define('DB_UPDATE_VERSION', 1435); } return [ @@ -1531,6 +1531,7 @@ return [ "fields" => [ "cid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "primary" => "1", "foreign" => ["contact" => "id"], "comment" => "Contact id of the linked public contact"], "uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "primary" => "1", "foreign" => ["user" => "uid"], "comment" => "User id"], + "uri-id" => ["type" => "int unsigned", "foreign" => ["item-uri" => "id"], "comment" => "Id of the item-uri table entry that contains the contact url"], "blocked" => ["type" => "boolean", "comment" => "Contact is completely blocked for this user"], "ignored" => ["type" => "boolean", "comment" => "Posts from this contact are ignored"], "collapsed" => ["type" => "boolean", "comment" => "Posts from this contact are collapsed"] @@ -1538,6 +1539,7 @@ return [ "indexes" => [ "PRIMARY" => ["uid", "cid"], "cid" => ["cid"], + "uri-id_uid" => ["UNIQUE", "uri-id", "uid"], ] ], "worker-ipc" => [