]> git.mxchange.org Git - friendica.git/blobdiff - src/Model/Contact.php
Renamed function
[friendica.git] / src / Model / Contact.php
index df3efa0c57036567144ddf536b707e0a9ed0a6c7..eb99a9fe0f3a0a75ec4bfa85fc89c8941a50db54 100644 (file)
@@ -136,6 +136,31 @@ class Contact extends BaseObject
                return $contact;
        }
 
+       /**
+        * Insert a row into the contact table
+        * Important: You can't use DBA::lastInsertId() after this call since it will be set to 0.
+        *
+        * @param array        $param               parameter array
+        * @param bool         $on_duplicate_update Do an update on a duplicate entry
+        *
+        * @return boolean was the insert successful?
+        * @throws \Exception
+        */
+       public static function insert(array $param, bool $on_duplicate_update = false)
+       {
+               $ret = DBA::insert('contact', $param, $on_duplicate_update);
+               $contact = DBA::selectFirst('contact', ['nurl', 'uid'], ['id' => DBA::lastInsertId()]);
+               if (!DBA::isResult($contact)) {
+                       // Shouldn't happen
+                       return $ret;
+               }
+
+               // Search for duplicated contacts and get rid of them
+               self::removeDuplicates($contact['nurl'], $contact['uid']);
+
+               return $ret;
+       }
+
        /**
         * @param integer $id     Contact ID
         * @param array   $fields Array of selected fields, empty for all
@@ -860,8 +885,8 @@ class Contact extends BaseObject
                                 * delete, though if the owner tries to unarchive them we'll start
                                 * the whole process over again.
                                 */
-                               DBA::update('contact', ['archive' => 1], ['id' => $contact['id']]);
-                               DBA::update('contact', ['archive' => 1], ['nurl' => Strings::normaliseLink($contact['url']), 'self' => false]);
+                               DBA::update('contact', ['archive' => true], ['id' => $contact['id']]);
+                               DBA::update('contact', ['archive' => true], ['nurl' => Strings::normaliseLink($contact['url']), 'self' => false]);
                                GContact::updateFromPublicContactURL($contact['url']);
                        }
                }
@@ -878,6 +903,13 @@ class Contact extends BaseObject
         */
        public static function unmarkForArchival(array $contact)
        {
+               // Always unarchive the relay contact entry
+               if (!empty($contact['batch'])) {
+                       $fields = ['term-date' => DBA::NULL_DATETIME, 'archive' => false];
+                       $condition = ['batch' => $contact['batch'], 'contact-type' => self::TYPE_RELAY];
+                       DBA::update('contact', $fields, $condition);
+               }
+
                $condition = ['`id` = ? AND (`term-date` > ? OR `archive`)', $contact['id'], DBA::NULL_DATETIME];
                $exists = DBA::exists('contact', $condition);
 
@@ -899,13 +931,8 @@ class Contact extends BaseObject
                // It's a miracle. Our dead contact has inexplicably come back to life.
                $fields = ['term-date' => DBA::NULL_DATETIME, 'archive' => false];
                DBA::update('contact', $fields, ['id' => $contact['id']]);
-               DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($contact['url'])]);
+               DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($contact['url']), 'self' => false]);
                GContact::updateFromPublicContactURL($contact['url']);
