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