Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
77 / 77
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
NewsController
100.00% covered (success)
100.00%
77 / 77
100.00% covered (success)
100.00%
5 / 5
21
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 edit
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 showEdit
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
4
 save
100.00% covered (success)
100.00%
49 / 49
100.00% covered (success)
100.00%
1 / 1
13
 delete
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace Engelsystem\Controllers\Admin;
6
7use Engelsystem\Controllers\BaseController;
8use Engelsystem\Controllers\HasUserNotifications;
9use Engelsystem\Controllers\NotificationType;
10use Engelsystem\Helpers\Authenticator;
11use Engelsystem\Http\Redirector;
12use Engelsystem\Http\Request;
13use Engelsystem\Http\Response;
14use Engelsystem\Models\News;
15use Engelsystem\Models\User\Settings;
16use Psr\Log\LoggerInterface;
17
18class NewsController extends BaseController
19{
20    use HasUserNotifications;
21
22    /** @var array<string> */
23    protected array $permissions = [
24        'admin_news',
25    ];
26
27    public function __construct(
28        protected Authenticator $auth,
29        protected LoggerInterface $log,
30        protected News $news,
31        protected Redirector $redirect,
32        protected Response $response
33    ) {
34    }
35
36    public function edit(Request $request): Response
37    {
38        $newsId = $request->getAttribute('news_id'); // optional
39
40        $news = $this->news->find($newsId);
41        $isMeeting = (bool) $request->get('meeting', false);
42
43        return $this->showEdit($news, !$news, $isMeeting);
44    }
45
46    protected function showEdit(?News $news, bool $sendNotification = true, bool $isMeetingDefault = false): Response
47    {
48        $notificationsCount = Settings::whereEmailNews(true)->count();
49        return $this->response->withView(
50            'pages/news/edit.twig',
51            [
52                'news'           => $news,
53                'is_meeting'     => $news ? $news->is_meeting : $isMeetingDefault,
54                'is_pinned'      => $news ? $news->is_pinned : false,
55                'is_highlighted' => $news ? $news->is_highlighted : false,
56                'send_notification' => $sendNotification,
57                'notifications_count' => $notificationsCount,
58            ],
59        );
60    }
61
62    public function save(Request $request): Response
63    {
64        $newsId = $request->getAttribute('news_id'); // optional
65
66        /** @var News $news */
67        $news = $this->news->findOrNew($newsId);
68
69        if ($request->request->has('delete')) {
70            return $this->delete($news);
71        }
72
73        $data = $this->validate($request, [
74            'title'          => 'required|max:150',
75            'text'           => 'required',
76            'is_meeting'     => 'optional|checked',
77            'is_pinned'      => 'optional|checked',
78            'is_highlighted' => 'optional|checked',
79            'delete'         => 'optional|checked',
80            'preview'        => 'optional|checked',
81            'send_notification' => 'optional|checked',
82        ]);
83
84        if (!$news->user) {
85            $news->user()->associate($this->auth->user());
86        }
87        $contentChanged = $news->title != $data['title'] || $news->text != $data['text'];
88        $news->title = $data['title'];
89        $news->text = $data['text'];
90        $news->is_meeting = !is_null($data['is_meeting']);
91        $news->is_pinned = !is_null($data['is_pinned']);
92        $notify = !is_null($data['send_notification']);
93
94        if ($this->auth->can('news.highlight')) {
95            $news->is_highlighted = !is_null($data['is_highlighted']);
96        }
97
98        if (!is_null($data['preview'])) {
99            return $this->showEdit($news, $notify);
100        }
101
102        $isNewNews = !$news->id;
103        if ($isNewNews && News::where('title', $news->title)->where('text', $news->text)->count()) {
104            $this->addNotification('news.edit.duplicate', NotificationType::ERROR);
105            return $this->showEdit($news, $notify);
106        }
107        if (!$isNewNews & !$contentChanged) {
108            $news->timestamps = false;
109        }
110        $news->save();
111
112        if ($isNewNews) {
113            event('news.created', ['news' => $news, 'sendNotification' => $notify]);
114        } else {
115            event('news.updated', ['news' => $news, 'sendNotification' => $notify]);
116        }
117
118        $this->log->info(
119            'Saved {pinned}{highlighted}{type} "{news}" ({id}): {text}',
120            [
121                'id'        => $news->id,
122                'pinned'    => $news->is_pinned ? 'pinned ' : '',
123                'highlighted' => $news->is_highlighted ? 'highlighted ' : '',
124                'type'      => $news->is_meeting ? 'meeting' : 'news',
125                'news'      => $news->title,
126                'text'      => $news->text,
127            ]
128        );
129
130        $this->addNotification('news.edit.success');
131
132        return $this->redirect->to('/news');
133    }
134
135    protected function delete(News $news): Response
136    {
137        $news->delete();
138
139        $this->log->info(
140            'Deleted {type} "{news}" ({id})',
141            [
142                'id' => $news->id,
143                'type' => $news->is_meeting ? 'meeting' : 'news',
144                'news' => $news->title,
145            ]
146        );
147
148        $this->addNotification('news.delete.success');
149
150        return $this->redirect->to('/news');
151    }
152}