]> git.mxchange.org Git - friendica.git/commitdiff
Move unfollow protocol to worker task
authorHypolite Petovan <hypolite@mrpetovan.com>
Sun, 17 Oct 2021 01:24:34 +0000 (21:24 -0400)
committerHypolite Petovan <hypolite@mrpetovan.com>
Thu, 24 Feb 2022 14:35:54 +0000 (09:35 -0500)
- Add uid parameter to unfollow hook
- Rework and unify Contact::unfollow, Contact::removeSharer and Contact::terminateFriendship
- Add missing twitterUser property to Twitter\Friendships\Destroy module

doc/Addons.md
mod/unfollow.php
src/Console/Contact.php
src/Core/Protocol.php
src/Model/Contact.php
src/Module/Api/Mastodon/Accounts/Block.php
src/Module/Api/Mastodon/Accounts/Unfollow.php
src/Module/Api/Twitter/Friendships/Destroy.php
src/Protocol/DFRN.php
src/Worker/Contact/Unfollow.php [new file with mode: 0644]
src/Worker/Notifier.php

index 578cffe7ca3df0df77f01508274556376d606c73..504ab43984b5231317db48b0e64901a3b1c600af 100644 (file)
@@ -626,7 +626,8 @@ Hook data:
 Called when unfollowing a remote contact on a non-native network (like Twitter)
 
 Hook data:
-- **contact** (input): the remote contact (uid = local unfollowing user id) array.
+- **contact** (input): the target public contact (uid = 0) array.
+- **uid** (input): the id of the source local user.
 - **result** (output): wether the unfollowing is successful or not.
 
 ### revoke_follow
index 0aa8a87b50865d3bc9715fe6d1d334d32a3f3704..10830bd103d29c51fb0d6f0bfb1bfa862e58bc17 100644 (file)
@@ -122,8 +122,7 @@ function unfollow_process(string $url)
 
        $owner = User::getOwnerDataById($uid);
        if (!$owner) {
-               (new \Friendica\Module\Security\Logout())->init();
-               // NOTREACHED
+               throw new \Friendica\Network\HTTPException\NotFoundException();
        }
 
        $condition = ["`uid` = ? AND (`rel` = ? OR `rel` = ?) AND (`nurl` = ? OR `alias` = ? OR `alias` = ?)",
@@ -140,15 +139,10 @@ function unfollow_process(string $url)
        $return_path = $base_return_path . '/' . $contact['id'];
 
        try {
-               $result = Contact::terminateFriendship($owner, $contact);
-
-               if ($result === false) {
-                       $notice_message = DI::l10n()->t('Unable to unfollow this contact, please retry in a few minutes or contact your administrator.');
-               } else {
-                       $notice_message = DI::l10n()->t('Contact was successfully unfollowed');
-               }
+               Contact::unfollow($contact);
+               $notice_message = DI::l10n()->t('Contact was successfully unfollowed');
        } catch (Exception $e) {
-               DI::logger()->error($e->getMessage(), ['owner' => $owner, 'contact' => $contact]);
+               DI::logger()->error($e->getMessage(), ['contact' => $contact]);
                $notice_message = DI::l10n()->t('Unable to unfollow this contact, please contact your administrator');
        }
 
index 11f7f87ced01f35045b1803d7061d826e34981e8..f051d870a295c8710a72f21a7b7c57992fc3f486 100644 (file)
@@ -199,19 +199,18 @@ HELP;
                        throw new RuntimeException('Contact not found');
                }
 
-               $user = UserModel::getById($contact['uid']);
+               if (empty($contact['uid'])) {
+                       throw new RuntimeException('Contact must be user-specific (uid != 0)');
+               }
 
                try {
-                       $result = ContactModel::terminateFriendship($user, $contact);
-                       if ($result === false) {
-                               throw new RuntimeException('Unable to unfollow this contact, please retry in a few minutes or check the logs.');
-                       }
+                       ContactModel::unfollow($contact);
 
                        $this->out('Contact was successfully unfollowed');
 
                        return true;
                } catch (\Exception $e) {
-                       DI::logger()->error($e->getMessage(), ['owner' => $user, 'contact' => $contact]);
+                       DI::logger()->error($e->getMessage(), ['contact' => $contact]);
                        throw new RuntimeException('Unable to unfollow this contact, please check the log');
                }
        }
