]> git.mxchange.org Git - friendica.git/blobdiff - src/Model/Contact.php
Merge pull request #10478 from annando/notice
[friendica.git] / src / Model / Contact.php
index cd4e608eae9a618d1d933d4c0de46a535dab97a4..0184861c3c2d8403750bb075d5c69a03781cd43c 100644 (file)
@@ -185,6 +185,8 @@ class Contact
                        $fields['gsid'] = GServer::getID($fields['baseurl'], true);
                }
 
+               $fields['uri-id'] = ItemURI::getIdByURI($fields['url']);
+
                if (empty($fields['created'])) {
                        $fields['created'] = DateTimeFormat::utcNow();
                }
@@ -623,29 +625,30 @@ class Contact
         *
         * @param int     $uid
         * @param boolean $update_avatar Force the avatar update
+        * @return bool   "true" if updated
         * @throws HTTPException\InternalServerErrorException
         */
        public static function updateSelfFromUserID($uid, $update_avatar = false)
        {
                $fields = ['id', 'name', 'nick', 'location', 'about', 'keywords', 'avatar', 'prvkey', 'pubkey',
                        'xmpp', 'contact-type', 'forum', 'prv', 'avatar-date', 'url', 'nurl', 'unsearchable',
-                       'photo', 'thumb', 'micro', 'addr', 'request', 'notify', 'poll', 'confirm', 'poco'];
+                       'photo', 'thumb', 'micro', 'addr', 'request', 'notify', 'poll', 'confirm', 'poco', 'network'];
                $self = DBA::selectFirst('contact', $fields, ['uid' => $uid, 'self' => true]);
                if (!DBA::isResult($self)) {
-                       return;
+                       return false;
                }
 
                $fields = ['nickname', 'page-flags', 'account-type', 'prvkey', 'pubkey'];
                $user = DBA::selectFirst('user', $fields, ['uid' => $uid, 'account_expired' => false]);
                if (!DBA::isResult($user)) {
-                       return;
+                       return false;
                }
 
                $fields = ['name', 'photo', 'thumb', 'about', 'address', 'locality', 'region',
                        'country-name', 'pub_keywords', 'xmpp', 'net-publish'];
                $profile = DBA::selectFirst('profile', $fields, ['uid' => $uid]);
                if (!DBA::isResult($profile)) {
-                       return;
+                       return false;
                }
 
                $file_suffix = 'jpg';
@@ -654,7 +657,7 @@ class Contact
                        'avatar-date' => $self['avatar-date'], 'location' => Profile::formatLocation($profile),
                        'about' => $profile['about'], 'keywords' => $profile['pub_keywords'],
                        'contact-type' => $user['account-type'], 'prvkey' => $user['prvkey'],
-                       'pubkey' => $user['pubkey'], 'xmpp' => $profile['xmpp']];
+                       'pubkey' => $user['pubkey'], 'xmpp' => $profile['xmpp'], 'network' => Protocol::DFRN];
 
                // it seems as if ported accounts can have wrong values, so we make sure that now everything is fine.
                $fields['url'] = DI::baseUrl() . '/profile/' . $user['nickname'];
@@ -715,6 +718,8 @@ class Contact
                        DBA::update('contact', $fields, ['id' => $self['id']]);
 
                        // Update the public contact as well
+                       $fields['prvkey'] = null;
+                       $fields['self']   = false;
                        DBA::update('contact', $fields, ['uid' => 0, 'nurl' => $self['nurl']]);
 
                        // Update the profile
@@ -722,6 +727,8 @@ class Contact
                                'thumb' => DI::baseUrl() . '/photo/avatar/' . $uid .'.' . $file_suffix];
                        DBA::update('profile', $fields, ['uid' => $uid]);
                }
