Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 183
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
OlzNewsDetailParams
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
OlzNewsDetail
0.00% covered (danger)
0.00%
0 / 183
0.00% covered (danger)
0.00%
0 / 4
1332
0.00% covered (danger)
0.00%
0 / 1
 getSearchTitle
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSearchResults
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
20
 getHtml
0.00% covered (danger)
0.00%
0 / 163
0.00% covered (danger)
0.00%
0 / 1
930
 getNewsEntryById
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3// =============================================================================
4// Alle Neuigkeiten rund um die OL Zimmerberg
5// =============================================================================
6
7namespace Olz\News\Components\OlzNewsDetail;
8
9use Doctrine\Common\Collections\Criteria;
10use Olz\Components\Common\OlzRootComponent;
11use Olz\Components\Page\OlzFooter\OlzFooter;
12use Olz\Components\Page\OlzHeader\OlzHeader;
13use Olz\Entity\News\NewsEntry;
14use Olz\News\Components\OlzArticleMetadata\OlzArticleMetadata;
15use Olz\News\Components\OlzAuthorBadge\OlzAuthorBadge;
16use Olz\News\Utils\NewsUtils;
17use Olz\Utils\HttpParams;
18
19/** @extends HttpParams<array{filter?: ?string, von?: ?string}> */
20class OlzNewsDetailParams extends HttpParams {
21}
22
23/** @extends OlzRootComponent<array<string, mixed>> */
24class OlzNewsDetail extends OlzRootComponent {
25    public function getSearchTitle(): string {
26        return 'News';
27    }
28
29    public function getSearchResults(array $terms): array {
30        $results = [];
31        $code_href = $this->envUtils()->getCodeHref();
32        $news_repo = $this->entityManager()->getRepository(NewsEntry::class);
33        $news = $news_repo->search($terms);
34        foreach ($news as $news_entry) {
35            $id = $news_entry->getId();
36            $results[] = $this->searchUtils()->getScoredSearchResult([
37                'link' => "{$code_href}news/{$id}",
38                'icon' => $this->newsUtils()->getNewsFormatIcon($news_entry) ?: null,
39                'date' => $news_entry->getPublishedDate(),
40                'title' => $news_entry->getTitle() ?: '?',
41                'text' => $news_entry->getTeaser()." ".$news_entry->getContent(),
42            ], $terms);
43        }
44        return $results;
45    }
46
47    public function getHtml(mixed $args): string {
48        $params = $this->httpUtils()->validateGetParams(OlzNewsDetailParams::class);
49        $code_href = $this->envUtils()->getCodeHref();
50        $db = $this->dbUtils()->getDb();
51        $entityManager = $this->dbUtils()->getEntityManager();
52        $user = $this->authUtils()->getCurrentUser();
53        $id = $args['id'] ?? null;
54
55        $news_utils = $this->newsUtils();
56        $news_repo = $entityManager->getRepository(NewsEntry::class);
57        $is_not_archived = $news_utils->getIsNotArchivedCriteria();
58        $criteria = Criteria::create()
59            ->where(Criteria::expr()->andX(
60                $is_not_archived,
61                Criteria::expr()->eq('id', $id),
62                Criteria::expr()->eq('on_off', 1),
63            ))
64            ->setFirstResult(0)
65            ->setMaxResults(1)
66        ;
67        $news_entries = $news_repo->matching($criteria);
68        $num_news_entries = $news_entries->count();
69        $is_archived = $num_news_entries !== 1;
70
71        if ($is_archived && !$this->authUtils()->hasPermission('any')) {
72            $this->httpUtils()->dieWithHttpError(404);
73            throw new \Exception('should already have failed');
74        }
75
76        $article_metadata = "";
77        try {
78            $article_metadata = OlzArticleMetadata::render(['id' => $id]);
79        } catch (\Exception $exc) {
80            $this->httpUtils()->dieWithHttpError(404);
81            throw new \Exception('should already have failed');
82        }
83
84        $news_entry = $this->getNewsEntryById($id);
85
86        if (!$news_entry) {
87            $this->httpUtils()->dieWithHttpError(404);
88            throw new \Exception('should already have failed');
89        }
90
91        $title = $news_entry->getTitle();
92        $back_filter = json_decode($params['filter'] ?? 'null', true);
93        $news_utils = $this->newsUtils();
94        if ($back_filter && !$news_utils->isValidFilter($back_filter)) {
95            $valid_filter = $news_utils->getValidFilter($back_filter);
96            $enc_json_filter = urlencode(json_encode($valid_filter) ?: '{}');
97            $this->httpUtils()->redirect("{$code_href}news/{$id}?filter={$enc_json_filter}", 308);
98        }
99        $enc_back_filter = urlencode(json_encode($back_filter ?: $news_utils->getDefaultFilter()) ?: '{}');
100        $out = OlzHeader::render([
101            'back_link' => "{$code_href}news?filter={$enc_back_filter}",
102            'title' => "{$title} - News",
103            'description' => "Aktuelle Beiträge, Berichte von Anlässen und weitere Neuigkeiten von der OL Zimmerberg.",
104            'norobots' => $is_archived,
105            'canonical_url' => "{$code_href}news/{$id}",
106            'additional_headers' => [
107                $article_metadata,
108            ],
109        ]);
110
111        $format = $news_entry->getFormat();
112        // TODO: Use array_find with PHP 8.4
113        $filtered = array_filter(
114            NewsUtils::ALL_FORMAT_OPTIONS,
115            fn ($entry) => $entry['ident'] === $format
116        );
117        // @phpstan-ignore-next-line
118        $found_entry = $filtered[array_keys($filtered)[0]];
119        $name = $found_entry['name'];
120        $icon = $found_entry['icon'] ?? null;
121        $icon_html = "<img src='{$code_href}assets/icns/{$icon}' alt='' class='format-icon'>";
122        $pretty_format = "{$icon_html}{$name}";
123
124        $pretty_date = $this->dateUtils()->olzDate("tt.mm.jjjj", $news_entry->getPublishedDate());
125        $author_user = $news_entry->getAuthorUser();
126        $author_role = $news_entry->getAuthorRole();
127        $author_name = $news_entry->getAuthorName();
128        $author_email = $news_entry->getAuthorEmail();
129        $pretty_author = OlzAuthorBadge::render([
130            'news_id' => $news_entry->getId() ?: 0,
131            'user' => $author_user,
132            'role' => $author_role,
133            'name' => $author_name,
134            'email' => $author_email,
135        ]);
136        $image_ids = $news_entry->getImageIds();
137        $num_images = count($image_ids);
138        $download_all_link = $this->authUtils()->hasPermission('any')
139            ? "<a href='{$code_href}news/{$id}/all.zip'>Alle herunterladen</a>" : '';
140
141        $out .= <<<ZZZZZZZZZZ
142            <div class='content-right'>
143                <div style='padding:4px 3px 10px 3px;'>
144                    <div id='format-info'><b>Format: </b>{$pretty_format}</div>
145                    <div><b>Datum: </b>{$pretty_date}</div>
146                    <div><b>Autor: </b>{$pretty_author}</div>
147                    <div><b>Anzahl Bilder: </b>{$num_images}</div>
148                    <div class='pretty'>{$download_all_link}</div>
149                </div>
150            </div>
151            <div class='content-middle'>
152            ZZZZZZZZZZ;
153
154        $db->query("UPDATE news SET `counter`=`counter` + 1 WHERE `id`='{$id}'");
155
156        $title = $news_entry->getTitle();
157        $teaser = $news_entry->getTeaser() ?? '';
158        $content = $news_entry->getContent() ?? '';
159        $published_date = $news_entry->getPublishedDate();
160
161        $published_date = $this->dateUtils()->olzDate("tt.mm.jj", $published_date);
162
163        $is_owner = $user && intval($news_entry->getOwnerUser()?->getId() ?? 0) === intval($user->getId());
164        $has_all_permissions = $this->authUtils()->hasPermission('all');
165        $can_edit = $is_owner || $has_all_permissions;
166        $edit_admin = '';
167        if ($can_edit) {
168            $json_id = json_encode($id);
169            $has_blog = $this->authUtils()->hasPermission('kaderblog', $user);
170            $has_roles = !empty($this->authUtils()->getAuthenticatedRoles());
171            $json_mode = htmlentities(json_encode($has_roles ? ($has_blog ? 'account_with_all' : 'account_with_aktuell') : ($has_blog ? 'account_with_blog' : 'account')) ?: '');
172            $edit_admin = <<<ZZZZZZZZZZ
173                <div>
174                    <button
175                        id='edit-news-button'
176                        class='btn btn-primary'
177                        onclick='return olz.editNews({$json_id}{$json_mode})'
178                    >
179                        <img src='{$code_href}assets/icns/edit_white_16.svg' class='noborder' />
180                        Bearbeiten
181                    </button>
182                </div>
183                ZZZZZZZZZZ;
184        }
185
186        // TODO: Temporary fix for broken Markdown
187        $content = str_replace("\n", "\n\n", $content);
188        $content = str_replace("\n\n\n\n", "\n\n", $content);
189
190        // Markdown
191        $html_input = $format === 'forum' ? 'escape' : 'allow'; // TODO: Do NOT allow!
192        $teaser = $this->htmlUtils()->renderMarkdown($teaser, [
193            'html_input' => $html_input,
194        ]);
195        $content = $this->htmlUtils()->renderMarkdown($content, [
196            'html_input' => $html_input,
197        ]);
198
199        // Datei- & Bildpfade
200        $teaser = $news_entry->replaceImagePaths($teaser);
201        $teaser = $news_entry->replaceFilePaths($teaser);
202        $content = $news_entry->replaceImagePaths($content);
203        $content = $news_entry->replaceFilePaths($content);
204
205        $out .= "<h1>{$edit_admin}{$title}</h1>";
206
207        $gallery = '';
208        $num_images = count($image_ids);
209        if ($num_images > 0) {
210            $gallery .= "<div class='lightgallery gallery-container'>";
211            foreach ($image_ids as $image_id) {
212                $gallery .= "<div class='gallery-image'>";
213                $gallery .= $this->imageUtils()->olzImage(
214                    'news',
215                    $id,
216                    $image_id,
217                    110,
218                    'gallery[myset]'
219                );
220                $gallery .= "</div>";
221            }
222            $gallery .= "</div>";
223        }
224
225        if ($format === 'aktuell') {
226            $out .= "<p><b>{$teaser}</b><p>{$content}</p><br/><br/>{$gallery}\n";
227        } elseif ($format === 'kaderblog') {
228            $out .= "<p>{$content}</p><br/><br/>{$gallery}\n";
229        } elseif ($format === 'forum') {
230            $out .= "<p><b>{$teaser}</b><p>{$content}</p><br/><br/>{$gallery}\n";
231        } elseif ($format === 'galerie') {
232            $out .= "<p>{$content}</p>{$gallery}\n";
233        } elseif ($format === 'video') {
234            $youtube_url = $news_entry->getExternalUrl() ?? '';
235            $res0 = preg_match("/^https\\:\\/\\/(www\\.)?youtu\\.be\\/([a-zA-Z0-9\\-\\_]{6,})/", $youtube_url, $matches0);
236            $res1 = preg_match("/^https\\:\\/\\/(www\\.)?youtube\\.com\\/watch\\?v\\=([a-zA-Z0-9\\-\\_]{6,})/", $youtube_url, $matches1);
237            $youtube_match = null;
238            if ($res0) {
239                $youtube_match = $matches0[2];
240            }
241            if ($res1) {
242                $youtube_match = $matches1[2];
243            }
244
245            $out .= "<div class='video-container'>";
246            $out .= "<div style='background-image:url({$code_href}assets/icns/movie_dot.svg);background-repeat:repeat-x;margin:0px;padding:0px;height:24px;'></div>\n";
247            if ($youtube_match != null) {
248                $out .= "<iframe width='560' height='315' src='https://www.youtube.com/embed/{$youtube_match}' frameborder='0' allow='accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture' allowfullscreen></iframe>";
249            } else {
250                $this->log()->error("Invalid YouTube link (ID:{$id}): {$youtube_url}");
251                $out .= "Fehlerhafter YouTube-Link!";
252            }
253            $out .= "<div style='background-image:url({$code_href}assets/icns/movie_dot.svg);background-repeat:repeat-x;margin:0px;padding:0px;height:24px;'></div>";
254            $out .= "</div>";
255        } else {
256            $out .= "<div class='lightgallery'><p><b>{$teaser}</b><p>{$content}</p></div>\n";
257        }
258        $out .= "</div>";
259
260        $out .= OlzFooter::render();
261
262        return $out;
263    }
264
265    protected function getNewsEntryById(int $id): ?NewsEntry {
266        $news_repo = $this->entityManager()->getRepository(NewsEntry::class);
267        return $news_repo->findOneBy([
268            'id' => $id,
269            'on_off' => 1,
270        ]);
271    }
272}