-
-               if (!empty($contact['batch'])) {
-                       $condition = ['batch' => $contact['batch'], 'contact-type' => self::TYPE_RELAY];
-                       DBA::update('contact', $fields, $condition);
-               }
        }
 
        /**
@@ -1488,7 +1515,7 @@ class Contact extends BaseObject
                        if (!DBA::isResult($contact)) {
                                Logger::info('Create new contact', $fields);
 
-                               DBA::insert('contact', $fields);
+                               self::insert($fields);
 
                                // We intentionally aren't using lastInsertId here. There is a chance for duplicates.
                                $contact = DBA::selectFirst('contact', ['id'], $condition, ['order' => ['id']]);
@@ -1556,6 +1583,49 @@ class Contact extends BaseObject
                return $contact_id;
        }
 
+       /**
+        * @brief Checks if the contact is archived
+        *
+        * @param int $cid contact id
+        *
+        * @return boolean Is the contact archived?
+        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
+        */
+       public static function isArchived(int $cid)
+       {
+               if ($cid == 0) {
+                       return false;
+               }
+
+               $contact = DBA::selectFirst('contact', ['archive', 'url', 'batch'], ['id' => $cid]);
+               if (!DBA::isResult($contact)) {
+                       return false;
+               }
+
+               if ($contact['archive']) {
+                       return true;
+               }
+
+               // Check status of ActivityPub endpoints
+               $apcontact = APContact::getByURL($contact['url'], false);
+               if (!empty($apcontact)) {
+                       if (!empty($apcontact['inbox']) && DBA::exists('inbox-status', ['archive' => true, 'url' => $apcontact['inbox']])) {
+                               return true;
+                       }
+
+                       if (!empty($apcontact['sharedinbox']) && DBA::exists('inbox-status', ['archive' => true, 'url' => $apcontact['sharedinbox']])) {
+                               return true;
+                       }
+               }
+
+               // Check status of Diaspora endpoints
+               if (!empty($contact['batch'])) {
+                       return DBA::exists('contact', ['archive' => true, 'batch' => $contact['batch'], 'contact-type' => self::TYPE_RELAY]);
+                }
+
+               return false;
+       }
+
        /**
         * @brief Checks if the contact is blocked
         *
@@ -1795,16 +1865,22 @@ class Contact extends BaseObject
         /**
         * @brief Helper function for "updateFromProbe". Updates personal and public contact
         *
-        * @param array $contact The personal contact entry
-        * @param array $fields  The fields that are updated
+        * @param integer $id      contact id
+        * @param integer $uid     user id
+        * @param string  $url     The profile URL of the contact
+        * @param array   $fields  The fields that are updated
+        *
         * @throws \Exception
         */
        private static function updateContact($id, $uid, $url, array $fields)
        {
-               DBA::update('contact', $fields, ['id' => $id]);
+               if (!DBA::update('contact', $fields, ['id' => $id])) {
+                       Logger::info('Couldn\'t update contact.', ['id' => $id, 'fields' => $fields]);
+                       return;
+               }
 
                // Search for duplicated contacts and get rid of them
-               if (self::handleDuplicates(Strings::normaliseLink($url), $uid, $id) || ($uid != 0)) {
+               if (self::removeDuplicates(Strings::normaliseLink($url), $uid) || ($uid != 0)) {
                        return;
                }
 
@@ -1814,6 +1890,11 @@ class Contact extends BaseObject
                // Archive or unarchive the contact. We only need to do this for the public contact.
                // The archive/unarchive function will update the personal contacts by themselves.
                $contact = DBA::selectFirst('contact', [], ['id' => $id]);
+               if (!DBA::isResult($contact)) {
+                       Logger::info('Couldn\'t select contact for archival.', ['id' => $id]);
+                       return;
+               }
+
                if (!empty($fields['success_update'])) {
                        self::unmarkForArchival($contact);
                } elseif (!empty($fields['failure_update'])) {
@@ -1841,50 +1922,44 @@ class Contact extends BaseObject
        }
 
         /**
-        * @brief Helper function for "updateFromProbe". Remove duplicated contacts
+        * @brief Remove duplicated contacts
         *
         * @param string  $nurl  Normalised contact url
         * @param integer $uid   User id
-        * @param integer $id    Contact id of a duplicate
         * @return boolean
         * @throws \Exception
         */
-       private static function handleDuplicates($nurl, $uid, $id)
+       public static function removeDuplicates(string $nurl, int $uid)
        {
-               $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;
                }
 
                $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]);
+               Logger::info('Found duplicates', ['count' => $count, 'first' => $first, '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]);
-
-                       // 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]);
+                       if (!in_array($duplicate['network'], Protocol::FEDERATED)) {
+                               continue;
+                       }
 
-                       // 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 +2060,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;
@@ -2216,7 +2291,7 @@ class Contact extends BaseObject
                        $new_relation = (in_array($protocol, [Protocol::MAIL]) ? self::FRIEND : self::SHARING);
 
                        // create contact record
-                       DBA::insert('contact', [
+                       self::insert([
                                'uid'     => $uid,
                                'created' => DateTimeFormat::utcNow(),
                                'url'     => $ret['url'],
@@ -2377,7 +2452,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'])) {