+
+               return $update;
        }
 
        /**
@@ -1065,12 +1072,12 @@ class Contact
                        return 0;
                }
 
-               $contact = self::getByURL($url, false, ['id', 'network'], $uid);
+               $contact = self::getByURL($url, false, ['id', 'network', 'uri-id'], $uid);
 
                if (!empty($contact)) {
                        $contact_id = $contact["id"];
 
-                       if (empty($update)) {
+                       if (empty($update) && (!empty($contact['uri-id']) || is_bool($update))) {
                                Logger::debug('Contact found', ['url' => $url, 'uid' => $uid, 'update' => $update, 'cid' => $contact_id]);
                                return $contact_id;
                        }
@@ -1098,7 +1105,7 @@ class Contact
                if (($uid == 0) && (empty($data['network']) || ($data['network'] == Protocol::PHANTOM))) {
                        // Fetch data for the public contact via the first found personal contact
                        /// @todo Check if this case can happen at all (possibly with mail accounts?)
-                       $fields = ['name', 'nick', 'url', 'addr', 'alias', 'avatar', 'contact-type',
+                       $fields = ['name', 'nick', 'url', 'addr', 'alias', 'avatar', 'header', 'contact-type',
                                'keywords', 'location', 'about', 'unsearchable', 'batch', 'notify', 'poll',
                                'request', 'confirm', 'poco', 'subscribe', 'network', 'baseurl', 'gsid'];
 
@@ -1492,67 +1499,46 @@ class Contact
         * @param bool  $no_update Don't perfom an update if no cached avatar was found
         * @return string photo path
         */
-       private static function getAvatarPath(array $contact, string $field, string $size, string $avatar, $no_update = false)
+       private static function getAvatarPath(array $contact, string $size, $no_update = false)
        {
-               if (!empty($contact)) {
-                       $contact = self::checkAvatarCacheByArray($contact, $no_update);
-                       if (!empty($contact[$field])) {
-                               $avatar = $contact[$field];
-                       }
-               }
-
-               if ($no_update && empty($avatar) && !empty($contact['avatar'])) {
-                       $avatar = $contact['avatar'];
-               }
-
-               if (empty($avatar)) {
-                       $avatar = self::getDefaultAvatar([], $size);
-               }
-
-               if (Proxy::isLocalImage($avatar)) {
-                       return $avatar;
-               } else {
-                       return Proxy::proxifyUrl($avatar, false, $size);
-               }
+               $contact = self::checkAvatarCacheByArray($contact, $no_update);
+               return self::getAvatarUrlForId($contact['id'], $size, $contact['updated'] ?? '');
        }
 
        /**
         * Return the photo path for a given contact array
         *
         * @param array  $contact   Contact array
-        * @param string $avatar    Avatar path that is displayed when no photo had been found
         * @param bool   $no_update Don't perfom an update if no cached avatar was found
         * @return string photo path
         */
-       public static function getPhoto(array $contact, string $avatar = '', bool $no_update = false)
+       public static function getPhoto(array $contact, bool $no_update = false)
        {
-               return self::getAvatarPath($contact, 'photo', Proxy::SIZE_SMALL, $avatar, $no_update);
+               return self::getAvatarPath($contact, Proxy::SIZE_SMALL, $no_update);
        }
 
        /**
         * Return the photo path (thumb size) for a given contact array
         *
         * @param array  $contact   Contact array
-        * @param string $avatar    Avatar path that is displayed when no photo had been found
         * @param bool   $no_update Don't perfom an update if no cached avatar was found
         * @return string photo path
         */
-       public static function getThumb(array $contact, string $avatar = '', bool $no_update = false)
+       public static function getThumb(array $contact, bool $no_update = false)
        {
-               return self::getAvatarPath($contact, 'thumb', Proxy::SIZE_THUMB, $avatar, $no_update);
+               return self::getAvatarPath($contact, Proxy::SIZE_THUMB, $no_update);
        }
 
        /**
         * Return the photo path (micro size) for a given contact array
         *
         * @param array  $contact   Contact array
-        * @param string $avatar    Avatar path that is displayed when no photo had been found
         * @param bool   $no_update Don't perfom an update if no cached avatar was found
         * @return string photo path
         */
