Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
74 / 74 |
|
100.00% |
8 / 8 |
CRAP | |
100.00% |
1 / 1 |
NewsController | |
100.00% |
74 / 74 |
|
100.00% |
8 / 8 |
11 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
index | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
meetings | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
show | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
comment | |
100.00% |
19 / 19 |
|
100.00% |
1 / 1 |
1 | |||
deleteComment | |
100.00% |
18 / 18 |
|
100.00% |
1 / 1 |
3 | |||
showOverview | |
100.00% |
28 / 28 |
|
100.00% |
1 / 1 |
2 | |||
renderView | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace Engelsystem\Controllers; |
6 | |
7 | use Engelsystem\Config\Config; |
8 | use Engelsystem\Helpers\Authenticator; |
9 | use Engelsystem\Http\Exceptions\HttpForbidden; |
10 | use Engelsystem\Http\Redirector; |
11 | use Engelsystem\Http\Request; |
12 | use Engelsystem\Http\Response; |
13 | use Engelsystem\Models\News; |
14 | use Engelsystem\Models\NewsComment; |
15 | use Psr\Log\LoggerInterface; |
16 | |
17 | class NewsController extends BaseController |
18 | { |
19 | use HasUserNotifications; |
20 | |
21 | /** @var array<string, string> */ |
22 | protected array $permissions = [ |
23 | 'news', |
24 | 'meetings' => 'user_meetings', |
25 | 'comment' => 'news_comments', |
26 | 'deleteComment' => 'news_comments', |
27 | ]; |
28 | |
29 | public function __construct( |
30 | protected Authenticator $auth, |
31 | protected NewsComment $comment, |
32 | protected Config $config, |
33 | protected LoggerInterface $log, |
34 | protected News $news, |
35 | protected Redirector $redirect, |
36 | protected Response $response, |
37 | protected Request $request |
38 | ) { |
39 | } |
40 | |
41 | public function index(): Response |
42 | { |
43 | return $this->showOverview(); |
44 | } |
45 | |
46 | public function meetings(): Response |
47 | { |
48 | return $this->showOverview(true); |
49 | } |
50 | |
51 | public function show(Request $request): Response |
52 | { |
53 | $newsId = (int) $request->getAttribute('news_id'); |
54 | |
55 | $news = $this->news |
56 | ->with(['user', 'comments.user.state', 'comments.user.personalData']) |
57 | ->findOrFail($newsId); |
58 | |
59 | return $this->renderView('pages/news/news.twig', ['news' => $news]); |
60 | } |
61 | |
62 | public function comment(Request $request): Response |
63 | { |
64 | $newsId = (int) $request->getAttribute('news_id'); |
65 | |
66 | $data = $this->validate($request, [ |
67 | 'comment' => 'required', |
68 | ]); |
69 | $user = $this->auth->user(); |
70 | $news = $this->news->findOrFail($newsId); |
71 | |
72 | /** @var NewsComment $comment */ |
73 | $comment = $news->comments()->create([ |
74 | 'text' => $data['comment'], |
75 | 'user_id' => $user->id, |
76 | ]); |
77 | |
78 | $this->log->info( |
79 | 'Created news comment for "{news}": {comment}', |
80 | [ |
81 | 'news' => $news->title, |
82 | 'comment' => $comment->text, |
83 | ] |
84 | ); |
85 | |
86 | $this->addNotification('news.comment.success'); |
87 | |
88 | return $this->redirect->back(); |
89 | } |
90 | |
91 | public function deleteComment(Request $request): Response |
92 | { |
93 | $commentId = (int) $request->getAttribute('comment_id'); |
94 | |
95 | $this->validate( |
96 | $request, |
97 | [ |
98 | 'delete' => 'checked', |
99 | ] |
100 | ); |
101 | |
102 | $comment = $this->comment->findOrFail($commentId); |
103 | if ( |
104 | $comment->user->id != $this->auth->user()->id |
105 | && !$this->auth->canAny(['admin_news', 'comment.delete']) |
106 | ) { |
107 | throw new HttpForbidden(); |
108 | } |
109 | |
110 | $comment->delete(); |
111 | |
112 | $this->log->info( |
113 | 'Deleted comment "{comment}" of news "{news}"', |
114 | ['comment' => $comment->text, 'news' => $comment->news->title] |
115 | ); |
116 | $this->addNotification('news.comment-delete.success'); |
117 | |
118 | return $this->redirect->to('/news/' . $comment->news->id); |
119 | } |
120 | |
121 | protected function showOverview(bool $onlyMeetings = false): Response |
122 | { |
123 | $query = $this->news; |
124 | $page = $this->request->get('page', 1); |
125 | $perPage = $this->config->get('display_news'); |
126 | |
127 | if ($onlyMeetings) { |
128 | $query = $query->where('is_meeting', true); |
129 | } |
130 | |
131 | $count = $query->count(); |
132 | $pagesCount = max(1, ceil($count / $perPage)); |
133 | $page = max(1, min($page, $pagesCount)); |
134 | |
135 | $news = $query |
136 | ->with('user') |
137 | ->withCount('comments') |
138 | ->orderByDesc('is_pinned') |
139 | ->orderByDesc('is_highlighted') |
140 | ->orderByDesc('updated_at') |
141 | ->orderByDesc('id') |
142 | ->limit($perPage) |
143 | ->offset(($page - 1) * $perPage) |
144 | ->get(); |
145 | |
146 | return $this->renderView( |
147 | 'pages/news/index.twig', |
148 | [ |
149 | 'news' => $news, |
150 | 'pages' => $pagesCount, |
151 | 'page' => $page, |
152 | 'only_meetings' => $onlyMeetings, |
153 | 'is_overview' => true, |
154 | ] |
155 | ); |
156 | } |
157 | |
158 | protected function renderView(string $page, array $data): Response |
159 | { |
160 | return $this->response->withView($page, $data); |
161 | } |
162 | } |