index 1b26265255422325f0651e5ed780a881f5ff1978..47d4b4539f2311cbbddded8ea4de73db9660b278 100644 (file)
@@ -171,15 +171,15 @@ class Protocol
        }
 
        /**
-        * Sends an unfriend message. Does not remove the contact
+        * Sends an unfollow message. Does not remove the contact
         *
-        * @param array   $user    User unfriending
-        * @param array   $contact Contact unfriended
+        * @param array $contact Target public contact (uid = 0) array
+        * @param array $user    Source local user array
         * @return bool|null true if successful, false if not, null if no remote action was performed
         * @throws HTTPException\InternalServerErrorException
         * @throws \ImagickException
         */
-       public static function terminateFriendship(array $user, array $contact): ?bool
+       public static function unfollow(array $contact, array $user): ?bool
        {
                if (empty($contact['network'])) {
                        throw new \InvalidArgumentException('Missing network key in contact array');
@@ -216,7 +216,8 @@ class Protocol
                // Catch-all hook for connector addons
                $hook_data = [
                        'contact' => $contact,
-                       'result' => null
+                       'uid'     => $user['uid'],
+                       'result'  => null,
                ];
                Hook::callAll('unfollow', $hook_data);
 
index 9b0020d009bd2209c12325d6ac5014ea0c303c61..99b1c0971f67dd31e3a282d5726a59cc4620a160 100644 (file)
@@ -813,26 +813,28 @@ class Contact
        }
 
        /**
-        * Sends an unfriend message. Removes the contact for two-way unfriending or sharing only protocols (feed an mail)
+        * Unfollow the remote contact
         *
-        * @param array   $user    User unfriending
-        * @param array   $contact Contact (uid != 0) unfriended
-        * @param boolean $two_way Revoke eventual inbound follow as well
-        * @return bool|null true if successful, false if not, null if no remote action was performed
+        * @param array $contact Target user-specific contact (uid != 0) array
         * @throws HTTPException\InternalServerErrorException
         * @throws \ImagickException
         */
-       public static function terminateFriendship(array $user, array $contact): ?bool
+       public static function unfollow(array $contact): void
        {
-               $result = Protocol::terminateFriendship($user, $contact);
+               if (empty($contact['network'])) {
+                       throw new \InvalidArgumentException('Empty network in contact array');
+               }
 
-               if ($contact['rel'] == Contact::SHARING || in_array($contact['network'], [Protocol::FEED, Protocol::MAIL])) {
-                       self::remove($contact['id']);
-               } else {
-                       self::update(['rel' => Contact::FOLLOWER], ['id' => $contact['id']]);
+               if (empty($contact['uid'])) {
+                       throw new \InvalidArgumentException('Unexpected public contact record');
                }
 
-               return $result;
+               if (in_array($contact['rel'], [self::SHARING, self::FRIEND])) {
+                       $cdata = Contact::getPublicAndUserContactID($contact['id'], $contact['uid']);
+                       Worker::add(PRIORITY_HIGH, 'Contact\Unfollow', $cdata['public'], $contact['uid']);
+               }
+
+               self::removeSharer($contact);
        }
 
        /**
@@ -868,6 +870,36 @@ class Contact
                return $result;
        }
 
+       /**
+        * Completely severs a relationship with a contact
+        *
+        * @param array $contact User-specific contact (uid != 0) array
+        * @throws HTTPException\InternalServerErrorException
+        * @throws \ImagickException
+        */
+       public static function terminateFriendship(array $contact)
+       {
+               if (empty($contact['network'])) {
+                       throw new \InvalidArgumentException('Empty network in contact array');
+               }
+
+               if (empty($contact['uid'])) {
+                       throw new \InvalidArgumentException('Unexpected public contact record');
+               }
+
+               $cdata = Contact::getPublicAndUserContactID($contact['id'], $contact['uid']);
+
+               if (in_array($contact['rel'], [self::SHARING, self::FRIEND])) {
+                       Worker::add(PRIORITY_HIGH, 'Contact\Unfollow', $cdata['public'], $contact['uid']);
+               }
+
+               if (in_array($contact['rel'], [self::FOLLOWER, self::FRIEND])) {
+                       Worker::add(PRIORITY_HIGH, 'Contact\RevokeFollow', $cdata['public'], $contact['uid']);
+               }
+
+               self::remove($contact['id']);
+       }
+
 
        /**
         * Marks a contact for archival after a communication issue delay
@@ -2574,28 +2606,6 @@ class Contact
                return $result;
        }
 
-       /**
-        * Unfollow a contact
-        *
-        * @param int $cid Public contact id
-        * @param int $uid  User ID
-        *
-        * @return bool "true" if unfollowing had been successful
-        */
-       public static function unfollow(int $cid, int $uid)
-       {
-               $cdata = self::getPublicAndUserContactID($cid, $uid);
-               if (empty($cdata['user'])) {
-                       return false;
-               }
-
-               $contact = self::getById($cdata['user']);
-
-               self::removeSharer([], $contact);
-
-               return true;
-       }
-
        /**
         * @param array  $importer Owner (local user) data
         * @param array  $contact  Existing owner-specific contact data we want to expand the relationship with. Optional.
@@ -2755,12 +2765,19 @@ class Contact
                }
        }
 
-       public static function removeSharer($importer, $contact)
+       /**
+        * Update the local relationship when a local user unfollow a contact.
+        * Removes the contact for sharing-only protocols (feed and mail).
+        *
+        * @param array $contact User-specific contact (uid != 0) array
+        * @throws HTTPException\InternalServerErrorException
+        */
+       public static function removeSharer(array $contact)
        {
-               if (($contact['rel'] == self::FRIEND) || ($contact['rel'] == self::FOLLOWER)) {
-                       self::update(['rel' => self::FOLLOWER], ['id' => $contact['id']]);
-               } else {
+               if ($contact['rel'] == self::SHARING || in_array($contact['network'], [Protocol::FEED, Protocol::MAIL])) {
                        self::remove($contact['id']);
+               } else {
+                       self::update(['rel' => self::FOLLOWER], ['id' => $contact['id']]);
                }
        }
 
index 94c0e3712a268f06b3208d951828c56b82f11415..41d0a6f5e398a5924ee677930f18ca507fafecf9 100644 (file)
@@ -59,8 +59,7 @@ class Block extends BaseApi
                Contact\User::setBlocked($cdata['user'], $uid, true);
 
                // Mastodon-expected behavior: relationship is severed on block
-               Contact::terminateFriendship($owner, $contact);
-               Contact::revokeFollow($contact);
+               Contact::terminateFriendship($contact);
 
                System::jsonExit(DI::mstdnRelationship()->createFromContactId($this->parameters['id'], $uid)->toArray());
        }
index db1e049db9f18dfdd0dc2e316745e61848f81367..29aa82b49ec625d5b383fd5c7261b0d90c170f02 100644 (file)
@@ -40,7 +40,14 @@ class Unfollow extends BaseApi
                        DI::mstdnError()->UnprocessableEntity();
                }
 
-               Contact::unfollow($this->parameters['id'], $uid);
+               $cdata = Contact::getPublicAndUserContactID($this->parameters['id'], $uid);
+               if (empty($cdata['user'])) {
+                       DI::mstdnError()->RecordNotFound();
+               }
+
+               $contact = Contact::getById($cdata['user']);
+
+               Contact::unfollow($contact);
 
                System::jsonExit(DI::mstdnRelationship()->createFromContactId($this->parameters['id'], $uid)->toArray());
        }
