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