. // }}} namespace Plugin\Favourite\Controller; use App\Core\DB; use App\Core\Event; use App\Core\Form; use App\Core\Log; use App\Core\Router; use App\Entity\Activity; use App\Entity\Actor; use App\Entity\LocalUser; use App\Entity\Note; use App\Util\Common; use App\Util\Exception\ClientException; use App\Util\Exception\InvalidFormException; use App\Util\Exception\NoLoggedInUser; use App\Util\Exception\NoSuchNoteException; use App\Util\Exception\RedirectException; use App\Util\Exception\ServerException; use Component\Collection\Util\Controller\FeedController; use Component\Notification\Entity\Attention; use function App\Core\I18n\_m; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\HttpFoundation\Request; class Favourite extends FeedController { /** * @throws InvalidFormException * @throws NoLoggedInUser * @throws NoSuchNoteException * @throws RedirectException * @throws ServerException */ public function favouriteAddNote(Request $request, int $id): bool|array { $user = Common::ensureLoggedIn(); $actor_id = $user->getId(); $opts = ['id' => $id]; $add_favourite_note = DB::findOneBy(Note::class, $opts); if (\is_null($add_favourite_note)) { throw new NoSuchNoteException(); } $form_add_to_favourite = Form::create([ ['add_favourite', SubmitType::class, [ 'label' => _m('Favourite note!'), 'attr' => [ 'title' => _m('Favourite this note!'), ], ], ], ]); $form_add_to_favourite->handleRequest($request); if ($form_add_to_favourite->isSubmitted()) { if (!\is_null($activity = \Plugin\Favourite\Favourite::favourNote(note_id: $id, actor_id: $actor_id))) { $actor = Actor::getById($actor_id); foreach ($actor->getSubscribers() as $subscriber) { $target_id = $subscriber->getId(); DB::persist(Attention::create(['object_type' => Activity::schemaName(), 'object_id' => $activity->getId(), 'target_id' => $target_id])); $effective_attentions[$target_id] = $subscriber; } DB::flush(); Event::handle('NewNotification', [$actor, $activity, $activity->getAttentionTargets(), _m('{actor_id} favoured note {note_id}.', ['{actor_id}' => $actor->getId(), '{note_id}' => $activity->getObjectId()])]); } else { throw new ClientException(_m('Note already favoured!')); } // Redirect user to where they came from // Prevent open redirect if (!\is_null($from = $this->string('from'))) { if (Router::isAbsolute($from)) { Log::warning("Actor {$actor_id} attempted to favourite a note and then get redirected to another host, or the URL was invalid ({$from})"); throw new ClientException(_m('Can not redirect to outside the website from here'), 400); // 400 Bad request (deceptive) } else { // TODO anchor on element id throw new RedirectException(url: $from); } } else { // If we don't have a URL to return to, go to the instance root throw new RedirectException('root'); } } return [ '_template' => 'favourite/add_to_favourites.html.twig', 'note' => $add_favourite_note, 'add_favourite' => $form_add_to_favourite->createView(), ]; } /** * @throws InvalidFormException * @throws NoLoggedInUser * @throws NoSuchNoteException * @throws RedirectException * @throws ServerException */ public function favouriteRemoveNote(Request $request, int $id): array { $user = Common::ensureLoggedIn(); $actor_id = $user->getId(); $opts = ['id' => $id]; $remove_favourite_note = DB::findOneBy(Note::class, $opts); if (\is_null($remove_favourite_note)) { throw new NoSuchNoteException(); } $form_remove_favourite = Form::create([ ['remove_favourite', SubmitType::class, [ 'label' => _m('Remove favourite'), 'attr' => [ 'title' => _m('Remove note from favourites.'), ], ], ], ]); $form_remove_favourite->handleRequest($request); if ($form_remove_favourite->isSubmitted()) { if (!\is_null($activity = \Plugin\Favourite\Favourite::unfavourNote(note_id: $id, actor_id: $actor_id))) { $actor = Actor::getById($actor_id); foreach ($actor->getSubscribers() as $subscriber) { $target_id = $subscriber->getId(); DB::persist(Attention::create(['object_type' => Activity::schemaName(), 'object_id' => $activity->getId(), 'target_id' => $target_id])); $effective_attentions[$target_id] = $subscriber; } DB::flush(); Event::handle('NewNotification', [$actor, $activity, $activity->getAttentionTargets(), _m('{actor_id} unfavoured note {note_id}.', ['{actor_id}' => $actor->getId(), '{note_id}' => $activity->getObjectId()])]); } else { throw new ClientException(_m('Note already unfavoured!')); } // Redirect user to where they came from // Prevent open redirect if (!\is_null($from = $this->string('from'))) { if (Router::isAbsolute($from)) { Log::warning("Actor {$actor_id} attempted to reply to a note and then get redirected to another host, or the URL was invalid ({$from})"); throw new ClientException(_m('Can not redirect to outside the website from here'), 400); // 400 Bad request (deceptive) } else { // TODO anchor on element id throw new RedirectException(url: $from); } } else { // If we don't have a URL to return to, go to the instance root throw new RedirectException('root'); } } $note = DB::findOneBy(Note::class, ['id' => $id]); return [ '_template' => 'favourite/remove_from_favourites.html.twig', 'note' => $note, 'remove_favourite' => $form_remove_favourite->createView(), ]; } public function favouritesViewByActorId(Request $request, int $id) { $notes = DB::dql( <<< 'EOF' select n from note n join note_favourite f with n.id = f.note_id where f.actor_id = :id order by f.created DESC EOF, ['id' => $id], ); return [ '_template' => 'collection/notes.html.twig', 'page_title' => 'Favourites', 'notes' => $notes, ]; } public function favouritesViewByActorNickname(Request $request, string $nickname) { $user = DB::findOneBy(LocalUser::class, ['nickname' => $nickname]); return self::favouritesViewByActorId($request, $user->getId()); } /** * Reverse favourites stream * * @throws NoLoggedInUser user not logged in * * @return array template */ public function reverseFavouritesViewByActorId(Request $request, int $id): array { $notes = DB::dql( <<< 'EOF' select n from note n join note_favourite f with n.id = f.note_id where f.actor_id != :id and n.actor_id = :id order by f.created DESC EOF, ['id' => $id], ); return [ '_template' => 'collection/notes.html.twig', 'page_title' => 'Reverse favourites feed.', 'notes' => $notes, ]; } public function reverseFavouritesViewByActorNickname(Request $request, string $nickname) { $user = DB::findOneBy(LocalUser::class, ['nickname' => $nickname]); return self::reverseFavouritesViewByActorId($request, $user->getId()); } }