Move unfollow protocol to worker task
- 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
This commit is contained in:
parent
48e2963ac7
commit
6a1f9a7fa3
11 changed files with 159 additions and 78 deletions
|
@ -626,7 +626,8 @@ Hook data:
|
||||||
Called when unfollowing a remote contact on a non-native network (like Twitter)
|
Called when unfollowing a remote contact on a non-native network (like Twitter)
|
||||||
|
|
||||||
Hook data:
|
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.
|
- **result** (output): wether the unfollowing is successful or not.
|
||||||
|
|
||||||
### revoke_follow
|
### revoke_follow
|
||||||
|
|
|
@ -122,8 +122,7 @@ function unfollow_process(string $url)
|
||||||
|
|
||||||
$owner = User::getOwnerDataById($uid);
|
$owner = User::getOwnerDataById($uid);
|
||||||
if (!$owner) {
|
if (!$owner) {
|
||||||
(new \Friendica\Module\Security\Logout())->init();
|
throw new \Friendica\Network\HTTPException\NotFoundException();
|
||||||
// NOTREACHED
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$condition = ["`uid` = ? AND (`rel` = ? OR `rel` = ?) AND (`nurl` = ? OR `alias` = ? OR `alias` = ?)",
|
$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'];
|
$return_path = $base_return_path . '/' . $contact['id'];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$result = Contact::terminateFriendship($owner, $contact);
|
Contact::unfollow($contact);
|
||||||
|
$notice_message = DI::l10n()->t('Contact was successfully unfollowed');
|
||||||
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');
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
} 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');
|
$notice_message = DI::l10n()->t('Unable to unfollow this contact, please contact your administrator');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -199,19 +199,18 @@ HELP;
|
||||||
throw new RuntimeException('Contact not found');
|
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 {
|
try {
|
||||||
$result = ContactModel::terminateFriendship($user, $contact);
|
ContactModel::unfollow($contact);
|
||||||
if ($result === false) {
|
|
||||||
throw new RuntimeException('Unable to unfollow this contact, please retry in a few minutes or check the logs.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->out('Contact was successfully unfollowed');
|
$this->out('Contact was successfully unfollowed');
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (\Exception $e) {
|
} 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');
|
throw new RuntimeException('Unable to unfollow this contact, please check the log');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 Target public contact (uid = 0) array
|
||||||
* @param array $contact Contact unfriended
|
* @param array $user Source local user array
|
||||||
* @return bool|null true if successful, false if not, null if no remote action was performed
|
* @return bool|null true if successful, false if not, null if no remote action was performed
|
||||||
* @throws HTTPException\InternalServerErrorException
|
* @throws HTTPException\InternalServerErrorException
|
||||||
* @throws \ImagickException
|
* @throws \ImagickException
|
||||||
*/
|
*/
|
||||||
public static function terminateFriendship(array $user, array $contact): ?bool
|
public static function unfollow(array $contact, array $user): ?bool
|
||||||
{
|
{
|
||||||
if (empty($contact['network'])) {
|
if (empty($contact['network'])) {
|
||||||
throw new \InvalidArgumentException('Missing network key in contact array');
|
throw new \InvalidArgumentException('Missing network key in contact array');
|
||||||
|
@ -216,7 +216,8 @@ class Protocol
|
||||||
// Catch-all hook for connector addons
|
// Catch-all hook for connector addons
|
||||||
$hook_data = [
|
$hook_data = [
|
||||||
'contact' => $contact,
|
'contact' => $contact,
|
||||||
'result' => null
|
'uid' => $user['uid'],
|
||||||
|
'result' => null,
|
||||||
];
|
];
|
||||||
Hook::callAll('unfollow', $hook_data);
|
Hook::callAll('unfollow', $hook_data);
|
||||||
|
|
||||||
|
|
|
@ -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 Target user-specific contact (uid != 0) array
|
||||||
* @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
|
|
||||||
* @throws HTTPException\InternalServerErrorException
|
* @throws HTTPException\InternalServerErrorException
|
||||||
* @throws \ImagickException
|
* @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']]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
if (empty($contact['uid'])) {
|
||||||
|
throw new \InvalidArgumentException('Unexpected public contact record');
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
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
|
* Marks a contact for archival after a communication issue delay
|
||||||
|
@ -2574,28 +2606,6 @@ class Contact
|
||||||
return $result;
|
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 $importer Owner (local user) data
|
||||||
* @param array $contact Existing owner-specific contact data we want to expand the relationship with. Optional.
|
* @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)) {
|
if ($contact['rel'] == self::SHARING || in_array($contact['network'], [Protocol::FEED, Protocol::MAIL])) {
|
||||||
self::update(['rel' => self::FOLLOWER], ['id' => $contact['id']]);
|
|
||||||
} else {
|
|
||||||
self::remove($contact['id']);
|
self::remove($contact['id']);
|
||||||
|
} else {
|
||||||
|
self::update(['rel' => self::FOLLOWER], ['id' => $contact['id']]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,8 +59,7 @@ class Block extends BaseApi
|
||||||
Contact\User::setBlocked($cdata['user'], $uid, true);
|
Contact\User::setBlocked($cdata['user'], $uid, true);
|
||||||
|
|
||||||
// Mastodon-expected behavior: relationship is severed on block
|
// Mastodon-expected behavior: relationship is severed on block
|
||||||
Contact::terminateFriendship($owner, $contact);
|
Contact::terminateFriendship($contact);
|
||||||
Contact::revokeFollow($contact);
|
|
||||||
|
|
||||||
System::jsonExit(DI::mstdnRelationship()->createFromContactId($this->parameters['id'], $uid)->toArray());
|
System::jsonExit(DI::mstdnRelationship()->createFromContactId($this->parameters['id'], $uid)->toArray());
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,14 @@ class Unfollow extends BaseApi
|
||||||
DI::mstdnError()->UnprocessableEntity();
|
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());
|
System::jsonExit(DI::mstdnRelationship()->createFromContactId($this->parameters['id'], $uid)->toArray());
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,13 +22,18 @@
|
||||||
namespace Friendica\Module\Api\Twitter\Friendships;
|
namespace Friendica\Module\Api\Twitter\Friendships;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use Friendica\App;
|
||||||
|
use Friendica\Core\L10n;
|
||||||
use Friendica\Core\Logger;
|
use Friendica\Core\Logger;
|
||||||
use Friendica\DI;
|
use Friendica\Factory\Api\Twitter\User as TwitterUser;
|
||||||
use Friendica\Model\Contact;
|
use Friendica\Model\Contact;
|
||||||
use Friendica\Model\User;
|
use Friendica\Model\User;
|
||||||
|
use Friendica\Module\Api\ApiResponse;
|
||||||
use Friendica\Module\Api\Twitter\ContactEndpoint;
|
use Friendica\Module\Api\Twitter\ContactEndpoint;
|
||||||
use Friendica\Module\BaseApi;
|
use Friendica\Module\BaseApi;
|
||||||
use Friendica\Network\HTTPException;
|
use Friendica\Network\HTTPException;
|
||||||
|
use Friendica\Util\Profiler;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unfollow Contact
|
* Unfollow Contact
|
||||||
|
@ -37,6 +42,16 @@ use Friendica\Network\HTTPException;
|
||||||
*/
|
*/
|
||||||
class Destroy extends ContactEndpoint
|
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 = [])
|
protected function post(array $request = [])
|
||||||
{
|
{
|
||||||
BaseApi::checkAllowedScope(BaseApi::SCOPE_WRITE);
|
BaseApi::checkAllowedScope(BaseApi::SCOPE_WRITE);
|
||||||
|
@ -66,18 +81,9 @@ class Destroy extends ContactEndpoint
|
||||||
$user = $this->twitterUser->createFromContactId($contact_id, $uid, true)->toArray();
|
$user = $this->twitterUser->createFromContactId($contact_id, $uid, true)->toArray();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$result = Contact::terminateFriendship($owner, $contact);
|
Contact::unfollow($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.');
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
} 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');
|
throw new HTTPException\InternalServerErrorException('Unable to unfollow this contact, please contact your administrator');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1668,7 +1668,7 @@ class DFRN
|
||||||
}
|
}
|
||||||
if ($activity->match($item["verb"], Activity::UNFRIEND)) {
|
if ($activity->match($item["verb"], Activity::UNFRIEND)) {
|
||||||
Logger::notice("Lost sharer");
|
Logger::notice("Lost sharer");
|
||||||
Contact::removeSharer($importer, $contact, $item);
|
Contact::removeSharer($contact);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
57
src/Worker/Contact/Unfollow.php
Normal file
57
src/Worker/Contact/Unfollow.php
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -673,7 +673,7 @@ class Notifier
|
||||||
}
|
}
|
||||||
|
|
||||||
while($contact = DBA::fetch($contacts_stmt)) {
|
while($contact = DBA::fetch($contacts_stmt)) {
|
||||||
Protocol::terminateFriendship($owner, $contact, true);
|
Contact::terminateFriendship($contact);
|
||||||
}
|
}
|
||||||
DBA::close($contacts_stmt);
|
DBA::close($contacts_stmt);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue