From 7d50a086e03ee224985ff84016db31dcdc1bc44f Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 26 Aug 2019 15:51:56 +0000 Subject: [PATCH] Issue 7559: Merge contact duplicates --- src/Model/Contact.php | 38 ++++++++++++++++++-------------- src/Module/Contact.php | 44 +++---------------------------------- src/Worker/MergeContact.php | 41 ++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 57 deletions(-) create mode 100644 src/Worker/MergeContact.php diff --git a/src/Model/Contact.php b/src/Model/Contact.php index df3efa0c57..6f9ee4cdb6 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -1851,13 +1851,13 @@ class Contact extends BaseObject */ private static function handleDuplicates($nurl, $uid, $id) { - $condition = ['nurl' => $nurl, 'uid' => $uid, 'deleted' => false]; + $condition = ['nurl' => $nurl, 'uid' => $uid, 'deleted' => false, 'network' => Protocol::FEDERATED]; $count = DBA::count('contact', $condition); if ($count <= 1) { return false; } - $first_contact = DBA::selectFirst('contact', ['id'], $condition, ['order' => ['id']]); + $first_contact = DBA::selectFirst('contact', ['id', 'network'], $condition, ['order' => ['id']]); if (!DBA::isResult($first_contact)) { // Shouldn't happen - so we handle it return false; @@ -1865,26 +1865,21 @@ class Contact extends BaseObject $first = $first_contact['id']; Logger::info('Found duplicates', ['count' => $count, 'id' => $id, 'first' => $first, 'uid' => $uid, 'nurl' => $nurl]); - if ($uid != 0) { - // Don't handle non public duplicates by now - Logger::info('Not handling non public duplicate', ['uid' => $uid, 'nurl' => $nurl]); + if (($uid != 0 && ($first_contact['network'] == Protocol::DFRN))) { + // Don't handle non public DFRN duplicates by now (legacy DFRN is very special because of the key handling) + Logger::info('Not handling non public DFRN duplicate', ['uid' => $uid, 'nurl' => $nurl]); return false; } // Find all duplicates $condition = ["`nurl` = ? AND `uid` = ? AND `id` != ? AND NOT `self` AND NOT `deleted`", $nurl, $uid, $first]; - $duplicates = DBA::select('contact', ['id'], $condition); + $duplicates = DBA::select('contact', ['id', 'network'], $condition); while ($duplicate = DBA::fetch($duplicates)) { - $dup_id = $duplicate['id']; - Logger::info('Handling duplicate', ['search' => $dup_id, 'replace' => $first]); + if (!in_array($duplicate['network'], Protocol::FEDERATED)) { + continue; + } - // Search and replace - DBA::update('item', ['author-id' => $first], ['author-id' => $dup_id]); - DBA::update('item', ['owner-id' => $first], ['owner-id' => $dup_id]); - DBA::update('item', ['contact-id' => $first], ['contact-id' => $dup_id]); - - // Remove the duplicate - DBA::delete('contact', ['id' => $dup_id]); + Worker::add(PRIORITY_HIGH, 'MergeContact', $first, $duplicate['id'], $uid); } Logger::info('Duplicates handled', ['uid' => $uid, 'nurl' => $nurl]); return true; @@ -1985,7 +1980,7 @@ class Contact extends BaseObject } if (!$update) { - if ($force && ($uid == 0)) { + if ($force) { self::updateContact($id, $uid, $ret['url'], ['last-update' => $updated, 'success_update' => $updated]); } return true; @@ -2377,7 +2372,18 @@ class Contact extends BaseObject $nick = $pub_contact['nick']; $network = $pub_contact['network']; + // Ensure that we don't create a new contact when there already is one + $cid = self::getIdForURL($url, $importer['uid']); + if (!empty($cid)) { + $contact = DBA::selectFirst('contact', [], ['id' => $cid]); + } + if (!empty($contact)) { + if (!empty($contact['pending'])) { + Logger::info('Pending contact request already exists.', ['url' => $url, 'uid' => $importer['uid']]); + return null; + } + // Contact is blocked at user-level if (!empty($contact['id']) && !empty($importer['id']) && self::isBlockedByUser($contact['id'], $importer['id'])) { diff --git a/src/Module/Contact.php b/src/Module/Contact.php index 04156daba9..198496a0eb 100644 --- a/src/Module/Contact.php +++ b/src/Module/Contact.php @@ -176,54 +176,16 @@ class Contact extends BaseModule private static function updateContactFromProbe($contact_id) { - $contact = DBA::selectFirst('contact', ['uid', 'url', 'network'], ['id' => $contact_id, 'uid' => local_user(), 'deleted' => false]); + $contact = DBA::selectFirst('contact', ['url'], ['id' => $contact_id, 'uid' => local_user(), 'deleted' => false]); if (!DBA::isResult($contact)) { return; } - $uid = $contact['uid']; - - $data = Probe::uri($contact['url'], '', 0, false); - - // 'Feed' or 'Unknown' is mostly a sign of communication problems - if ((in_array($data['network'], [Protocol::FEED, Protocol::PHANTOM])) && ($data['network'] != $contact['network'])) { - return; - } - - $updatefields = ['name', 'nick', 'url', 'addr', 'batch', 'notify', 'poll', 'request', 'confirm', 'poco', 'network', 'alias']; - $fields = []; - - if ($data['network'] == Protocol::OSTATUS) { - $result = Model\Contact::createFromProbe($uid, $data['url'], false); - - if ($result['success']) { - $fields['subhub'] = true; - } - } - - foreach ($updatefields AS $field) { - if (!empty($data[$field])) { - $fields[$field] = $data[$field]; - } - } - - $fields['nurl'] = Strings::normaliseLink($data['url']); - - if (!empty($data['priority'])) { - $fields['priority'] = intval($data['priority']); - } - - if (empty($fields)) { - return; - } - - DBA::update('contact', $fields, ['id' => $contact_id, 'uid' => local_user()]); - // Update the entry in the contact table - Model\Contact::updateAvatar($data['photo'], local_user(), $contact_id, true); + Model\Contact::updateFromProbe($contact_id, '', true); // Update the entry in the gcontact table - Model\GContact::updateFromProbe($data['url']); + Model\GContact::updateFromProbe($contact['url']); } private static function blockContact($contact_id) diff --git a/src/Worker/MergeContact.php b/src/Worker/MergeContact.php new file mode 100644 index 0000000000..a5e28e5254 --- /dev/null +++ b/src/Worker/MergeContact.php @@ -0,0 +1,41 @@ + $dup_id, 'replace' => $first]); + + // Search and replace + DBA::update('item', ['contact-id' => $first], ['contact-id' => $dup_id]); + DBA::update('thread', ['contact-id' => $first], ['contact-id' => $dup_id]); + DBA::update('mail', ['contact-id' => $first], ['contact-id' => $dup_id]); + DBA::update('photo', ['contact-id' => $first], ['contact-id' => $dup_id]); + DBA::update('event', ['cid' => $first], ['cid' => $dup_id]); + if ($uid == 0) { + DBA::update('item', ['author-id' => $first], ['author-id' => $dup_id]); + DBA::update('item', ['owner-id' => $first], ['owner-id' => $dup_id]); + DBA::update('thread', ['author-id' => $first], ['author-id' => $dup_id]); + DBA::update('thread', ['owner-id' => $first], ['owner-id' => $dup_id]); + } else { + /// @todo Check if some other data needs to be adjusted as well, possibly the "rel" status? + } + + // Remove the duplicate + DBA::delete('contact', ['id' => $dup_id]); + } +}