Passed
Push — master ( 6f4b10...8c675f )
by Darko
13:02
created

AdminUserController::resendVerification()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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

317
        return redirect()->to(/** @scrutinizer ignore-type */ $request->server('HTTP_REFERER'));
Loading history...
318
    }
319
320
    public function resendVerification(Request $request): RedirectResponse
321
    {
322
        if ($request->has('id')) {
323
            $user = User::find($request->input('id'));
324
            UserVerification::generate($user);
325
326
            UserVerification::send($user, 'User email verification required');
327
328
            return redirect()->back()->with('success', 'Email verification for '.$user->username.' sent');
329
        }
330
331
        return redirect()->back()->with('error', 'User is invalid');
332
    }
333
334
    public function verify(Request $request): RedirectResponse
335
    {
336
        if ($request->has('id')) {
337
            $user = User::find($request->input('id'));
338
            User::query()->where('id', $request->input('id'))->update(['verified' => 1, 'email_verified_at' => now()]);
339
340
            return redirect()->back()->with('success', 'Email verification for '.$user->username.' sent');
341
        }
342
343
        return redirect()->back()->with('error', 'User is invalid');
344
    }
345
}
346