<?php declare(strict_types=1); /** * @copyright 2021 Ad Aures * @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3 * @link https://castopod.org/ */ namespace Modules\Fediverse\Models; use CodeIgniter\Events\Events; use CodeIgniter\I18n\Time; use CodeIgniter\Model; use Exception; use Modules\Fediverse\Activities\FollowActivity; use Modules\Fediverse\Activities\UndoActivity; use Modules\Fediverse\Entities\Actor; use Modules\Fediverse\Entities\Follow; class FollowModel extends Model { /** * @var string */ protected $table = 'fediverse_follows'; /** * @var list<string> */ protected $allowedFields = ['actor_id', 'target_actor_id']; /** * @var string */ protected $returnType = Follow::class; /** * @var bool */ protected $useTimestamps = true; protected $updatedField = ''; /** * @param Actor $actor Actor that is following * @param Actor $targetActor Actor that is being followed */ public function addFollower(Actor $actor, Actor $targetActor, bool $registerActivity = true): void { try { $this->db->transStart(); $this->insert([ 'actor_id' => $actor->id, 'target_actor_id' => $targetActor->id, ]); // increment followers_count for target actor model('ActorModel', false) ->builder() ->where('id', $targetActor->id) ->increment('followers_count'); if ($registerActivity) { $followActivity = new FollowActivity(); $followActivity ->set('actor', $actor->uri) ->set('object', $targetActor->uri); $activityId = model('ActivityModel', false) ->newActivity( 'Follow', $actor->id, $targetActor->id, null, $followActivity->toJSON(), Time::now(), 'queued', ); $followActivity->set('id', url_to('activity', esc($actor->username), $activityId)); model('ActivityModel', false) ->update($activityId, [ 'payload' => $followActivity->toJSON(), ]); } Events::trigger('on_follow', $actor, $targetActor); model('ActorModel', false) ->clearCache($targetActor); $this->db->transComplete(); } catch (Exception) { // follow already exists, do nothing } } /** * @param Actor $actor Actor that is unfollowing * @param Actor $targetActor Actor that is being unfollowed */ public function removeFollower(Actor $actor, Actor $targetActor, bool $registerActivity = true): void { $this->db->transStart(); $this->where([ 'actor_id' => $actor->id, 'target_actor_id' => $targetActor->id, ])->delete(); // decrement followers_count for target actor model('ActorModel', false) ->builder() ->where('id', $targetActor->id) ->decrement('followers_count'); if ($registerActivity) { $undoActivity = new UndoActivity(); // get follow activity from database $followActivity = model('ActivityModel', false) ->where([ 'type' => 'Follow', 'actor_id' => $actor->id, 'target_actor_id' => $targetActor->id, ]) ->first(); $undoActivity ->set('actor', $actor->uri) ->set('object', $followActivity->payload); $activityId = model('ActivityModel', false) ->newActivity( 'Undo', $actor->id, $targetActor->id, null, $undoActivity->toJSON(), Time::now(), 'queued', ); $undoActivity->set('id', url_to('activity', esc($actor->username), $activityId)); model('ActivityModel', false) ->update($activityId, [ 'payload' => $undoActivity->toJSON(), ]); } Events::trigger('on_undo_follow', $actor, $targetActor); model('ActorModel', false) ->clearCache($targetActor); $this->db->transComplete(); } }