Passed
Push — master ( 57205f...9aba4c )
by Darko
19:10 queued 06:58
created

AdminUserController   F

Complexity

Total Complexity 61

Size/Duplication

Total Lines 341
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 61
eloc 221
c 1
b 0
f 0
dl 0
loc 341
rs 3.52

5 Methods

Rating   Name   Duplication   Size   Complexity  
F index() 0 81 19
A verify() 0 10 2
F edit() 0 203 35
A destroy() 0 17 3
A resendVerification() 0 12 2

How to fix   Complexity   

Complex Class

Complex classes like AdminUserController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AdminUserController, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace App\Http\Controllers\Admin;
4
5
use App\Http\Controllers\BasePageController;
6
use App\Models\Invitation;
7
use App\Models\User;
8
use Illuminate\Http\RedirectResponse;
9
use Illuminate\Http\Request;
10
use Jrean\UserVerification\Facades\UserVerification;
11
use Spatie\Permission\Models\Role;
12
use Stevebauman\Location\Facades\Location;
13
14
class AdminUserController extends BasePageController
15
{
16
    /**
17
     * @throws \Throwable
18
     */
19
    public function index(Request $request)
20
    {
21
        $this->setAdminPrefs();
22
23
        $meta_title = $title = 'User List';
24
25
        $roles = [];
26
        $userRoles = Role::cursor()->remember();
27
        foreach ($userRoles as $userRole) {
28
            $roles[$userRole->id] = $userRole->name;
29
        }
30
31
        $ordering = getUserBrowseOrdering();
32
        $orderBy = $request->has('ob') && \in_array($request->input('ob'), $ordering, false) ? $request->input('ob') : '';
33
        $page = $request->has('page') && is_numeric($request->input('page')) ? $request->input('page') : 1;
34
        $offset = ($page - 1) * config('nntmux.items_per_page');
35
36
        $variables = [
37
            'username' => $request->has('username') ? $request->input('username') : '',
38
            'email' => $request->has('email') ? $request->input('email') : '',
39
            'host' => $request->has('host') ? $request->input('host') : '',
40
            'role' => $request->has('role') ? $request->input('role') : '',
41
            'created_from' => $request->has('created_from') ? $request->input('created_from') : '',
42
            'created_to' => $request->has('created_to') ? $request->input('created_to') : '',
43
        ];
44
45
        $result = User::getRange(
46
            $offset,
47
            config('nntmux.items_per_page'),
48
            $orderBy,
49
            $variables['username'],
50
            $variables['email'],
51
            $variables['host'],
52
            $variables['role'],
53
            true,
54
            $variables['created_from'],
55
            $variables['created_to']
56
        );
57
58
        $results = $this->paginate($result ?? [], User::getCount($variables['role'], $variables['username'], $variables['host'], $variables['email'], $variables['created_from'], $variables['created_to']) ?? 0, config('nntmux.items_per_page'), $page, $request->url(), $request->query());
59
60
        // Add country data to each user based on their host IP
61
        foreach ($results as $user) {
62
            $position = null;
63
            if (! empty($user->host) && filter_var($user->host, FILTER_VALIDATE_IP)) {
64
                $position = Location::get($user->host);
65
            }
66
            $user->country_name = $position ? $position->countryName : null;
67
            $user->country_code = $position ? $position->countryCode : null;
68
69
            // Add daily API and download counts
70
            try {
71
                $user->daily_api_count = \App\Models\UserRequest::getApiRequests($user->id);
72
                $user->daily_download_count = \App\Models\UserDownload::getDownloadRequests($user->id);
73
            } catch (\Exception $e) {
74
                $user->daily_api_count = 0;
75
                $user->daily_download_count = 0;
76
            }
77
        }
78
79
        // Build order by URLs
80
        $orderByUrls = [];
81
        foreach ($ordering as $orderType) {
82
            $orderByUrls['orderby'.$orderType] = url('admin/user-list?ob='.$orderType);
83
        }
84
85
        $this->viewData = array_merge($this->viewData, [
86
            'username' => $variables['username'],
87
            'email' => $variables['email'],
88
            'host' => $variables['host'],
89
            'role' => $variables['role'],
90
            'created_from' => $variables['created_from'],
91
            'created_to' => $variables['created_to'],
92
            'role_ids' => array_keys($roles),
93
            'role_names' => $roles,
94
            'userlist' => $results,
95
            'title' => $title,
96
            'meta_title' => $meta_title,
97
        ], $orderByUrls);
98
99
        return view('admin.users.index', $this->viewData);
100
    }
101
102
    /**
103
     * @return RedirectResponse|\Illuminate\View\View
104
     *
105
     * @throws \Exception
106
     */
107
    public function edit(Request $request)
108
    {
109
        $this->setAdminPrefs();
110
111
        $user = [
112
            'id' => '',
113
            'username' => '',
114
            'email' => '',
115
            'password' => '',
116
            'role' => User::ROLE_USER,
117
            'notes' => '',
118
            'rate_limit' => 60,
119
        ];
120
121
        $meta_title = $title = 'View User';
122
123
        // set the current action
124
        $action = $request->input('action') ?? 'view';
125
126
        // get the user roles
127
        $userRoles = Role::cursor()->remember();
128
        $roles = [];
129
        $defaultRole = 'User';
130
        $defaultInvites = Invitation::DEFAULT_INVITES;
131
        foreach ($userRoles as $r) {
132
            $roles[$r->id] = $r->name;
133
            if ($r->isdefault === 1) {
134
                $defaultRole = $r->id;
135
                $defaultInvites = $r->defaultinvites;
136
            }
137
        }
138
139
        $error = null;
140
141
        switch ($action) {
142
            case 'add':
143
                $user += [
144
                    'role' => $defaultRole,
145
                    'notes' => '',
146
                    'invites' => $defaultInvites,
147
                    'movieview' => 0,
148
                    'xxxview' => 0,
149
                    'musicview' => 0,
150
                    'consoleview' => 0,
151
                    'gameview' => 0,
152
                    'bookview' => 0,
153
                ];
154
                break;
155
            case 'submit':
156
                if (empty($request->input('id'))) {
157
                    $invites = $defaultInvites;
158
                    foreach ($userRoles as $role) {
159
                        if ($role['id'] === $request->input('role')) {
160
                            $invites = $role['defaultinvites'];
161
                        }
162
                    }
163
                    $ret = User::signUp($request->input('username'), $request->input('password'), $request->input('email'), '', $request->input('notes'), $invites, '', true, $request->input('role'), false);
164
                } else {
165
                    $editedUser = User::find($request->input('id'));
166
167
                    // Check if role is changing and get stack preference
168
                    $roleChanged = $editedUser->roles_id != $request->input('role');
169
                    $stackRole = $request->input('stack_role') ? true : false; // Check if checkbox is checked
170
                    $changedBy = auth()->check() ? auth()->id() : null;
171
172
                    // CRITICAL: Capture the ORIGINAL rolechangedate BEFORE any updates
173
                    // This is needed for accurate role history tracking
174
                    $originalRoleChangeDate = $editedUser->rolechangedate;
175
176
                    // Handle pending role cancellation
177
                    if ($request->has('cancel_pending_role') && $request->input('cancel_pending_role')) {
178
                        $editedUser->cancelPendingRole();
179
                    }
180
181
                    // Handle rolechangedate - Update the expiry for the CURRENT role FIRST
182
                    // This must happen BEFORE role change so the new expiry applies to the old role
183
                    if ($request->has('rolechangedate')) {
184
                        $roleChangeDate = $request->input('rolechangedate');
185
                        if (! empty($roleChangeDate)) {
186
                            User::updateUserRoleChangeDate($editedUser->id, $roleChangeDate);
187
                            $editedUser->refresh();
188
                        } else {
189
                            // Clear the rolechangedate if empty string is provided
190
                            $editedUser->update(['rolechangedate' => null]);
191
                            $editedUser->refresh();
192
                        }
193
                    }
194
195
                    // If role is changing, handle it with stacking logic
196
                    // Pass the original expiry so history records the correct old_expiry_date
197
                    if ($roleChanged && $request->input('role') !== null) {
198
                        User::updateUserRole(
199
                            $editedUser->id,
200
                            (int) $request->input('role'), // Cast to integer
201
                            true, // Apply promotions
202
                            $stackRole, // Stack role if requested
203
                            $changedBy,
204
                            $originalRoleChangeDate // Pass original expiry for history
205
                        );
206
                        $editedUser->refresh();
207
                    } elseif (!$roleChanged && $request->input('role') !== null) {
208
                        // Role isn't changing, but we should still apply promotions if there are any
209
                        // This handles the case where admin extends expiry date for existing role
210
                        User::updateUserRole(
211
                            $editedUser->id,
212
                            (int) $request->input('role'), // Same role
213
                            true, // Apply promotions
214
                            false, // Don't stack (not changing role)
215
                            $changedBy,
216
                            $originalRoleChangeDate
217
                        );
218
                        $editedUser->refresh();
219
                    }
220
221
                    // Update user basic information (but NOT the role - it's handled above)
222
                    // Use current role to avoid overwriting
223
                    $ret = User::updateUser(
224
                        $editedUser->id,
225
                        $request->input('username'),
226
                        $request->input('email'),
227
                        $editedUser->grabs,
228
                        $editedUser->roles_id, // Use current role, not the request role
229
                        $request->input('notes'),
230
                        $request->input('invites'),
231
                        ($request->has('movieview') ? 1 : 0),
232
                        ($request->has('musicview') ? 1 : 0),
233
                        ($request->has('gameview') ? 1 : 0),
234
                        ($request->has('xxxview') ? 1 : 0),
235
                        ($request->has('consoleview') ? 1 : 0),
236
                        ($request->has('bookview') ? 1 : 0)
237
                    );
238
239
                    if ($request->input('password') !== null) {
240
                        User::updatePassword($editedUser->id, $request->input('password'));
241
                    }
242
                }
243
244
                if ($ret >= 0) {
245
                    return redirect()->to('admin/user-list');
246
                }
247
248
                switch ($ret) {
249
                    case User::ERR_SIGNUP_BADUNAME:
250
                        $error = 'Bad username. Try a better one.';
251
                        break;
252
                    case User::ERR_SIGNUP_BADPASS:
253
                        $error = 'Bad password. Try a longer one.';
254
                        break;
255
                    case User::ERR_SIGNUP_BADEMAIL:
256
                        $error = 'Bad email.';
257
                        break;
258
                    case User::ERR_SIGNUP_UNAMEINUSE:
259
                        $error = 'Username in use.';
260
                        break;
261
                    case User::ERR_SIGNUP_EMAILINUSE:
262
                        $error = 'Email in use.';
263
                        break;
264
                    default:
265
                        $error = 'Unknown save error.';
266
                        break;
267
                }
268
                $user += [
269
                    'id' => $request->input('id'),
270
                    'username' => $request->input('username'),
271
                    'email' => $request->input('email'),
272
                    'role' => $request->input('role'),
273
                    'notes' => $request->input('notes'),
274
                ];
275
                break;
276
            case 'view':
277
            default:
278
                if ($request->has('id')) {
279
                    $title = 'User Edit';
280
                    $id = $request->input('id');
281
                    $user = User::find($id);
282
283
                    // Add daily API and download counts
284
                    if ($user) {
285
                        try {
286
                            $user->daily_api_count = \App\Models\UserRequest::getApiRequests($user->id);
287
                            $user->daily_download_count = \App\Models\UserDownload::getDownloadRequests($user->id);
288
                        } catch (\Exception $e) {
289
                            $user->daily_api_count = 0;
290
                            $user->daily_download_count = 0;
291
                        }
292
                    }
293
                }
294
295
                break;
296
        }
297
298
        $this->viewData = array_merge($this->viewData, [
299
            'yesno_ids' => [1, 0],
300
            'yesno_names' => ['Yes', 'No'],
301
            'role_ids' => array_keys($roles),
302
            'role_names' => $roles,
303
            'user' => $user,
304
            'error' => $error,
305
            'title' => $title,
306
            'meta_title' => $meta_title,
307
        ]);
308
309
        return view('admin.users.edit', $this->viewData);
310
    }
311
312
    public function destroy(Request $request): RedirectResponse
313
    {
314
        if ($request->has('id')) {
315
            $user = User::find($request->input('id'));
316
            $username = $user->username; // Store username before deletion
317
318
            $user->delete();
319
320
            // Redirect with username to display in notification
321
            return redirect()->to('admin/user-list?deleted=1&username='.urlencode($username));
322
        }
323
324
        if ($request->has('redir')) {
325
            return redirect()->to($request->input('redir'));
326
        }
327
328
        return redirect()->to($request->server('HTTP_REFERER'));
0 ignored issues
show
Bug introduced by
It seems like $request->server('HTTP_REFERER') can also be of type array; however, parameter $path of Illuminate\Routing\Redirector::to() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

328
        return redirect()->to(/** @scrutinizer ignore-type */ $request->server('HTTP_REFERER'));
Loading history...
329
    }
330
331
    public function resendVerification(Request $request): RedirectResponse
332
    {
333
        if ($request->has('id')) {
334
            $user = User::find($request->input('id'));
335
            UserVerification::generate($user);
336
337
            UserVerification::send($user, 'User email verification required');
338
339
            return redirect()->back()->with('success', 'Email verification for '.$user->username.' sent');
340
        }
341
342
        return redirect()->back()->with('error', 'User is invalid');
343
    }
344
345
    public function verify(Request $request): RedirectResponse
346
    {
347
        if ($request->has('id')) {
348
            $user = User::find($request->input('id'));
349
            User::query()->where('id', $request->input('id'))->update(['verified' => 1, 'email_verified_at' => now()]);
350
351
            return redirect()->back()->with('success', 'Email verification for '.$user->username.' sent');
352
        }
353
354
        return redirect()->back()->with('error', 'User is invalid');
355
    }
356
}
357