-       public static function getMicro(array $contact, string $avatar = '', bool $no_update = false)
+       public static function getMicro(array $contact, bool $no_update = false)
        {
-               return self::getAvatarPath($contact, 'micro', Proxy::SIZE_MICRO, $avatar, $no_update);
+               return self::getAvatarPath($contact, Proxy::SIZE_MICRO, $no_update);
        }
 
        /**
@@ -1609,7 +1595,7 @@ class Contact
         *
         * @param array $contact  contact array
         * @param string $size    Size of the avatar picture
-        * @return void
+        * @return string avatar URL
         */
        public static function getDefaultAvatar(array $contact, string $size)
        {
@@ -1657,6 +1643,99 @@ class Contact
                return DI::baseUrl() . $default;
        }
 
+       /**
+        * Get avatar link for given contact id
+        *
+        * @param integer $cid     contact id
+        * @param string  $size    One of the ProxyUtils::SIZE_* constants
+        * @param string  $updated Contact update date
+        * @return string avatar link
+        */
+       public static function getAvatarUrlForId(int $cid, string $size = '', string $updated = ''):string
+       {
+               // We have to fetch the "updated" variable when it wasn't provided
+               // The parameter can be provided to improve performance
+               if (empty($updated)) {
+                       $contact = self::getById($cid, ['updated']);
+                       $updated = $contact['updated'] ?? '';
+               }
+
+               $url = DI::baseUrl() . '/photo/contact/';
+               switch ($size) {
+                       case Proxy::SIZE_MICRO:
+                               $url .= Proxy::PIXEL_MICRO . '/';
+                               break;
+                       case Proxy::SIZE_THUMB:
+                               $url .= Proxy::PIXEL_THUMB . '/';
+                               break;
+                       case Proxy::SIZE_SMALL:
+                               $url .= Proxy::PIXEL_SMALL . '/';
+                               break;
+                       case Proxy::SIZE_MEDIUM:
+                               $url .= Proxy::PIXEL_MEDIUM . '/';
+                               break;
+                       case Proxy::SIZE_LARGE:
+                               $url .= Proxy::PIXEL_LARGE . '/';
+                               break;
+               }
+               return $url . $cid . ($updated ? '?ts=' . strtotime($updated) : '');
+       }
+
+       /**
+        * Get avatar link for given contact URL
+        *
+        * @param string  $url  contact url
+        * @param integer $uid  user id
+        * @param string  $size One of the ProxyUtils::SIZE_* constants
+        * @return string avatar link
+        */
+       public static function getAvatarUrlForUrl(string $url, int $uid, string $size = ''):string
+       {
+               $condition = ["`nurl` = ? AND ((`uid` = ? AND `network` IN (?, ?)) OR `uid` = ?)",
+                       Strings::normaliseLink($url), $uid, Protocol::FEED, Protocol::MAIL, 0];
+               $contact = self::selectFirst(['id', 'updated'], $condition);
+               return self::getAvatarUrlForId($contact['id'] ?? 0, $size, $contact['updated'] ?? '');
+       }
+
+       /**
+        * Get header link for given contact id
+        *
+        * @param integer $cid     contact id
+        * @param string  $size    One of the ProxyUtils::SIZE_* constants
+        * @param string  $updated Contact update date
+        * @return string header link
+        */
+       public static function getHeaderUrlForId(int $cid, string $size = '', string $updated = ''):string
+       {
+               // We have to fetch the "updated" variable when it wasn't provided
+               // The parameter can be provided to improve performance
+               if (empty($updated)) {
+                       $contact = self::getById($cid, ['updated']);
+                       $updated = $contact['updated'] ?? '';
+               }
+
+               $url = DI::baseUrl() . '/photo/header/';
+               switch ($size) {
+                       case Proxy::SIZE_MICRO:
+                               $url .= Proxy::PIXEL_MICRO . '/';
+                               break;
+                       case Proxy::SIZE_THUMB:
+                               $url .= Proxy::PIXEL_THUMB . '/';
+                               break;
+                       case Proxy::SIZE_SMALL:
+                               $url .= Proxy::PIXEL_SMALL . '/';
+                               break;
+                       case Proxy::SIZE_MEDIUM:
+                               $url .= Proxy::PIXEL_MEDIUM . '/';
+                               break;
+                       case Proxy::SIZE_LARGE:
+                               $url .= Proxy::PIXEL_LARGE . '/';
+                               break;
+               }
+
+               return $url . $cid . ($updated ? '?ts=' . strtotime($updated) : '');
+       }
+
        /**
         * Updates the avatar links in a contact only if needed
         *
@@ -1809,7 +1888,7 @@ class Contact
        {
                if (Strings::normaliseLink($new_url) != Strings::normaliseLink($old_url)) {
                        Logger::notice('New URL differs from old URL', ['old' => $old_url, 'new' => $new_url]);
-                       // @todo It is to decide what to do when the URL is changed
+                       return;
                }
 
                if (!DBA::update('contact', $fields, ['id' => $id])) {
@@ -1944,8 +2023,8 @@ class Contact
                // These fields aren't updated by this routine:
                // 'xmpp', 'sensitive'
 
-               $fields = ['uid', 'avatar', 'name', 'nick', 'location', 'keywords', 'about', 'subscribe', 'manually-approve',
-                       'unsearchable', 'url', 'addr', 'batch', 'notify', 'poll', 'request', 'confirm', 'poco',
+               $fields = ['uid', 'uri-id', 'avatar', 'header', 'name', 'nick', 'location', 'keywords', 'about', 'subscribe',
+                       'manually-approve', 'unsearchable', 'url', 'addr', 'batch', 'notify', 'poll', 'request', 'confirm', 'poco',
                        'network', 'alias', 'baseurl', 'gsid', 'forum', 'prv', 'contact-type', 'pubkey', 'last-item'];
                $contact = DBA::selectFirst('contact', $fields, ['id' => $id]);
                if (!DBA::isResult($contact)) {
@@ -1953,7 +2032,11 @@ class Contact
                }
 
                if (self::isLocal($ret['url'])) {
-                       Logger::info('Local contacts are not updated here.');
+                       if ($contact['uid'] == 0) {
+                               Logger::info('Local contacts are not updated here.');
+                       } else {
+                               self::updateFromPublicContact($id, $contact);
+                       }
                        return true;
                }
 
@@ -1969,6 +2052,9 @@ class Contact
                $uid = $contact['uid'];
                unset($contact['uid']);
 
+               $uriid = $contact['uri-id'];
+               unset($contact['uri-id']);
+
                $pubkey = $contact['pubkey'];
                unset($contact['pubkey']);
 
@@ -1992,6 +2078,14 @@ class Contact
                        return false;
                }
 
+               if (Strings::normaliseLink($ret['url']) != Strings::normaliseLink($contact['url'])) {
+                       $cid = self::getIdForURL($ret['url']);
+                       if (!empty($cid) && ($cid != $id)) {
+                               Logger::notice('URL of contact changed.', ['id' => $id, 'new_id' => $cid, 'old' => $contact['url'], 'new' => $ret['url']]);
+                               return self::updateFromProbeArray($cid, $ret);
+                       }
+               }
+
                if (isset($ret['hide']) && is_bool($ret['hide'])) {
                        $ret['unsearchable'] = $ret['hide'];
                }
@@ -2014,6 +2108,7 @@ class Contact
                }
 
                $update = false;
+               $guid = $ret['guid'] ?? '';
 
                // make sure to not overwrite existing values with blank entries except some technical fields
                $keep = ['batch', 'notify', 'poll', 'request', 'confirm', 'poco', 'baseurl'];
@@ -2033,6 +2128,10 @@ class Contact
                        unset($ret['last-item']);
                }
 
+               if (empty($uriid)) {
+                       $update = true;
+               }
+
                if (!empty($ret['photo']) && ($ret['network'] != Protocol::FEED)) {
                        self::updateAvatar($id, $ret['photo'], $update);
                }
@@ -2055,9 +2154,15 @@ class Contact
                        return true;
                }
 
-               $ret['nurl'] = Strings::normaliseLink($ret['url']);
+               if (empty($guid)) {
+                       $ret['uri-id'] = ItemURI::getIdByURI($ret['url']);
+               } else {
+                       $ret['uri-id'] = ItemURI::insert(['uri' => $ret['url'], 'guid' => $guid]);
+               }
+
+               $ret['nurl']    = Strings::normaliseLink($ret['url']);
                $ret['updated'] = $updated;
-               $ret['failed'] = false;
+               $ret['failed']  = false;
 
                // Only fill the pubkey if it had been empty before. We have to prevent identity theft.
                if (empty($pubkey) && !empty($new_pubkey)) {
@@ -2088,6 +2193,26 @@ class Contact
                return true;
        }
 
+       private static function updateFromPublicContact(int $id, array $contact)
+       {
+               $public = self::getByURL($contact['url'], false);
+
+               $fields = [];
+
+               foreach ($contact as $field => $value) {
+                       if ($field == 'uid') {
+                               continue;
+                       }
+                       if ($public[$field] != $value) {
+                               $fields[$field] = $public[$field];
+                       }
+               }
+               if (!empty($fields)) {
+                       DBA::update('contact', $fields, ['id' => $id, 'self' => false]);
+                       Logger::info('Updating local contact', ['id' => $id]);
+               }
+       }
+
        /**
         * @param integer $url contact url
         * @return integer Contact id
@@ -2196,8 +2321,10 @@ class Contact
                }
 
                if (!empty($arr['contact']['name'])) {
+                       $probed = false;
                        $ret = $arr['contact'];
                } else {
+                       $probed = true;                 
                        $ret = Probe::uri($url, $network, $user['uid']);
                }
 
@@ -2341,6 +2468,10 @@ class Contact
                // pull feed and consume it, which should subscribe to the hub.
                if ($contact['network'] == Protocol::OSTATUS) {
                        Worker::add(PRIORITY_HIGH, 'OnePoll', $contact_id, 'force');
+               }
+
+               if ($probed) {
+                       self::updateFromProbeArray($contact_id, $ret);
                } else {
                        Worker::add(PRIORITY_HIGH, 'UpdateContact', $contact_id);
                }
@@ -2534,6 +2665,8 @@ class Contact
                        // Ensure to always have the correct network type, independent from the connection request method
                        self::updateFromProbe($contact['id']);
 
+                       Post\UserNotification::insertNotication($contact['id'], Verb::getID(Activity::FOLLOW), $importer['uid']);
+
                        return true;
                } else {
                        // send email notification to owner?
@@ -2565,6 +2698,8 @@ class Contact
 
                        self::updateAvatar($contact_id, $photo, true);
 
+                       Post\UserNotification::insertNotication($contact_id, Verb::getID(Activity::FOLLOW), $importer['uid']);
+
                        $contact_record = DBA::selectFirst('contact', ['id', 'network', 'name', 'url', 'photo'], ['id' => $contact_id]);
 
                        /// @TODO Encapsulate this into a function/method
@@ -2857,7 +2992,8 @@ class Contact
                $search .= '%';
 
                $results = DBA::p("SELECT * FROM `contact`
-                       WHERE NOT `unsearchable` AND `network` IN (?, ?, ?, ?) AND
+                       WHERE (NOT `unsearchable` OR `nurl` IN (SELECT `nurl` FROM `owner-view` where `publish` OR `net-publish`))
+                               AND `network` IN (?, ?, ?, ?) AND
                                NOT `failed` AND `uid` = ? AND
                                (`addr` LIKE ? OR `name` LIKE ? OR `nick` LIKE ?) $extra_sql
                                ORDER BY `nurl` DESC LIMIT 1000",