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