Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 229
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
OlzTerminDetailParams
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
OlzTerminDetail
0.00% covered (danger)
0.00%
0 / 229
0.00% covered (danger)
0.00%
0 / 6
3192
0.00% covered (danger)
0.00%
0 / 1
 hasAccess
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 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
 getHtmlWhenHasAccess
0.00% covered (danger)
0.00%
0 / 205
0.00% covered (danger)
0.00%
0 / 1
2162
 getTerminById
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 getTimeText
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3namespace Olz\Termine\Components\OlzTerminDetail;
4
5use Doctrine\Common\Collections\Criteria;
6use Olz\Components\Common\OlzLocationMap\OlzLocationMap;
7use Olz\Components\Common\OlzRootComponent;
8use Olz\Components\Page\OlzFooter\OlzFooter;
9use Olz\Components\Page\OlzHeader\OlzHeader;
10use Olz\Components\Schema\OlzEventData\OlzEventData;
11use Olz\Entity\Termine\Termin;
12use Olz\Entity\Termine\TerminLabel;
13use Olz\Termine\Components\OlzDateCalendar\OlzDateCalendar;
14use Olz\Utils\HttpParams;
15
16/** @extends HttpParams<array{von?: ?string}> */
17class OlzTerminDetailParams extends HttpParams {
18}
19
20/** @extends OlzRootComponent<array<string, mixed>> */
21class OlzTerminDetail extends OlzRootComponent {
22    public function hasAccess(): bool {
23        return true;
24    }
25
26    public function getSearchTitle(): string {
27        return 'Termine';
28    }
29
30    public function getSearchResults(array $terms): array {
31        $results = [];
32        $code_href = $this->envUtils()->getCodeHref();
33        $termin_repo = $this->entityManager()->getRepository(Termin::class);
34        $termine = $termin_repo->search($terms);
35        foreach ($termine as $termin) {
36            $id = $termin->getId();
37            $results[] = $this->searchUtils()->getScoredSearchResult([
38                'link' => "{$code_href}termine/{$id}",
39                'icon' => "{$code_href}assets/icns/termine_type_all_20.svg",
40                'date' => $termin->getStartDate(),
41                'title' => $termin->getTitle() ?: '?',
42                'text' => strip_tags("{$termin->getText()}") ?: null,
43            ], $terms);
44        }
45        return $results;
46    }
47
48    public function getHtmlWhenHasAccess(mixed $args): string {
49        $this->httpUtils()->validateGetParams(OlzTerminDetailParams::class);
50
51        $code_href = $this->envUtils()->getCodeHref();
52        $code_path = $this->envUtils()->getCodePath();
53        $data_path = $this->envUtils()->getDataPath();
54        $date_utils = $this->dateUtils();
55        $today = $date_utils->getIsoToday();
56        $entityManager = $this->dbUtils()->getEntityManager();
57        $user = $this->authUtils()->getCurrentUser();
58        $id = $args['id'] ?? null;
59
60        $termin_repo = $entityManager->getRepository(Termin::class);
61        $is_not_archived = $this->termineUtils()->getIsNotArchivedCriteria();
62        $criteria = Criteria::create()
63            ->where(Criteria::expr()->andX(
64                $is_not_archived,
65                Criteria::expr()->eq('id', $id),
66                Criteria::expr()->eq('on_off', 1),
67            ))
68            ->setFirstResult(0)
69            ->setMaxResults(1)
70        ;
71        $termine = $termin_repo->matching($criteria);
72        $num_termine = $termine->count();
73        $is_archived = $num_termine !== 1;
74
75        if ($is_archived && !$this->authUtils()->hasPermission('any')) {
76            $this->httpUtils()->dieWithHttpError(404);
77            throw new \Exception('should already have failed');
78        }
79
80        $termin = $this->getTerminById($id);
81
82        if (!$termin) {
83            $this->httpUtils()->dieWithHttpError(404);
84            throw new \Exception('should already have failed');
85        }
86
87        $title = $termin->getTitle() ?? '';
88        $termin_year = $termin->getStartDate()->format('Y');
89        $this_year = $this->dateUtils()->getCurrentDateInFormat('Y');
90        $maybe_date = ($termin_year !== $this_year) ? " {$termin_year}" : '';
91        $title = "{$title}{$maybe_date}";
92        $out = OlzHeader::render([
93            'back_link' => "{$code_href}termine",
94            'title' => "{$title} - Termine",
95            'description' => "Orientierungslauf-Wettkämpfe, OL-Wochen, OL-Weekends, Trainings und Vereinsanlässe der OL Zimmerberg.",
96            'norobots' => $is_archived,
97            'canonical_url' => "{$code_href}termine/{$id}",
98        ]);
99
100        $out .= <<<'ZZZZZZZZZZ'
101            <div class='content-right optional'>
102                <div style='padding:4px 3px 10px 3px;'>
103                </div>
104            </div>
105            <div class='content-middle'>
106            ZZZZZZZZZZ;
107
108        $start_date = $termin->getStartDate();
109        $end_date = $termin->getEndDate() ?? null;
110        $start_time = $termin->getStartTime() ?? null;
111        $end_time = $termin->getEndTime() ?? null;
112        $text = $termin->getText() ?? '';
113        $labels = [...$termin->getLabels()];
114        $xkoord = $termin->getCoordinateX() ?? 0;
115        $ykoord = $termin->getCoordinateY() ?? 0;
116        $solv_uid = $termin->getSolvId();
117        $termin_location = $termin->getLocation();
118        $has_olz_location = ($xkoord > 0 && $ykoord > 0);
119        $has_termin_location = (
120            $termin_location
121            && $termin_location->getLatitude() > 0
122            && $termin_location->getLongitude() > 0
123        );
124        $lat = null;
125        $lng = null;
126        $location_name = null;
127        if ($has_termin_location) {
128            $lat = $termin_location->getLatitude();
129            $lng = $termin_location->getLongitude();
130            $location_name = $termin_location->getName();
131        }
132        if ($has_olz_location) {
133            $lat = $this->mapUtils()->CHtoWGSlat($xkoord, $ykoord);
134            $lng = $this->mapUtils()->CHtoWGSlng($xkoord, $ykoord);
135            $location_name = null;
136        }
137        $has_location = $has_olz_location || $has_termin_location;
138        $image_ids = $termin->getImageIds();
139
140        $out .= OlzEventData::render([
141            'name' => $title,
142            'start_date' => $date_utils->olzDate('jjjj-mm-tt', $start_date),
143            'end_date' => $end_date ? $date_utils->olzDate('jjjj-mm-tt', $end_date) : null,
144            'location' => $has_location ? [
145                'lat' => $lat,
146                'lng' => $lng,
147                'name' => $location_name,
148            ] : null,
149        ]);
150
151        $out .= "<div class='olz-termin-detail'>";
152
153        $out .= "<div class='preview'>";
154        // Bild anzeigen
155        if (count($image_ids) > 0) {
156            $out .= $this->imageUtils()->olzImage(
157                'termine',
158                $id,
159                $image_ids[0],
160                840
161            );
162        // Karte zeigen
163        } elseif ($has_location) {
164            $out .= OlzLocationMap::render([
165                'latitude' => $lat,
166                'longitude' => $lng,
167                'zoom' => 13,
168            ]);
169        }
170        // Date Calendar Icon
171        $out .= "<div class='date-calendar-container'>";
172        $out .= "<div class='date-calendars'>";
173        $out .= "<div class='date-calendar'>";
174        $out .= OlzDateCalendar::render(['date' => $start_date]);
175        $out .= $this->getTimeText($start_time) ?? '';
176        $out .= ($end_time && (!$end_date || $end_date === $start_date))
177            ? ' &ndash; '.$this->getTimeText($end_time)
178            : '';
179        $out .= "</div>";
180        $out .= "<div class='date-calendar'>";
181        $out .= ($end_date && $end_date !== $start_date)
182            ? OlzDateCalendar::render(['date' => $end_date])
183            : '';
184        $out .= ($end_time && $end_date && $end_date !== $start_date)
185            ? $this->getTimeText($end_time)
186            : '';
187        $out .= "</div>";
188        $out .= "</div>";
189        $out .= "</div>";
190
191        $out .= "</div>";
192
193        // Editing Tools
194        $is_owner = $user && intval($termin->getOwnerUser()?->getId() ?? 0) === intval($user->getId());
195        $has_termine_permissions = $this->authUtils()->hasPermission('termine');
196        $can_edit = $is_owner || $has_termine_permissions;
197        if ($can_edit) {
198            $json_id = json_encode($id);
199            $out .= <<<ZZZZZZZZZZ
200                <div>
201                    <button
202                        id='edit-termin-button'
203                        class='btn btn-primary'
204                        onclick='return olz.editTermin({$json_id})'
205                    >
206                        <img src='{$code_href}assets/icns/edit_white_16.svg' class='noborder' />
207                        Bearbeiten
208                    </button>
209                </div>
210                ZZZZZZZZZZ;
211        }
212
213        // Date & Title
214        $pretty_date = $this->dateUtils()->formatDateTimeRange(
215            $start_date->format('Y-m-d'),
216            $start_time?->format('H:i:s'),
217            $end_date?->format('Y-m-d'),
218            $end_time?->format('H:i:s'),
219            $format = 'long',
220        );
221        $maybe_solv_link = '';
222        if ($solv_uid) {
223            // SOLV-Übersicht-Link zeigen
224            $maybe_solv_link .= "<a href='https://www.o-l.ch/cgi-bin/fixtures?&mode=show&unique_id={$solv_uid}' target='_blank' class='linkol' style='margin-left: 20px; font-weight: normal;'>O-L.ch</a>\n";
225        }
226        $label_imgs = implode('', array_map(function (TerminLabel $label) use ($code_path, $code_href) {
227            $ident = $label->getIdent();
228            // TODO: Remove fallback mechanism?
229            $fallback_path = "{$code_path}assets/icns/termine_type_{$ident}_20.svg";
230            $fallback_href = is_file($fallback_path)
231                ? "{$code_href}assets/icns/termine_type_{$ident}_20.svg" : null;
232            $icon_href = $label->getIcon() ? $label->getFileHref($label->getIcon()) : $fallback_href;
233            return $icon_href ? "<img src='{$icon_href}' alt='' class='type-icon'>" : '';
234        }, $labels));
235        $out .= "<h5>{$pretty_date}{$maybe_solv_link}</h5>";
236        $out .= "<h1>{$title} {$label_imgs}</h1>";
237
238        // Text
239        // TODO: Temporary fix for broken Markdown
240        $text = str_replace("\n", "\n\n", $text);
241        $text = str_replace("\n\n\n\n", "\n\n", $text);
242        $text_html = $this->htmlUtils()->renderMarkdown($text, [
243            'html_input' => 'allow', // TODO: Do NOT allow!
244        ]);
245        $text_html = $termin->replaceImagePaths($text_html);
246        $text_html = $termin->replaceFilePaths($text_html);
247        if ($termin->getDeadline() && $termin->getDeadline() != "0000-00-00") {
248            $text_html .= ($text_html == "" ? "" : "<br />")."Meldeschluss: ".$date_utils->olzDate("t. MM ", $termin->getDeadline());
249        }
250        $out .= "<div>".$text_html."</div>";
251
252        // Link
253        $link = '';
254        if ($solv_uid && $start_date <= $today && !preg_match('/(Rangliste|Resultat)/', $link)) {
255            // SOLV Ranglisten-Link zeigen
256            $link .= "<div><a href='http://www.o-l.ch/cgi-bin/results?unique_id={$solv_uid}&club=zimmerberg' target='_blank' class='linkol'>Rangliste</a></div>\n";
257        }
258        $result_filename = "{$termin_year}-termine-{$id}.xml";
259        if (is_file("{$data_path}results/{$result_filename}")) {
260            // OLZ Ranglisten-Link zeigen
261            $link .= "<div><a href='{$code_href}apps/resultate?file={$result_filename}' target='_blank' class='linkext'>Ranglisten</a></div>\n";
262        } elseif ($can_edit) {
263            // OLZ Rangliste-hochladen-Link zeigen
264            $link .= "<div><a href='{$code_href}apps/resultate?file={$result_filename}' target='_blank' class='linkext'>Rangliste hochladen</a></div>\n";
265        }
266        if ($link == "") {
267            $link = "&nbsp;";
268        } else {
269            $link = str_replace("&", "&amp;", str_replace("&amp;", "&", $link));
270        }
271        $link = str_replace("www.solv.ch", "www.o-l.ch", $link);
272        $out .= "<div class='links'>".$link."</div>";
273
274        // Karte zeigen
275        if ($has_location) {
276            if ($location_name !== null) {
277                $location_maybe_link = $location_name;
278                if ($has_termin_location && $this->authUtils()->hasPermission('termine')) {
279                    $location_maybe_link = "<a href='{$code_href}termine/orte/{$termin_location->getId()}' class='linkmap'>{$location_name}</a>";
280                }
281                $out .= "<h3>Ort: {$location_maybe_link}</h3>";
282            } else {
283                $out .= "<h3>Ort</h3>";
284            }
285            $out .= OlzLocationMap::render([
286                'name' => $location_name ?? '',
287                'latitude' => $lat,
288                'longitude' => $lng,
289                'zoom' => 13,
290            ]);
291            if ($has_termin_location) {
292                $details = $termin_location->getDetails() ?? '';
293                $details_html = $this->htmlUtils()->renderMarkdown($details);
294                $details_html = $termin_location->replaceImagePaths($details_html);
295                $details_html = $termin_location->replaceFilePaths($details_html);
296                $out .= $details_html;
297
298                $location_image_ids = $termin_location->getImageIds();
299                if (count($location_image_ids) > 0) {
300                    $out .= "<div class='lightgallery gallery-container'>";
301                    foreach ($location_image_ids as $image_id) {
302                        $out .= "<div class='gallery-image'>";
303                        $out .= $this->imageUtils()->olzImage(
304                            'termin_locations',
305                            $termin_location->getId(),
306                            $image_id,
307                            110,
308                            'gallery[myset]'
309                        );
310                        $out .= "</div>";
311                    }
312                    $out .= "</div>";
313                }
314            }
315        }
316
317        $out .= "</div>"; // olz-termin-detail
318        $out .= "</div>"; // content-middle
319
320        $out .= OlzFooter::render();
321
322        return $out;
323    }
324
325    protected function getTerminById(int $id): ?Termin {
326        $termin_repo = $this->entityManager()->getRepository(Termin::class);
327        return $termin_repo->findOneBy([
328            'id' => $id,
329            'on_off' => 1,
330        ]);
331    }
332
333    protected function getTimeText(?\DateTime $time): ?string {
334        if (!$time || $time->format('H:i:s') === '00:00:00') {
335            return null;
336        }
337        return $time->format('H:i');
338    }
339}