Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.33% covered (success)
93.33%
42 / 45
0.00% covered (danger)
0.00%
0 / 1
CRAP
0.00% covered (danger)
0.00%
0 / 1
ToggleNewsReactionEndpoint
93.33% covered (success)
93.33%
42 / 45
0.00% covered (danger)
0.00%
0 / 1
19.11
0.00% covered (danger)
0.00%
0 / 1
 handle
93.33% covered (success)
93.33%
42 / 45
0.00% covered (danger)
0.00%
0 / 1
19.11
1<?php
2
3namespace Olz\News\Endpoints;
4
5use Olz\Api\OlzTypedEndpoint;
6use Olz\Entity\News\NewsEntry;
7use Olz\Entity\News\NewsReaction;
8use Olz\Entity\Users\User;
9use PhpTypeScriptApi\HttpError;
10
11/**
12 * @phpstan-import-type OlzReaction from ListNewsReactionsEndpoint
13 *
14 * @extends OlzTypedEndpoint<
15 *   array{
16 *     userId?: ?int<1, max>,
17 *     newsEntryId: int<1, max>,
18 *     emoji: non-empty-string,
19 *     action: 'on'|'off'|'toggle',
20 *   },
21 *   array{
22 *     result: ?OlzReaction,
23 *   }
24 * >
25 */
26class ToggleNewsReactionEndpoint extends OlzTypedEndpoint {
27    protected function handle(mixed $input): mixed {
28        $has_access = $this->authUtils()->hasPermission('any');
29        $user = $this->authUtils()->getCurrentUser();
30        if (($input['userId'] ?? null) !== null) {
31            $user_repo = $this->entityManager()->getRepository(User::class);
32            $user = $user_repo->findOneBy(['id' => $input['userId']]);
33        }
34        if (!$has_access || !$user) {
35            throw new HttpError(403, 'Kein Zugriff!');
36        }
37        $auth_user_id = $this->session()->get('auth_user_id');
38        $is_parent = $auth_user_id && intval($user->getParentUserId()) === intval($auth_user_id);
39        $is_self = $auth_user_id && intval($user->getId()) === intval($auth_user_id);
40        if (!$is_self && !$is_parent) {
41            throw new HttpError(403, "Kein Zugriff!");
42        }
43        if (!$this->generalUtils()->isOneEmoji($input['emoji'])) {
44            $enc_emoji = urlencode($input['emoji']);
45            throw new HttpError(400, "Ungültiges Emoji: {$input['emoji']} ({$enc_emoji})");
46        }
47
48        $news_reaction_repo = $this->entityManager()->getRepository(NewsReaction::class);
49        $reactions = $news_reaction_repo->findBy([
50            'news_entry' => $input['newsEntryId'],
51            'emoji' => $input['emoji'],
52            'user' => $user,
53        ]);
54        $has_reactions = count($reactions) > 0;
55        $want_reaction = $input['action'] === 'on' || ($input['action'] === 'toggle' && !$has_reactions);
56        $result = null;
57
58        if (!$has_reactions && $want_reaction) {
59            $news_repo = $this->entityManager()->getRepository(NewsEntry::class);
60            $news_entry = $news_repo->findOneBy(['id' => $input['newsEntryId']]);
61            if (!$news_entry) {
62                throw new HttpError(400, "Kein solcher News-Eintrag");
63            }
64            $reaction = new NewsReaction();
65            $reaction->setNewsEntry($news_entry);
66            $reaction->setUser($user);
67            $reaction->setEmoji($input['emoji']);
68            $this->entityManager()->persist($reaction);
69            $this->entityManager()->flush();
70            $result = [
71                'userId' => $reaction->getUser()->getId() ?? 0,
72                'name' => $reaction->getUser()->getFullName() ?: '?',
73                'emoji' => $reaction->getEmoji() ?: '?',
74            ];
75        }
76        if ($has_reactions && !$want_reaction) {
77            foreach ($reactions as $reaction) {
78                $this->entityManager()->remove($reaction);
79            }
80            $this->entityManager()->flush();
81        }
82
83        return ['result' => $result];
84    }
85}