]> git.mxchange.org Git - friendica.git/blobdiff - src/Model/Contact.php
Pagecache for frequently fetched pages
[friendica.git] / src / Model / Contact.php
index 5997b1569b1b56ffe05a04d0cf8ddecbe5cbe015..6a57693991127be745e836acf043a08ecec04ed9 100644 (file)
@@ -160,6 +160,7 @@ class Contact
                        $fields['created'] = DateTimeFormat::utcNow();
                }
 
+               $fields = DI::dbaDefinition()->truncateFieldsForTable('contact', $fields);
                DBA::insert('contact', $fields, $duplicate_mode);
                $contact = DBA::selectFirst('contact', [], ['id' => DBA::lastInsertId()]);
                if (!DBA::isResult($contact)) {
@@ -191,6 +192,7 @@ class Contact
         */
        public static function update(array $fields, array $condition, $old_fields = [])
        {
+               $fields = DI::dbaDefinition()->truncateFieldsForTable('contact', $fields);
                $ret = DBA::update('contact', $fields, $condition, $old_fields);
 
                // Apply changes to the "user-contact" table on dedicated fields
@@ -327,14 +329,15 @@ class Contact
        /**
         * Tests if the given contact is a follower
         *
-        * @param int $cid Either public contact id or user's contact id
-        * @param int $uid User ID
+        * @param int  $cid    Either public contact id or user's contact id
+        * @param int  $uid    User ID
+        * @param bool $strict If "true" then contact mustn't be set to pending or readonly
         *
         * @return boolean is the contact id a follower?
         * @throws HTTPException\InternalServerErrorException
         * @throws \ImagickException
         */
-       public static function isFollower(int $cid, int $uid): bool
+       public static function isFollower(int $cid, int $uid, bool $strict = false): bool
        {
                if (Contact\User::isBlocked($cid, $uid)) {
                        return false;
@@ -346,20 +349,24 @@ class Contact
                }
 
                $condition = ['id' => $cdata['user'], 'rel' => [self::FOLLOWER, self::FRIEND]];
+               if ($strict) {
+                       $condition = array_merge($condition, ['pending' => false, 'readonly' => false, 'blocked' => false]);
+               }
                return DBA::exists('contact', $condition);
        }
 
        /**
         * Tests if the given contact url is a follower
         *
-        * @param string $url Contact URL
-        * @param int    $uid User ID
+        * @param string $url    Contact URL
+        * @param int    $uid    User ID
+        * @param bool   $strict If "true" then contact mustn't be set to pending or readonly
         *
         * @return boolean is the contact id a follower?
         * @throws HTTPException\InternalServerErrorException
         * @throws \ImagickException
         */
-       public static function isFollowerByURL(string $url, int $uid): bool
+       public static function isFollowerByURL(string $url, int $uid, bool $strict = false): bool
        {
                $cid = self::getIdForURL($url, $uid);
 
@@ -367,20 +374,21 @@ class Contact
                        return false;
                }
 
-               return self::isFollower($cid, $uid);
+               return self::isFollower($cid, $uid, $strict);
        }
 
        /**
         * Tests if the given user shares with the given contact
         *
-        * @param int $cid Either public contact id or user's contact id
-        * @param int $uid User ID
+        * @param int  $cid    Either public contact id or user's contact id
+        * @param int  $uid    User ID
+        * @param bool $strict If "true" then contact mustn't be set to pending or readonly
         *
         * @return boolean is the contact sharing with given user?
         * @throws HTTPException\InternalServerErrorException
         * @throws \ImagickException
         */
-       public static function isSharing(int $cid, int $uid): bool
+       public static function isSharing(int $cid, int $uid, bool $strict = false): bool
        {
                if (Contact\User::isBlocked($cid, $uid)) {
                        return false;
@@ -392,20 +400,24 @@ class Contact
                }
 
                $condition = ['id' => $cdata['user'], 'rel' => [self::SHARING, self::FRIEND]];
+               if ($strict) {
+                       $condition = array_merge($condition, ['pending' => false, 'readonly' => false, 'blocked' => false]);
+               }
                return DBA::exists('contact', $condition);
        }
 
        /**
         * Tests if the given user follow the given contact url
         *
-        * @param string $url Contact URL
-        * @param int    $uid User ID
+        * @param string $url    Contact URL
+        * @param int    $uid    User ID
+        * @param bool   $strict If "true" then contact mustn't be set to pending or readonly
         *
         * @return boolean is the contact url being followed?
         * @throws HTTPException\InternalServerErrorException
         * @throws \ImagickException
         */
-       public static function isSharingByURL(string $url, int $uid): bool
+       public static function isSharingByURL(string $url, int $uid, bool $strict = false): bool
        {
                $cid = self::getIdForURL($url, $uid);
 
@@ -413,7 +425,7 @@ class Contact
                        return false;
                }
 
-               return self::isSharing($cid, $uid);
+               return self::isSharing($cid, $uid, $strict);
        }
 
        /**
@@ -703,23 +715,33 @@ class Contact
                }
 
                $file_suffix = 'jpg';
+               $url = DI::baseUrl() . '/profile/' . $user['nickname'];
+
+               $fields = [
+                       'name'         => $profile['name'],
+                       'nick'         => $user['nickname'],
+                       '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'],
+                       'matrix'       => $profile['matrix'],
+                       'network'      => Protocol::DFRN,
+                       'url'          => $url,
+                       // it seems as if ported accounts can have wrong values, so we make sure that now everything is fine.
+                       'nurl'         => Strings::normaliseLink($url),
+                       'uri-id'       => ItemURI::getIdByURI($url),
+                       'addr'         => $user['nickname'] . '@' . substr(DI::baseUrl(), strpos(DI::baseUrl(), '://') + 3),
+                       'request'      => DI::baseUrl() . '/dfrn_request/' . $user['nickname'],
+                       'notify'       => DI::baseUrl() . '/dfrn_notify/' . $user['nickname'],
+                       'poll'         => DI::baseUrl() . '/dfrn_poll/'. $user['nickname'],
+                       'confirm'      => DI::baseUrl() . '/dfrn_confirm/' . $user['nickname'],
+                       'poco'         => DI::baseUrl() . '/poco/' . $user['nickname'],
+               ];
 
-               $fields = ['name' => $profile['name'], 'nick' => $user['nickname'],
-                       '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'], 'matrix' => $profile['matrix'], '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'];
-               $fields['nurl'] = Strings::normaliseLink($fields['url']);
-               $fields['uri-id'] = ItemURI::getIdByURI($fields['url']);
-               $fields['addr'] = $user['nickname'] . '@' . substr(DI::baseUrl(), strpos(DI::baseUrl(), '://') + 3);
-               $fields['request'] = DI::baseUrl() . '/dfrn_request/' . $user['nickname'];
-               $fields['notify'] = DI::baseUrl() . '/dfrn_notify/' . $user['nickname'];
-               $fields['poll'] = DI::baseUrl() . '/dfrn_poll/'. $user['nickname'];
-               $fields['confirm'] = DI::baseUrl() . '/dfrn_confirm/' . $user['nickname'];
-               $fields['poco'] = DI::baseUrl() . '/poco/' . $user['nickname'];
 
                $avatar = Photo::selectFirst(['resource-id', 'type'], ['uid' => $uid, 'profile' => true]);
                if (DBA::isResult($avatar)) {
@@ -1031,7 +1053,6 @@ class Contact
                $pm_url = '';
                $status_link = '';
                $photos_link = '';
-               $poke_link = '';
 
                if ($uid == 0) {
                        $uid = local_user();
@@ -1074,10 +1095,6 @@ class Contact
                        $pm_url = DI::baseUrl() . '/message/new/' . $contact['id'];
                }
 
-               if (($contact['network'] == Protocol::DFRN) && !$contact['self'] && empty($contact['pending'])) {
-                       $poke_link = 'contact/' . $contact['id'] . '/poke';
-               }
-
                $contact_url = DI::baseUrl() . '/contact/' . $contact['id'];
 
                $posts_link = DI::baseUrl() . '/contact/' . $contact['id'] . '/conversations';
@@ -1112,7 +1129,6 @@ class Contact
                                'network' => [DI::l10n()->t('Network Posts') , $posts_link       , false],
                                'edit'    => [DI::l10n()->t('View Contact')  , $contact_url      , false],
                                'pm'      => [DI::l10n()->t('Send PM')       , $pm_url           , false],
-                               'poke'    => [DI::l10n()->t('Poke')          , $poke_link        , false],
                                'follow'  => [DI::l10n()->t('Connect/Follow'), $follow_link      , true],
                                'unfollow'=> [DI::l10n()->t('UnFollow')      , $unfollow_link    , true],
                        ];
@@ -1169,11 +1185,11 @@ class Contact
         * @throws HTTPException\InternalServerErrorException
         * @throws \ImagickException
         */
