Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
101 / 101
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
AngelTypesController
100.00% covered (success)
100.00%
101 / 101
100.00% covered (success)
100.00%
6 / 6
26
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
 hasPermission
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
5
 edit
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 showEdit
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
3
 save
100.00% covered (success)
100.00%
68 / 68
100.00% covered (success)
100.00%
1 / 1
14
 delete
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace Engelsystem\Controllers\Admin;
6
7use Engelsystem\Config\Config;
8use Engelsystem\Controllers\BaseController;
9use Engelsystem\Controllers\HasUserNotifications;
10use Engelsystem\Helpers\Authenticator;
11use Engelsystem\Http\Exceptions\HttpForbidden;
12use Engelsystem\Http\Exceptions\ValidationException;
13use Engelsystem\Http\Redirector;
14use Engelsystem\Http\Request;
15use Engelsystem\Http\Response;
16use Engelsystem\Http\Validation\Validator;
17use Engelsystem\Models\AngelType;
18use Psr\Http\Message\ServerRequestInterface;
19use Psr\Log\LoggerInterface;
20
21class AngelTypesController extends BaseController
22{
23    use HasUserNotifications;
24
25    /** @var array<string> */
26    protected array $permissions = [
27        'delete' => 'angeltypes.edit',
28    ];
29    public function __construct(
30        protected Response $response,
31        protected Config $config,
32        protected Authenticator $auth,
33        protected AngelType $angelType,
34        protected LoggerInterface $log,
35        protected Redirector $redirect,
36    ) {
37    }
38
39    public function hasPermission(ServerRequestInterface $request, string $method): ?bool
40    {
41        $angelTypeId = (int) $request->getAttribute('angel_type_id');
42        /** @var AngelType $angelType */
43        $angelType = AngelType::find($angelTypeId);
44        $canEdit = ($angelType && $this->auth->user()?->isAngelTypeSupporter($angelType))
45            || $this->auth->can('angeltypes.edit');
46        return match ($method) {
47            'edit', 'save' => $canEdit,
48            default => parent::hasPermission($request, $method),
49        };
50    }
51
52    public function edit(Request $request): Response
53    {
54        $angelTypeId = (int) $request->getAttribute('angel_type_id');
55        $angelType = $this->angelType->find($angelTypeId);
56        return $this->showEdit($angelType);
57    }
58
59    protected function showEdit(?AngelType $angelType): Response
60    {
61        $isSupporter = !$this->auth->can('angeltypes.edit') && $angelType &&
62            $this->auth->user()->isAngelTypeSupporter($angelType);
63
64        return $this->response->withView(
65            'admin/angeltypes/edit',
66            [
67                'angelType' => $angelType,
68                'isSupporter' => $isSupporter,
69            ]
70        );
71    }
72
73    public function save(Request $request): Response
74    {
75        $angelTypeId = (int) $request->getAttribute('angel_type_id');
76
77        /** @var AngelType $angelType */
78        $angelType = $this->angelType->find($angelTypeId);
79
80        if (!$angelType) {
81            if ($this->auth->can('angeltypes.edit')) {
82                $angelType = new AngelType();
83            } else {
84                throw new HttpForbidden();
85            }
86        }
87
88        $data = $this->validate(
89            $request,
90            [
91                'name' => ($this->auth->can('angeltypes.edit') ? 'required' : 'optional') . '|max:255',
92                'description' => 'optional',
93                'contact_name' => 'optional|max:255',
94                'contact_dect' => 'optional|max:255',
95                'contact_email' => 'optional|max:255',
96                'restricted' => 'optional|checked',
97                'shift_self_signup' => 'optional|checked',
98                'show_on_dashboard' => 'optional|checked',
99                'hide_register' => 'optional|checked',
100                'hide_on_shift_view' => 'optional|checked',
101                'requires_driver_license' => 'optional|checked',
102                'requires_ifsg_certificate' => 'optional|checked',
103            ]
104        );
105
106        if ($this->auth->can('angeltypes.edit')) {
107            if (AngelType::whereName($data['name'])->where('id', '!=', $angelType->id)->exists()) {
108                throw new ValidationException((new Validator())->addErrors(['name' => ['validation.name.exists']]));
109            }
110            $angelType->name = $data['name'];
111            $angelType->restricted = (bool) $data['restricted'];
112            $angelType->shift_self_signup = (bool) $data['shift_self_signup'];
113            $angelType->show_on_dashboard = (bool) $data['show_on_dashboard'];
114            $angelType->hide_register = (bool) $data['hide_register'];
115            $angelType->hide_on_shift_view = (bool) $data['hide_on_shift_view'];
116            $angelType->requires_driver_license = (bool) $data['requires_driver_license'];
117            $angelType->requires_ifsg_certificate = (bool) $data['requires_ifsg_certificate'];
118        }
119
120        $angelType->description = (string) $data['description'];
121        $angelType->contact_name = (string) $data['contact_name'];
122        $angelType->contact_dect = (string) $data['contact_dect'];
123        $angelType->contact_email = (string) $data['contact_email'];
124
125        $angelType->save();
126
127        $this->log->info(
128            '{new} angel type "{name}" ({id}): {restricted} {shift_self_signup} {description}, '
129            . '{contact_name} {contact_dect}{contact_email}, {show_on_dashboard}, {hide_register}, '
130            . '{hide_on_shift_view}{requires_driver_license}{requires_ifsg}',
131            [
132                'new' => $angelType->wasRecentlyCreated ? 'Created' : 'Updated',
133                'id' => $angelType->id,
134                'name' => $angelType->name,
135                'restricted' => $angelType->restricted ? 'restricted ,' : '',
136                'shift_self_signup' => $angelType->shift_self_signup ? 'shift_self_signup ,' : '',
137                'description' => $angelType->description,
138                'contact_name' => $angelType->contact_name,
139                'contact_dect' => $this->config->get('enable_dect') ? $angelType->contact_dect . ', ' : '',
140                'contact_email' => $angelType->contact_email,
141                'show_on_dashboard' => $angelType->show_on_dashboard,
142                'hide_register' => $angelType->hide_register,
143                'hide_on_shift_view' => $angelType->hide_on_shift_view,
144                'requires_driver_license' =>
145                    $this->config->get('driving_license_enabled') && $angelType->requires_driver_license
146                        ? ', requires driver license'
147                        : '',
148                'requires_ifsg' =>
149                    $this->config->get('ifsg_enabled') && $angelType->requires_ifsg_certificate
150                        ? ', requires ifsg certificate'
151                        : '',
152            ]
153        );
154
155        $this->addNotification('angeltype.edit.success');
156
157        return $this->redirect->to('/angeltypes');
158    }
159
160    public function delete(Request $request): Response
161    {
162        $data = $this->validate($request, [
163            'id'     => 'required|int',
164            'delete' => 'checked',
165        ]);
166
167        $angelType = $this->angelType->findOrFail($data['id']);
168
169        $shiftEntries = $angelType->shiftEntries;
170        foreach ($shiftEntries as $entry) {
171            event('shift.entry.deleting', ['entry' => $entry]);
172        }
173        $angelType->delete();
174
175        $this->log->info('Deleted angel type {angelType}', ['angelType' => $angelType->name]);
176        $this->addNotification('angeltype.delete.success');
177
178        return $this->redirect->to('/angeltypes');
179    }
180}