diff --git a/doc/Addons.md b/doc/Addons.md index 504ab43984..7c9b89d342 100644 --- a/doc/Addons.md +++ b/doc/Addons.md @@ -635,7 +635,8 @@ Hook data: Called when making a remote contact on a non-native network (like Twitter) unfollow you. Hook data: -- **contact** (input): the remote contact (uid = local revoking user id) array. +- **contact** (input): the target public contact (uid = 0) array. +- **uid** (input): the id of the source local user. - **result** (output): a boolean value indicating wether the operation was successful or not. ### block diff --git a/src/Core/Protocol.php b/src/Core/Protocol.php index 47d4b4539f..c141bbc54e 100644 --- a/src/Core/Protocol.php +++ b/src/Core/Protocol.php @@ -22,7 +22,6 @@ namespace Friendica\Core; use Friendica\Database\DBA; -use Friendica\DI; use Friendica\Model\User; use Friendica\Network\HTTPException; use Friendica\Protocol\Activity; @@ -227,12 +226,13 @@ class Protocol /** * Revoke an incoming follow from the provided contact * - * @param array $contact Private contact (uid != 0) array + * @param array $contact Target public contact (uid == 0) array + * @param int $uid Source local user id * @return bool|null true if successful, false if not, null if no action was performed * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - public static function revokeFollow(array $contact): ?bool + public static function revokeFollow(array $contact, int $uid): ?bool { if (empty($contact['network'])) { throw new \InvalidArgumentException('Missing network key in contact array'); @@ -244,13 +244,14 @@ class Protocol } if ($protocol == Protocol::ACTIVITYPUB) { - return ActivityPub\Transmitter::sendContactReject($contact['url'], $contact['hub-verify'], $contact['uid']); + return ActivityPub\Transmitter::sendContactReject($contact['url'], $contact['hub-verify'], $uid); } // Catch-all hook for connector addons $hook_data = [ 'contact' => $contact, - 'result' => null, + 'uid' => $uid, + 'result' => null, ]; Hook::callAll('revoke_follow', $hook_data); diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 99b1c0971f..f0be457722 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -840,12 +840,13 @@ class Contact /** * Revoke follow privileges of the remote user contact * - * @param array $contact Contact unfriended - * @return bool|null Whether the remote operation is successful or null if no remote operation was performed + * The local relationship is updated immediately, the eventual remote server is messaged in the background. + * + * @param array $contact User-specific contact array (uid != 0) to revoke the follow from * @throws HTTPException\InternalServerErrorException * @throws \ImagickException */ - public static function revokeFollow(array $contact): ?bool + public static function revokeFollow(array $contact): void { if (empty($contact['network'])) { throw new \InvalidArgumentException('Empty network in contact array'); @@ -855,19 +856,12 @@ class Contact throw new \InvalidArgumentException('Unexpected public contact record'); } - $result = Protocol::revokeFollow($contact); - - // A null value here means the remote network doesn't support explicit follow revocation, we can still - // break the locally recorded relationship - if ($result !== false) { - if ($contact['rel'] == self::FRIEND) { - self::update(['rel' => self::SHARING], ['id' => $contact['id']]); - } else { - self::remove($contact['id']); - } + if (in_array($contact['rel'], [self::FOLLOWER, self::FRIEND])) { + $cdata = Contact::getPublicAndUserContactID($contact['id'], $contact['uid']); + Worker::add(PRIORITY_HIGH, 'Contact\RevokeFollow', $cdata['public'], $contact['uid']); } - return $result; + self::removeFollower($contact); } /** @@ -2754,6 +2748,13 @@ class Contact return null; } + /** + * Update the local relationship when a local user loses a follower + * + * @param array $contact User-specific contact (uid != 0) array + * @throws HTTPException\InternalServerErrorException + * @throws \ImagickException + */ public static function removeFollower(array $contact) { if (in_array($contact['rel'] ?? [], [self::FRIEND, self::SHARING])) { diff --git a/src/Module/Contact/Revoke.php b/src/Module/Contact/Revoke.php index 59b3bdafae..35cb481495 100644 --- a/src/Module/Contact/Revoke.php +++ b/src/Module/Contact/Revoke.php @@ -38,7 +38,10 @@ use Psr\Log\LoggerInterface; class Revoke extends BaseModule { - /** @var array */ + /** + * User-specific contact (uid != 0) array + * @var array + */ protected $contact; /** @var Database */ @@ -82,14 +85,9 @@ class Revoke extends BaseModule self::checkFormSecurityTokenRedirectOnError('contact/' . $this->parameters['id'], 'contact_revoke'); - $result = Model\Contact::revokeFollow($this->contact); - if ($result === true) { - notice($this->t('Follow was successfully revoked.')); - } elseif ($result === null) { - notice($this->t('Follow was successfully revoked, however the remote contact won\'t be aware of this revokation.')); - } else { - notice($this->t('Unable to revoke follow, please try again later or contact the administrator.')); - } + Model\Contact::revokeFollow($this->contact); + + notice($this->t('Follow was successfully revoked.')); $this->baseUrl->redirect('contact/' . $this->parameters['id']); } diff --git a/src/Worker/Contact/RevokeFollow.php b/src/Worker/Contact/RevokeFollow.php new file mode 100644 index 0000000000..726a69b8da --- /dev/null +++ b/src/Worker/Contact/RevokeFollow.php @@ -0,0 +1,51 @@ +. + * + */ + +namespace Friendica\Worker\Contact; + +use Friendica\Core\Protocol; +use Friendica\Core\Worker; +use Friendica\Model\Contact; + +class RevokeFollow +{ + /** + * Issue asynchronous follow revokation 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 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; + } + + $result = Protocol::revokeFollow($contact, $uid); + if ($result === false) { + Worker::defer(); + } + } +}