-       public static function getIdForURL(string $url, int $uid = 0, $update = null, array $default = []): int
+       public static function getIdForURL(string $url = null, int $uid = 0, $update = null, array $default = []): int
        {
                $contact_id = 0;
 
-               if ($url == '') {
+               if (empty($url)) {
                        Logger::notice('Empty url, quitting', ['url' => $url, 'user' => $uid, 'default' => $default]);
                        return 0;
                }
@@ -1283,7 +1299,7 @@ class Contact
                        }
 
                        if (!$contact_id) {
-                               Logger::info('Contact was not inserted', ['url' => $url, 'uid' => $uid]);
+                               Logger::warning('Contact was not inserted', ['url' => $url, 'uid' => $uid]);
                                return 0;
                        }
                } else {
@@ -2335,6 +2351,47 @@ class Contact
                return self::updateFromProbeArray($id, $ret);
        }
 
+       /**
+        * Checks if the given contact has got local data
+        *
+        * @param int   $id
+        * @param array $contact
+        *
+        * @return boolean
+        */
+       private static function hasLocalData(int $id, array $contact): bool
+       {
+               if (!empty($contact['uri-id']) && DBA::exists('contact', ["`uri-id` = ? AND `uid` != ?", $contact['uri-id'], 0])) {
+                       // User contacts with the same uri-id exist
+                       return true;
+               } elseif (DBA::exists('contact', ["`nurl` = ? AND `uid` != ?", Strings::normaliseLink($contact['url']), 0])) {
+                       // User contacts with the same nurl exists (compatibility mode for systems with missing uri-id values)
+                       return true;
+               }
+               if (DBA::exists('post-tag', ['cid' => $id])) {
+                       // Is tagged in a post
+                       return true;
+               }
+               if (DBA::exists('user-contact', ['cid' => $id])) {
+                       // Has got user-contact data
+                       return true;
+               }
+               if (Post::exists(['author-id' => $id])) {
+                       // Posts with this author exist
+                       return true;
+               }
+               if (Post::exists(['owner-id' => $id])) {
+                       // Posts with this owner exist
+                       return true;
+               }
+               if (Post::exists(['causer-id' => $id])) {
+                       // Posts with this causer exist
+                       return true;
+               }
+               // We don't have got this contact locally
+               return false;
+       }
+
        /**
         * Updates contact record by provided id and probed data
         *
@@ -2356,7 +2413,8 @@ class Contact
 
                $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', 'xmpp', 'matrix'];
+                       'network', 'alias', 'baseurl', 'gsid', 'forum', 'prv', 'contact-type', 'pubkey', 'last-item', 'xmpp', 'matrix',
+                       'created', 'last-update'];
                $contact = DBA::selectFirst('contact', $fields, ['id' => $id]);
                if (!DBA::isResult($contact)) {
                        return false;
@@ -2389,14 +2447,34 @@ class Contact
                $pubkey = $contact['pubkey'];
                unset($contact['pubkey']);
 
+               $created = $contact['created'];
+               unset($contact['created']);
+
+               $last_update = $contact['last-update'];
+               unset($contact['last-update']);
+
                $contact['photo'] = $contact['avatar'];
                unset($contact['avatar']);
 
                $updated = DateTimeFormat::utcNow();
 
+               $has_local_data = self::hasLocalData($id, $contact);
+
+               if (!in_array($ret['network'], array_merge(Protocol::FEDERATED, [Protocol::ZOT, Protocol::PHANTOM]))) {
+                       // Periodical checks are only done on federated contacts
+                       $failed_next_update  = null;
+                       $success_next_update = null;
+               } elseif ($has_local_data) {
+                       $failed_next_update  = GServer::getNextUpdateDate(false, $created, $last_update, !in_array($contact['network'], Protocol::FEDERATED));
+                       $success_next_update = GServer::getNextUpdateDate(true, $created, $last_update, !in_array($contact['network'], Protocol::FEDERATED));
+               } else {
+                       $failed_next_update  = DateTimeFormat::utc('now +6 month');
+                       $success_next_update = DateTimeFormat::utc('now +1 month');
+               }
+
                if (Strings::normaliseLink($contact['url']) != Strings::normaliseLink($ret['url'])) {
                        Logger::notice('New URL differs from old URL', ['id' => $id, 'uid' => $uid, 'old' => $contact['url'], 'new' => $ret['url']]);
-                       self::updateContact($id, $uid, $contact['url'], $ret['url'], ['failed' => true, 'last-update' => $updated, 'failure_update' => $updated]);
+                       self::updateContact($id, $uid, $contact['url'], $ret['url'], ['failed' => true, 'local-data' => $has_local_data, 'last-update' => $updated, 'next-update' => $failed_next_update, 'failure_update' => $updated]);
                        return false;
                }
 
@@ -2404,14 +2482,14 @@ class Contact
                // We check after the probing to be able to correct falsely detected contact types.
                if (($contact['contact-type'] == self::TYPE_RELAY) &&
                        (!Strings::compareLink($ret['url'], $contact['url']) || in_array($ret['network'], [Protocol::FEED, Protocol::PHANTOM]))) {
-                       self::updateContact($id, $uid, $contact['url'], $contact['url'], ['failed' => false, 'last-update' => $updated, 'success_update' => $updated]);
+                       self::updateContact($id, $uid, $contact['url'], $contact['url'], ['failed' => false, 'local-data' => $has_local_data, 'last-update' => $updated, 'next-update' => $success_next_update, 'success_update' => $updated]);
                        Logger::info('Not updating relais', ['id' => $id, 'url' => $contact['url']]);
                        return true;
                }
 
                // If Probe::uri fails the network code will be different ("feed" or "unkn")
                if (($ret['network'] == Protocol::PHANTOM) || (($ret['network'] == Protocol::FEED) && ($ret['network'] != $contact['network']))) {
-                       self::updateContact($id, $uid, $contact['url'], $ret['url'], ['failed' => true, 'last-update' => $updated, 'failure_update' => $updated]);
+                       self::updateContact($id, $uid, $contact['url'], $ret['url'], ['failed' => true, 'local-data' => $has_local_data, 'last-update' => $updated, 'next-update' => $failed_next_update, 'failure_update' => $updated]);
                        return false;
                }
 
@@ -2439,7 +2517,7 @@ class Contact
 
                $new_pubkey = $ret['pubkey'] ?? '';
 
-               if ($uid == 0) {
+               if ($uid == 0 && DI::config()->get('system', 'fetch_featured_posts')) {
                        if ($ret['network'] == Protocol::ACTIVITYPUB) {
                                $apcontact = APContact::getByURL($ret['url'], false);
                                if (!empty($apcontact['featured'])) {
@@ -2483,7 +2561,7 @@ class Contact
                $uriid = ItemURI::insert(['uri' => $ret['url'], 'guid' => $guid]);
 
                if (!$update) {
-                       self::updateContact($id, $uid, $contact['url'], $ret['url'], ['failed' => false, 'last-update' => $updated, 'success_update' => $updated]);
+                       self::updateContact($id, $uid, $contact['url'], $ret['url'], ['failed' => false, 'local-data' => $has_local_data, 'last-update' => $updated, 'next-update' => $success_next_update, 'success_update' => $updated]);
 
                        if (Contact\Relation::isDiscoverable($ret['url'])) {
                                Worker::add(PRIORITY_LOW, 'ContactDiscovery', $ret['url']);
@@ -2500,10 +2578,12 @@ class Contact
                        return true;
                }
 
-               $ret['uri-id']  = $uriid;
-               $ret['nurl']    = Strings::normaliseLink($ret['url']);
-               $ret['updated'] = $updated;
-               $ret['failed']  = false;
+               $ret['uri-id']      = $uriid;
+               $ret['nurl']        = Strings::normaliseLink($ret['url']);
+               $ret['updated']     = $updated;
+               $ret['failed']      = false;
+               $ret['next-update'] = $success_next_update;
+               $ret['local-data']  = $has_local_data;
 
                // Only fill the pubkey if it had been empty before. We have to prevent identity theft.
                if (empty($pubkey) && !empty($new_pubkey)) {
@@ -3241,7 +3321,7 @@ class Contact
                                continue;
                        }
                        $contact = self::getByURL($url, false, ['id', 'updated']);
-                       if (empty($contact['id'])) {
+                       if (empty($contact['id']) && Network::isValidHttpUrl($url)) {
                                Worker::add(PRIORITY_LOW, 'AddContact', 0, $url);
                                ++$added;
                        } elseif ($contact['updated'] < DateTimeFormat::utc('now -7 days')) {