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