index e2e0dd70c5e4a8135c2fd4e3b6f507bfa3cac54a..b730f0663650f69535a75fec6506a8e8ba96e372 100644 (file)
 namespace Friendica\Module\Api\Twitter\Friendships;
 
 use Exception;
+use Friendica\App;
+use Friendica\Core\L10n;
 use Friendica\Core\Logger;
-use Friendica\DI;
+use Friendica\Factory\Api\Twitter\User as TwitterUser;
 use Friendica\Model\Contact;
 use Friendica\Model\User;
+use Friendica\Module\Api\ApiResponse;
 use Friendica\Module\Api\Twitter\ContactEndpoint;
 use Friendica\Module\BaseApi;
 use Friendica\Network\HTTPException;
+use Friendica\Util\Profiler;
+use Psr\Log\LoggerInterface;
 
 /**
  * Unfollow Contact
@@ -37,6 +42,16 @@ use Friendica\Network\HTTPException;
  */
 class Destroy extends ContactEndpoint
 {
+       /** @var TwitterUser */
+       private $twitterUser;
+
+       public function __construct(App $app, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, ApiResponse $response, TwitterUser $twitterUser, array $server, array $parameters = [])
+       {
+               parent::__construct($app, $l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
+
+               $this->twitterUser = $twitterUser;
+       }
+
        protected function post(array $request = [])
        {
                BaseApi::checkAllowedScope(BaseApi::SCOPE_WRITE);
@@ -66,18 +81,9 @@ class Destroy extends ContactEndpoint
                $user    = $this->twitterUser->createFromContactId($contact_id, $uid, true)->toArray();
 
                try {
-                       $result = Contact::terminateFriendship($owner, $contact);
-
-                       if ($result === null) {
-                               Logger::notice(BaseApi::LOG_PREFIX . 'Not supported for {network}', ['module' => 'api', 'action' => 'friendships_destroy', 'network' => $contact['network']]);
-                               throw new HTTPException\ExpectationFailedException('Unfollowing is currently not supported by this contact\'s network.');
-                       }
-
-                       if ($result === false) {
-                               throw new HTTPException\ServiceUnavailableException('Unable to unfollow this contact, please retry in a few minutes or contact your administrator.');
-                       }
+                       Contact::unfollow($contact);
                } catch (Exception $e) {
-                       Logger::error(BaseApi::LOG_PREFIX . $e->getMessage(), ['owner' => $owner, 'contact' => $contact]);
+                       Logger::error(BaseApi::LOG_PREFIX . $e->getMessage(), ['contact' => $contact]);
                        throw new HTTPException\InternalServerErrorException('Unable to unfollow this contact, please contact your administrator');
                }
 
index 50faf987aaa7a38e582fe809aad564dc04d90759..2be55b8a55dc70121c3c1a64d63e81897d409635 100644 (file)
@@ -1668,7 +1668,7 @@ class DFRN
                        }
                        if ($activity->match($item["verb"], Activity::UNFRIEND)) {
                                Logger::notice("Lost sharer");
-                               Contact::removeSharer($importer, $contact, $item);
+                               Contact::removeSharer($contact);
                                return false;
                        }
                } else {
diff --git a/src/Worker/Contact/Unfollow.php b/src/Worker/Contact/Unfollow.php
new file mode 100644 (file)
index 0000000..a6d8c59
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+/**
+ * @copyright Copyright (C) 2010-2022, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Worker\Contact;
+
+use Friendica\Core\Protocol;
+use Friendica\Core\Worker;
+use Friendica\Model\Contact;
+use Friendica\Model\User;
+
+class Unfollow
+{
+       /**
+        * Issue asynchronous unfollow message to remote servers.
+        * The local relationship has already been updated, so we can't use the user-specific contact.
+        *
+        * @param int $cid Target public contact (uid = 0) id
+        * @param int $uid Source local user id
+        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
+        * @throws \ImagickException
+        */
+       public static function execute(int $cid, int $uid)
+       {
+               $contact = Contact::getById($cid);
+               if (empty($contact)) {
+                       return;
+               }
+
+               $owner = User::getOwnerDataById($uid, false);
+               if (empty($owner)) {
+                       return;
+               }
+
+               $result = Protocol::unfollow($contact, $owner);
+               if ($result === false) {
+                       Worker::defer();
+               }
+       }
+}
index e570b13fe1ab6e64ef867d73e0b4378649e3a673..80628d7dba08d08fe5e55a3dabc84886e2a27038 100644 (file)
@@ -673,7 +673,7 @@ class Notifier
                }
 
                while($contact = DBA::fetch($contacts_stmt)) {
-                       Protocol::terminateFriendship($owner, $contact, true);
+                       Contact::terminateFriendship($contact);
                }
                DBA::close($contacts_stmt);