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 / 5
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 / 5
552
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
 getSearchResultsWhenHasAccess
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getHtmlWhenHasAccess
0.00% covered (danger)
0.00%
0 / 116
0.00% covered (danger)
0.00%
0 / 1
342
 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\OlzEditableText\OlzEditableText;
10use Olz\Components\Common\OlzRootComponent;
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\Utils\HttpParams;
18
19/** @extends HttpParams<array{filter?: ?string, von?: ?string}> */
20class OlzTermineListParams extends HttpParams {
21}
22
23/** @extends OlzRootComponent<array<string, mixed>> */
24class 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}