Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 122 |
|
0.00% |
0 / 2 |
CRAP | |
0.00% |
0 / 1 |
OlzTermineListParams | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | n/a |
0 / 0 |
|||
OlzTermineList | |
0.00% |
0 / 122 |
|
0.00% |
0 / 2 |
552 | |
0.00% |
0 / 1 |
getHtml | |
0.00% |
0 / 119 |
|
0.00% |
0 / 1 |
462 | |||
getMonth | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | |
3 | // ============================================================================= |
4 | // Zeigt geplante und vergangene Termine an. |
5 | // ============================================================================= |
6 | |
7 | namespace Olz\Termine\Components\OlzTermineList; |
8 | |
9 | use Olz\Components\Common\OlzComponent; |
10 | use Olz\Components\Common\OlzEditableText\OlzEditableText; |
11 | use Olz\Components\Page\OlzFooter\OlzFooter; |
12 | use Olz\Components\Page\OlzHeader\OlzHeader; |
13 | use Olz\Entity\Termine\Termin; |
14 | use Olz\Entity\Termine\TerminLabel; |
15 | use Olz\Termine\Components\OlzTermineFilter\OlzTermineFilter; |
16 | use Olz\Termine\Components\OlzTermineListItem\OlzTermineListItem; |
17 | use Olz\Termine\Utils\TermineFilterUtils; |
18 | use Olz\Utils\HttpParams; |
19 | |
20 | /** @extends HttpParams<array{filter?: ?string, von?: ?string}> */ |
21 | class OlzTermineListParams extends HttpParams { |
22 | } |
23 | |
24 | /** @extends OlzComponent<array<string, mixed>> */ |
25 | class OlzTermineList extends OlzComponent { |
26 | public static string $title = "Termine"; |
27 | public static string $description = "Orientierungslauf-Wettkämpfe, OL-Wochen, OL-Weekends, Trainings und Vereinsanlässe der OL Zimmerberg."; |
28 | |
29 | public function getHtml(mixed $args): string { |
30 | /** @return array{filter?: ?string} */ |
31 | $params = $this->httpUtils()->validateGetParams(OlzTermineListParams::class); |
32 | $db = $this->dbUtils()->getDb(); |
33 | $code_href = $this->envUtils()->getCodeHref(); |
34 | |
35 | $current_filter = json_decode($params['filter'] ?? '{}', true); |
36 | $termine_utils = TermineFilterUtils::fromEnv()->loadTypeOptions(); |
37 | |
38 | if (!$termine_utils->isValidFilter($current_filter)) { |
39 | $valid_filter = $termine_utils->getValidFilter($current_filter); |
40 | $enc_json_filter = urlencode(json_encode($valid_filter) ?: '{}'); |
41 | $this->httpUtils()->redirect("{$code_href}termine?filter={$enc_json_filter}", 308); |
42 | } |
43 | |
44 | $termine_list_title = $termine_utils->getTitleFromFilter($current_filter); |
45 | $is_not_archived = $termine_utils->isFilterNotArchived($current_filter); |
46 | $allow_robots = $is_not_archived; |
47 | $enc_json_filter = urlencode(json_encode($current_filter) ?: '{}'); |
48 | |
49 | $out = OlzHeader::render([ |
50 | 'title' => $termine_list_title, |
51 | 'description' => self::$description, // TODO: Filter-specific description? |
52 | 'norobots' => !$allow_robots, |
53 | 'canonical_url' => "{$code_href}termine?filter={$enc_json_filter}", |
54 | ]); |
55 | |
56 | $admin_menu_out = ''; |
57 | $has_termine_permissions = $this->authUtils()->hasPermission('termine'); |
58 | if ($has_termine_permissions) { |
59 | $admin_menu_out = <<<ZZZZZZZZZZ |
60 | <div class='termine-list-admin-menu'> |
61 | <span class='entry'> |
62 | <a href='{$code_href}termine/orte' class='linkmap'> |
63 | Termin-Orte |
64 | </a> |
65 | </span> |
66 | <span class='entry'> |
67 | <a href='{$code_href}termine/vorlagen' class='linkint'> |
68 | Termin-Vorlagen |
69 | </a> |
70 | </span> |
71 | </div> |
72 | ZZZZZZZZZZ; |
73 | } |
74 | $filter_out = OlzTermineFilter::render(); |
75 | $downloads_links_out = OlzEditableText::render(['snippet_id' => 2]); |
76 | $newsletter_out = OlzEditableText::render(['snippet_id' => 3]); |
77 | $out .= <<<ZZZZZZZZZZ |
78 | <div class='content-right'> |
79 | {$admin_menu_out} |
80 | <h2 class='optional'>Filter</h2> |
81 | {$filter_out} |
82 | <div class='optional'> |
83 | <h2>Downloads und Links</h2> |
84 | {$downloads_links_out} |
85 | </div> |
86 | <div class='optional'> |
87 | <h2>Newsletter</h2> |
88 | {$newsletter_out} |
89 | </div> |
90 | </div> |
91 | <div class='content-middle olz-termine-list-middle'> |
92 | ZZZZZZZZZZ; |
93 | |
94 | $has_access = $this->authUtils()->hasPermission('termine'); |
95 | if ($has_access) { |
96 | $out .= <<<ZZZZZZZZZZ |
97 | <button |
98 | id='create-termin-button' |
99 | class='btn btn-secondary create-termin-container' |
100 | onclick='return olz.initOlzEditTerminModal()' |
101 | > |
102 | <img src='{$code_href}assets/icns/new_white_16.svg' class='noborder' /> |
103 | Neuer Termin |
104 | </button> |
105 | ZZZZZZZZZZ; |
106 | } |
107 | |
108 | $termin_repo = $this->entityManager()->getRepository(Termin::class); |
109 | $termin_label_repo = $this->entityManager()->getRepository(TerminLabel::class); |
110 | $termin_label = $termin_label_repo->findOneBy(['ident' => $current_filter['typ']]); |
111 | $edit_admin = ''; |
112 | if ($termin_label) { |
113 | $has_termine_permissions = $this->authUtils()->hasPermission('termine'); |
114 | if ($has_termine_permissions) { |
115 | $json_id = json_encode($termin_label->getId()); |
116 | $edit_admin = <<<ZZZZZZZZZZ |
117 | <button |
118 | id='edit-termin-label-button' |
119 | class='btn btn-secondary-outline btn-sm' |
120 | onclick='return olz.termineListEditTerminLabel({$json_id})' |
121 | > |
122 | <img src='{$code_href}assets/icns/edit_16.svg' class='noborder' /> |
123 | </button> |
124 | ZZZZZZZZZZ; |
125 | } |
126 | } |
127 | $out .= "<h1>{$termine_list_title}{$edit_admin}</h1>"; |
128 | $details = $termin_label?->getDetails(); |
129 | if ($details) { |
130 | $details_html = $this->htmlUtils()->renderMarkdown($details); |
131 | $details_html = $termin_label->replaceImagePaths($details_html); |
132 | $details_html = $termin_label->replaceFilePaths($details_html); |
133 | $out .= $details_html; |
134 | } |
135 | |
136 | // ------------------------------------------------------------- |
137 | // VORSCHAU - LISTE |
138 | $inner_date_filter = $termine_utils->getSqlDateRangeFilter($current_filter, 't'); |
139 | $inner_sql_where = <<<ZZZZZZZZZZ |
140 | (t.on_off = '1') |
141 | AND ({$inner_date_filter}) |
142 | ZZZZZZZZZZ; |
143 | $type_filter = $termine_utils->getSqlTypeFilter($current_filter, 'c'); |
144 | $outer_date_filter = $termine_utils->getSqlDateRangeFilter($current_filter, 'c'); |
145 | $outer_sql_where = "({$type_filter}) AND ({$outer_date_filter})"; |
146 | |
147 | $sql = <<<ZZZZZZZZZZ |
148 | SELECT * FROM (( |
149 | SELECT |
150 | 'termin' as item_type, |
151 | t.owner_user_id as owner_user_id, |
152 | t.start_date as start_date, |
153 | t.start_time as start_time, |
154 | t.end_date as end_date, |
155 | t.end_time as end_time, |
156 | t.title as title, |
157 | t.text as text, |
158 | t.id as id, |
159 | ( |
160 | SELECT GROUP_CONCAT(l.ident ORDER BY l.position ASC SEPARATOR ' ') |
161 | FROM |
162 | termin_label_map tl |
163 | JOIN termin_labels l ON (l.id = tl.label_id) |
164 | WHERE tl.termin_id = t.id |
165 | GROUP BY t.id |
166 | ) as typ, |
167 | t.on_off as on_off, |
168 | t.newsletter as newsletter, |
169 | t.xkoord as xkoord, |
170 | t.ykoord as ykoord, |
171 | t.go2ol as go2ol, |
172 | t.solv_uid as solv_uid, |
173 | t.last_modified_by_user_id as last_modified_by_user_id, |
174 | t.image_ids as image_ids, |
175 | t.location_id as location_id |
176 | FROM termine t |
177 | WHERE ({$inner_sql_where}) |
178 | ) UNION ALL ( |
179 | SELECT |
180 | 'deadline' as item_type, |
181 | t.owner_user_id as owner_user_id, |
182 | DATE(t.deadline) as start_date, |
183 | TIME(t.deadline) as start_time, |
184 | NULL as end_date, |
185 | NULL as end_time, |
186 | CONCAT('Meldeschluss für ', t.title) as title, |
187 | '' as text, |
188 | t.id as id, |
189 | 'meldeschluss' as typ, |
190 | t.on_off as on_off, |
191 | NULL as newsletter, |
192 | NULL as xkoord, |
193 | NULL as ykoord, |
194 | t.go2ol as go2ol, |
195 | t.solv_uid as solv_uid, |
196 | t.last_modified_by_user_id as last_modified_by_user_id, |
197 | t.image_ids as image_ids, |
198 | NULL as location_id |
199 | FROM termine t |
200 | WHERE (t.deadline IS NOT NULL) AND ({$inner_sql_where}) |
201 | )) AS c |
202 | WHERE ({$outer_sql_where}) |
203 | ORDER BY c.start_date ASC |
204 | ZZZZZZZZZZ; |
205 | |
206 | $has_archive_access = $this->authUtils()->hasPermission('verified_email'); |
207 | if ($is_not_archived || $has_archive_access) { |
208 | $result = $db->query($sql); |
209 | $today = $this->dateUtils()->getCurrentDateInFormat('Y-m-d'); |
210 | $meldeschluss_label = new TerminLabel(); |
211 | $meldeschluss_label->setIdent('meldeschluss'); |
212 | $meldeschluss_label->setIcon(null); |
213 | $last_date = null; |
214 | // @phpstan-ignore-next-line |
215 | while ($row = $result->fetch_assoc()) { |
216 | $this_date = $row['start_date']; |
217 | // @phpstan-ignore-next-line |
218 | $this_month_start = $this->getMonth($this_date).'-01'; |
219 | |
220 | if ($today < $this_month_start && $today > $last_date) { |
221 | $out .= "<div class='bar today'>Heute</div>"; |
222 | } |
223 | // @phpstan-ignore-next-line |
224 | if ($this->getMonth($this_date) !== $this->getMonth($last_date)) { |
225 | // @phpstan-ignore-next-line |
226 | $pretty_month = $this->dateUtils()->olzDate("MM jjjj", $this_date); |
227 | $out .= "<h3 class='bar green'>{$pretty_month}</h3>"; |
228 | } |
229 | if ($today <= $this_date && $today > $last_date && $today >= $this_month_start) { |
230 | $out .= "<div class='bar today'>Heute</div>"; |
231 | } |
232 | $labels = [$meldeschluss_label]; |
233 | if ($row['item_type'] === 'termin') { |
234 | $termin = $termin_repo->findOneBy(['id' => $row['id']]); |
235 | $labels = [...($termin?->getLabels() ?? [])]; |
236 | } |
237 | |
238 | $out .= OlzTermineListItem::render([ |
239 | 'id' => $row['id'], |
240 | 'owner_user_id' => $row['owner_user_id'], |
241 | 'start_date' => $row['start_date'], |
242 | 'start_time' => $row['start_time'], |
243 | 'end_date' => $row['end_date'], |
244 | 'end_time' => $row['end_time'], |
245 | 'title' => $row['title'], |
246 | 'text' => $row['text'], |
247 | 'solv_uid' => $row['solv_uid'], |
248 | 'labels' => $labels, |
249 | // @phpstan-ignore-next-line |
250 | 'image_ids' => $row['image_ids'] ? json_decode($row['image_ids'], true) : null, |
251 | 'location_id' => $row['location_id'], |
252 | ]); |
253 | |
254 | $last_date = $this_date; |
255 | } |
256 | if ($last_date === null) { |
257 | $out .= "<div class='no-entries'>Keine Einträge. Bitte Filter anpassen.</div>"; |
258 | } |
259 | $out .= "</div>"; |
260 | } else { |
261 | $out .= <<<'ZZZZZZZZZZ' |
262 | <div class='olz-no-access'> |
263 | <div>Das Archiv ist nur für Vereins-Mitglieder verfügbar.</div> |
264 | <div class='auth-buttons'> |
265 | <a class='btn btn-primary' href='#login-dialog' role='button'>Login</a> |
266 | </div> |
267 | </div> |
268 | ZZZZZZZZZZ; |
269 | } |
270 | |
271 | $out .= OlzFooter::render(); |
272 | |
273 | return $out; |
274 | } |
275 | |
276 | protected function getMonth(?string $date): ?string { |
277 | if ($date === null) { |
278 | return null; |
279 | } |
280 | return substr($date, 0, 7); |
281 | } |
282 | } |