Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 122
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
OlzTermineListParams
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
OlzTermineList
0.00% covered (danger)
0.00%
0 / 122
0.00% covered (danger)
0.00%
0 / 2
552
0.00% covered (danger)
0.00%
0 / 1
 getHtml
0.00% covered (danger)
0.00%
0 / 119
0.00% covered (danger)
0.00%
0 / 1
462
 getMonth
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3// =============================================================================
4// Zeigt geplante und vergangene Termine an.
5// =============================================================================
6
7namespace Olz\Termine\Components\OlzTermineList;
8
9use Olz\Components\Common\OlzComponent;
10use Olz\Components\Common\OlzEditableText\OlzEditableText;
11use Olz\Components\Page\OlzFooter\OlzFooter;
12use Olz\Components\Page\OlzHeader\OlzHeader;
13use Olz\Entity\Termine\Termin;
14use Olz\Entity\Termine\TerminLabel;
15use Olz\Termine\Components\OlzTermineFilter\OlzTermineFilter;
16use Olz\Termine\Components\OlzTermineListItem\OlzTermineListItem;
17use Olz\Termine\Utils\TermineFilterUtils;
18use Olz\Utils\HttpParams;
19
20/** @extends HttpParams<array{filter?: ?string, von?: ?string}> */
21class OlzTermineListParams extends HttpParams {
22}
23
24/** @extends OlzComponent<array<string, mixed>> */
25class 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}