Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 143
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
OlzNewsListParams
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
OlzNewsList
0.00% covered (danger)
0.00%
0 / 143
0.00% covered (danger)
0.00%
0 / 5
870
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 / 14
0.00% covered (danger)
0.00%
0 / 1
2
 getPageTitle
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getPageDescription
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 / 126
0.00% covered (danger)
0.00%
0 / 1
650
1<?php
2
3namespace Olz\News\Components\OlzNewsList;
4
5use Olz\Apps\OlzApps;
6use Olz\Components\Common\OlzRootComponent;
7use Olz\Components\Page\OlzFooter\OlzFooter;
8use Olz\Components\Page\OlzHeader\OlzHeader;
9use Olz\Entity\News\NewsEntry;
10use Olz\Entity\Roles\Role;
11use Olz\Entity\Users\User;
12use Olz\News\Components\OlzNewsFilter\OlzNewsFilter;
13use Olz\News\Components\OlzNewsListItem\OlzNewsListItem;
14use Olz\News\Utils\NewsUtils;
15use Olz\Utils\HttpParams;
16
17/** @extends HttpParams<array{
18 *   filter?: ?string,
19 *   seite?: ?numeric-string,
20 *   von?: ?string,
21 * }> */
22class OlzNewsListParams extends HttpParams {
23}
24
25/** @extends OlzRootComponent<array<string, mixed>> */
26class OlzNewsList extends OlzRootComponent {
27    public function hasAccess(): bool {
28        return true;
29    }
30
31    public function searchSqlWhenHasAccess(array $terms): string|array|null {
32        $format_options = NewsUtils::ALL_FORMAT_OPTIONS;
33        $queries = array_map(function ($format_option) use ($terms) {
34            $ident = $format_option['ident'];
35            $valid_filter = $this->newsUtils()->getValidFilter([
36                'format' => $ident,
37            ]);
38            return $this->searchUtils()->getStaticResultQuery([
39                'link' => $this->newsUtils()->getUrl($valid_filter),
40                'icon' => $this->newsUtils()->getNewsFormatIcon($ident),
41                'title' => "News: {$format_option['name']}",
42                'text' => $this->getPageDescription(),
43            ], $terms);
44        }, $format_options);
45        return $this->searchUtils()->unionAllQueries($queries);
46    }
47
48    public function getPageTitle(): string {
49        return "News";
50    }
51
52    public function getPageDescription(): string {
53        return "Aktuelle Beiträge, Berichte von Anlässen, Foto-Galerien und weitere Neuigkeiten vom Vorstand, Kaderläufern und anderen Mitgliedern der OL Zimmerberg.";
54    }
55
56    public static int $page_size = 25;
57
58    public function getHtmlWhenHasAccess(mixed $args): string {
59        $params = $this->httpUtils()->validateGetParams(OlzNewsListParams::class);
60        $db = $this->dbUtils()->getDb();
61        $entityManager = $this->dbUtils()->getEntityManager();
62        $code_href = $this->envUtils()->getCodeHref();
63
64        $news_utils = $this->newsUtils();
65        $current_filter = $news_utils->deserialize($params['filter'] ?? $this->session()->get('news_filter') ?? '');
66        $page_number = intval($params['seite'] ?? $this->session()->get('news_page') ?? 1);
67        $page_index = $page_number - 1;
68
69        $valid_filter = $news_utils->getValidFilter($current_filter);
70        $serialized_filter = $news_utils->serialize($valid_filter);
71
72        if (
73            !$news_utils->isValidFilter($current_filter)
74            || ($params['filter'] ?? '') !== $serialized_filter
75            || ($params['seite'] ?? '') !== "{$page_number}"
76        ) {
77            $is_backfill = empty($params['filter']) || empty($params['seite']);
78            $this->httpUtils()->redirect(
79                "{$code_href}news?filter={$serialized_filter}&seite={$page_number}",
80                $is_backfill ? 308 : 410,
81            );
82        }
83
84        $this->session()->set('news_filter', $serialized_filter);
85        $this->session()->set('news_page', "{$page_number}");
86
87        $news_list_title = $news_utils->getTitleFromFilter($valid_filter);
88        $out = OlzHeader::render([
89            'title' => $news_list_title,
90            'description' => $this->getPageDescription(), // TODO: Filter-specific description?
91            'canonical_url' => "{$code_href}news?filter={$serialized_filter}&seite={$page_number}",
92        ]);
93
94        $out .= "<div class='content-right'>";
95        $out .= "<h2 class='optional'>Filter</h2>";
96        $out .= OlzNewsFilter::render(['currentFilter' => $valid_filter]);
97        $out .= "</div>";
98        $out .= "<div class='content-middle olz-news-list-middle'>";
99
100        $is_logged_in = $this->authUtils()->hasPermission('any');
101        $has_blog = $this->authUtils()->hasPermission('kaderblog');
102        $has_roles = !empty($this->authUtils()->getAuthenticatedRoles());
103        $json_mode = htmlentities(json_encode($has_roles ? ($has_blog ? 'account_with_all' : 'account_with_aktuell') : ($has_blog ? 'account_with_blog' : 'account')) ?: '');
104        $class = $is_logged_in ? ' create-news-container' : ' dropdown-toggle';
105        $properties = $is_logged_in
106            ? <<<ZZZZZZZZZZ
107                onclick='return olz.initOlzEditNewsModal({$json_mode})'
108                ZZZZZZZZZZ
109            : <<<'ZZZZZZZZZZ'
110                type='button'
111                data-bs-toggle='dropdown'
112                aria-expanded='false'
113                ZZZZZZZZZZ;
114        if (!$is_logged_in) {
115            $out .= "<div class='dropdown create-news-container'>";
116        }
117        $out .= <<<ZZZZZZZZZZ
118            <button
119                id='create-news-button'
120                class='btn btn-secondary{$class}'
121                {$properties}
122            >
123                <img src='{$code_href}assets/icns/new_white_16.svg' class='noborder' />
124                Neuer Eintrag
125            </button>
126            ZZZZZZZZZZ;
127        if (!$is_logged_in) {
128            $out .= <<<'ZZZZZZZZZZ'
129                <div
130                    class='dropdown-menu dropdown-menu-end'
131                    aria-labelledby='create-news-button'
132                >
133                    <li><button
134                        class='dropdown-item'
135                        onclick='return olz.initOlzLoginModal({})'
136                    >
137                        Login
138                    </button></li>
139                    <li><hr class="dropdown-divider"></li>
140                    <li><div class='dropdown-item disabled should-login'>
141                        <b>Achtung</b>: Bild-Upload und nachträgliches Bearbeiten des Eintrags nur mit Login möglich!
142                    </div></li>
143                    <li><button
144                        id='create-anonymous-button'
145                        class='dropdown-item'
146                        onclick='return olz.initOlzEditNewsModal(&quot;anonymous&quot;)'
147                    >
148                        Forumseintrag ohne Login
149                    </button></li>
150                </div>
151                ZZZZZZZZZZ;
152            $out .= "</div>";
153        }
154
155        $newsletter_link = '';
156        $newsletter_app = OlzApps::getApp('Newsletter');
157        if ($newsletter_app) {
158            $newsletter_link = <<<ZZZZZZZZZZ
159                <a href='{$code_href}{$newsletter_app->getHref()}' class='newsletter-link'>
160                    <img
161                        src='{$newsletter_app->getIcon()}'
162                        alt='newsletter'
163                        class='newsletter-link-icon'
164                        title='Newsletter abonnieren!'
165                    />
166                </a>
167                ZZZZZZZZZZ;
168        } else {
169            $this->log()->error('Newsletter App does not exist!');
170        }
171        $out .= "<h1>{$news_list_title} {$newsletter_link}</h1>";
172
173        $filter_where = $news_utils->getSqlFromFilter($valid_filter);
174        $first_index = $page_index * $this::$page_size;
175        $sql = <<<ZZZZZZZZZZ
176            SELECT
177                COUNT(n.id) as count
178            FROM news n
179            WHERE
180                {$filter_where}
181                AND n.on_off='1'
182            ORDER BY published_date DESC, published_time DESC
183            ZZZZZZZZZZ;
184        // @phpstan-ignore-next-line
185        $count = intval($db->query($sql)->fetch_assoc()['count']);
186        $num_pages = intval($count / $this::$page_size) + 1;
187
188        $sql = <<<ZZZZZZZZZZ
189            SELECT
190                id,
191                owner_user_id,
192                owner_role_id,
193                published_date,
194                published_time,
195                format,
196                author_user_id,
197                author_role_id,
198                author_name,
199                author_email,
200                title,
201                teaser,
202                content,
203                image_ids
204            FROM news n
205            WHERE
206                {$filter_where}
207                AND n.on_off='1'
208            ORDER BY published_date DESC, published_time DESC
209            LIMIT {$first_index}{$this::$page_size}
210            ZZZZZZZZZZ;
211        $res = $db->query($sql);
212
213        $user_repo = $entityManager->getRepository(User::class);
214        $role_repo = $entityManager->getRepository(Role::class);
215
216        $page_content = '';
217        // @phpstan-ignore-next-line
218        for ($index = 0; $index < $res->num_rows; $index++) {
219            // @phpstan-ignore-next-line
220            $row = $res->fetch_assoc();
221
222            // TODO: Directly use doctrine to run the DB query.
223            // @phpstan-ignore-next-line
224            $owner_user = $row['owner_user_id'] ?
225                $user_repo->findOneBy(['id' => $row['owner_user_id']]) : null;
226            $owner_role = $row['owner_role_id'] ?
227                $role_repo->findOneBy(['id' => $row['owner_role_id']]) : null;
228            $author_user = $row['author_user_id'] ?
229                $user_repo->findOneBy(['id' => $row['author_user_id']]) : null;
230            $author_role = $row['author_role_id'] ?
231                $role_repo->findOneBy(['id' => $row['author_role_id']]) : null;
232
233            $news_entry = new NewsEntry();
234            $news_entry->setOwnerUser($owner_user);
235            $news_entry->setOwnerRole($owner_role);
236            // @phpstan-ignore-next-line
237            $news_entry->setPublishedDate(new \DateTime($row['published_date']));
238            // @phpstan-ignore-next-line
239            $news_entry->setFormat($row['format']);
240            $news_entry->setAuthorUser($author_user);
241            $news_entry->setAuthorRole($author_role);
242            // @phpstan-ignore-next-line
243            $news_entry->setAuthorName($row['author_name']);
244            // @phpstan-ignore-next-line
245            $news_entry->setAuthorEmail($row['author_email']);
246            // @phpstan-ignore-next-line
247            $news_entry->setTitle($row['title']);
248            // @phpstan-ignore-next-line
249            $news_entry->setTeaser($row['teaser']);
250            // @phpstan-ignore-next-line
251            $news_entry->setContent($row['content']);
252            $news_entry->setId(intval($row['id']));
253            // @phpstan-ignore-next-line
254            $news_entry->setImageIds($row['image_ids'] ? json_decode($row['image_ids'], true) : null);
255
256            $page_content .= OlzNewsListItem::render([
257                'json_mode' => $json_mode,
258                'news_entry' => $news_entry,
259            ]);
260        }
261        if ($page_content === '') {
262            $page_content = "<div class='no-entries'>Keine Einträge. Bitte Filter anpassen.</div>";
263        }
264        $out .= $page_content;
265        if ($num_pages > 1) {
266            $pages = '';
267            for ($page_number = 1; $page_number <= $num_pages; $page_number++) {
268                $is_current_page = $page_number === $page_index + 1;
269                $page_link_class = $is_current_page ? ' active' : '';
270                $pages .= <<<ZZZZZZZZZZ
271                    <li class='page-item'>
272                        <a
273                            class='page-link{$page_link_class}'
274                            href='?filter={$serialized_filter}&seite={$page_number}'
275                        >
276                            {$page_number}
277                        </a>
278                    </li>
279                    ZZZZZZZZZZ;
280            }
281            $out .= "<nav><ul class='no-style pagination justify-content-center'>{$pages}</ul></nav>";
282        }
283
284        $out .= "</div>";
285
286        $out .= OlzFooter::render();
287
288        return $out;
289    }
290}