Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
88.79% covered (warning)
88.79%
95 / 107
61.54% covered (warning)
61.54%
8 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
NewsEndpointTrait
88.79% covered (warning)
88.79%
95 / 107
61.54% covered (warning)
61.54%
8 / 13
40.04
0.00% covered (danger)
0.00%
0 / 1
 configureNewsEndpointTrait
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getEntityData
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
1 / 1
5
 updateEntityWithData
97.14% covered (success)
97.14%
34 / 35
0.00% covered (danger)
0.00%
0 / 1
5
 persistUploads
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 editUploads
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getEntityById
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 getFormat
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getFormatForApi
25.00% covered (danger)
25.00%
2 / 8
0.00% covered (danger)
0.00%
0 / 1
35.00
 getAuthorUserId
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 getAuthorRoleId
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 getTerminId
50.00% covered (danger)
50.00%
3 / 6
0.00% covered (danger)
0.00%
0 / 1
4.12
 getTagsForDb
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getTagsForApi
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3namespace Olz\News\Endpoints;
4
5use Olz\Entity\News\NewsEntry;
6use Olz\Entity\Roles\Role;
7use Olz\Entity\Users\User;
8use Olz\Utils\WithUtilsTrait;
9use PhpTypeScriptApi\HttpError;
10use PhpTypeScriptApi\PhpStan\IsoDateTime;
11
12/**
13 * @phpstan-type OlzNewsId int
14 * @phpstan-type OlzNewsData array{
15 *   format: OlzNewsFormat,
16 *   authorUserId?: ?int<1, max>,
17 *   authorRoleId?: ?int<1, max>,
18 *   authorName?: ?non-empty-string,
19 *   authorEmail?: ?non-empty-string,
20 *   publishAt?: ?IsoDateTime,
21 *   title: non-empty-string,
22 *   teaser: string,
23 *   content: string,
24 *   externalUrl?: ?non-empty-string,
25 *   tags: array<non-empty-string>,
26 *   terminId?: ?int<1, max>,
27 *   imageIds?: ?array<non-empty-string>,
28 *   fileIds: array<non-empty-string>,
29 * }
30 * @phpstan-type OlzNewsFormat 'aktuell'|'kaderblog'|'forum'|'galerie'|'video'|'anonymous'
31 */
32trait NewsEndpointTrait {
33    use WithUtilsTrait;
34
35    public function configureNewsEndpointTrait(): void {
36        $this->phpStanUtils->registerApiObject(IsoDateTime::class);
37    }
38
39    /** @return OlzNewsData */
40    public function getEntityData(NewsEntry $entity): array {
41        $author_name = $entity->getAuthorName();
42        $author_email = $entity->getAuthorEmail();
43        $published_date = $entity->getPublishedDate()->format('Y-m-d');
44        $published_time = $entity->getPublishedTime()?->format('H:i:s') ?? '00:00:00';
45        $tags_for_api = $this->getTagsForApi($entity->getTags());
46        $external_url = $entity->getExternalUrl();
47        $termin_id = $entity->getTermin();
48
49        $valid_image_ids = $this->uploadUtils()->getValidUploadIds($entity->getImageIds());
50        $file_ids = $entity->getStoredFileUploadIds();
51
52        return [
53            'format' => $this->getFormatForApi($entity),
54            'authorUserId' => $this->getAuthorUserId($entity),
55            'authorRoleId' => $this->getAuthorRoleId($entity),
56            'authorName' => $author_name ? $author_name : null,
57            'authorEmail' => $author_email ? $author_email : null,
58            'publishAt' => new IsoDateTime("{$published_date} {$published_time}"),
59            'title' => $entity->getTitle() ?: '-',
60            'teaser' => $entity->getTeaser() ?? '',
61            'content' => $entity->getContent() ?? '',
62            'externalUrl' => $external_url ? $external_url : null,
63            'tags' => $tags_for_api,
64            'terminId' => $this->getTerminId($entity),
65            'imageIds' => $valid_image_ids,
66            'fileIds' => $file_ids,
67        ];
68    }
69
70    /** @param OlzNewsData $input_data */
71    public function updateEntityWithData(NewsEntry $entity, array $input_data): void {
72        $user_repo = $this->entityManager()->getRepository(User::class);
73        $role_repo = $this->entityManager()->getRepository(Role::class);
74        $current_user = $this->authUtils()->getCurrentUser();
75        $now = new \DateTime($this->dateUtils()->getIsoNow());
76
77        $author_user_id = $input_data['authorUserId'] ?? null;
78        $author_user = $current_user;
79        if ($author_user_id) {
80            $author_user = $user_repo->findOneBy(['id' => $author_user_id]);
81        }
82
83        $author_role_id = $input_data['authorRoleId'] ?? null;
84        $author_role = null;
85        if ($author_role_id) {
86            $is_admin = $this->authUtils()->hasPermission('all');
87            $is_authenticated_role = $this->authUtils()->isRoleIdAuthenticated($author_role_id);
88            if (!$is_authenticated_role && !$is_admin) {
89                throw new HttpError(403, "Kein Zugriff auf Autor-Rolle!");
90            }
91            $author_role = $role_repo->findOneBy(['id' => $author_role_id]);
92        }
93
94        $publish_at = $input_data['publishAt'] ?? $now;
95
96        $tags_for_db = $this->getTagsForDb($input_data['tags']);
97        $valid_image_ids = $this->uploadUtils()->getValidUploadIds($input_data['imageIds'] ?? null);
98
99        $entity->setAuthorUser($author_user);
100        $entity->setAuthorRole($author_role);
101        $entity->setAuthorName($input_data['authorName'] ?? null);
102        $entity->setAuthorEmail($input_data['authorEmail'] ?? null);
103        $entity->setPublishedDate($publish_at);
104        $entity->setPublishedTime($publish_at);
105        $entity->setTitle($input_data['title']);
106        $entity->setTeaser($input_data['teaser']);
107        $entity->setContent($input_data['content']);
108        $entity->setExternalUrl($input_data['externalUrl'] ?? null);
109        $entity->setTags($tags_for_db);
110        $entity->setImageIds($valid_image_ids);
111        // TODO: Do not ignore
112        $entity->setTermin(0);
113        $entity->setCounter(0);
114        $entity->setFormat($this->getFormat($input_data['format']));
115        $entity->setNewsletter(true);
116    }
117
118    /** @param OlzNewsData $input_data */
119    public function persistUploads(NewsEntry $entity, array $input_data): void {
120        $this->persistOlzImages($entity, $entity->getImageIds());
121        $this->persistOlzFiles($entity, $input_data['fileIds']);
122    }
123
124    public function editUploads(NewsEntry $entity): void {
125        $this->editOlzImages($entity, $entity->getImageIds());
126        $this->editOlzFiles($entity);
127    }
128
129    protected function getEntityById(int $id): NewsEntry {
130        $news_repo = $this->entityManager()->getRepository(NewsEntry::class);
131        $entity = $news_repo->findOneBy(['id' => $id]);
132        if (!$entity) {
133            throw new HttpError(404, "Nicht gefunden.");
134        }
135        return $entity;
136    }
137
138    // ---
139
140    protected function getFormat(string $format): string {
141        if ($format === 'anonymous') {
142            return 'forum';
143        }
144        return $format;
145    }
146
147    /** @return OlzNewsFormat */
148    protected function getFormatForApi(NewsEntry $entity): string {
149        switch ($entity->getFormat()) {
150            case 'aktuell': return 'aktuell';
151            case 'anonymous': return 'anonymous';
152            case 'forum': return 'forum';
153            case 'galerie': return 'galerie';
154            case 'kaderblog': return 'kaderblog';
155            case 'video': return 'video';
156            default: throw new \Exception("Unknown news format: {$entity->getFormat()} ({$entity})");
157        }
158    }
159
160    /** @return ?int<1, max> */
161    protected function getAuthorUserId(NewsEntry $entity): ?int {
162        $number = $entity->getAuthorUser()?->getId();
163        if ($number === null) {
164            return null;
165        }
166        if ($number < 1) {
167            throw new \Exception("Invalid author user ID: {$number} ({$entity})");
168        }
169        return $number;
170    }
171
172    /** @return ?int<1, max> */
173    protected function getAuthorRoleId(NewsEntry $entity): ?int {
174        $number = $entity->getAuthorRole()?->getId();
175        if ($number === null) {
176            return null;
177        }
178        if ($number < 1) {
179            throw new \Exception("Invalid author role ID: {$number} ({$entity})");
180        }
181        return $number;
182    }
183
184    /** @return ?int<1, max> */
185    protected function getTerminId(NewsEntry $entity): ?int {
186        $number = $entity->getTermin();
187        if (!$number) {
188            return null;
189        }
190        if ($number < 1) {
191            throw new \Exception("Invalid termin ID: {$number} ({$entity})");
192        }
193        return $number;
194    }
195
196    /** @param array<string> $tags */
197    protected function getTagsForDb(?array $tags): string {
198        return ' '.implode(' ', $tags ?? []).' ';
199    }
200
201    /** @return array<non-empty-string> */
202    protected function getTagsForApi(?string $tags): array {
203        $tags_string = $tags ?? '';
204        $tags_for_api = [];
205        foreach (explode(' ', $tags_string) as $tag) {
206            $trimmed = trim($tag);
207            if ($trimmed) {
208                $tags_for_api[] = $trimmed;
209            }
210        }
211        return $tags_for_api;
212    }
213}