ProfileController::edit()   F
last analyzed

Complexity

Conditions 51
Paths > 20000

Size

Total Lines 209
Code Lines 129

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 129
c 0
b 0
f 0
dl 0
loc 209
rs 0
cc 51
nc 14155796
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace App\Http\Controllers;
4
5
use App\Jobs\SendAccountDeletedEmail;
6
use App\Models\ReleaseComment;
7
use App\Models\User;
8
use App\Models\UserDownload;
9
use App\Models\UserRequest;
10
use Illuminate\Contracts\View\Factory;
11
use Illuminate\Contracts\View\View;
12
use Illuminate\Foundation\Application;
13
use Illuminate\Http\RedirectResponse;
14
use Illuminate\Http\Request;
15
use Illuminate\Support\Arr;
16
use Illuminate\Support\Facades\Auth;
17
use Illuminate\Support\Facades\Validator;
18
use Jrean\UserVerification\Facades\UserVerification;
19
20
class ProfileController extends BasePageController
21
{
22
    /**
23
     * @throws \Throwable
24
     */
25
    public function show(Request $request)
26
    {
27
28
        $userID = $this->userdata->id;
29
        $privileged = $this->userdata->hasRole('Admin') || $this->userdata->hasRole('Moderator');
30
        $privateProfiles = config('nntmux_settings.private_profiles');
31
        $publicView = false;
32
33
        if ($privileged || ! $privateProfiles) {
34
            $altID = ($request->has('id') && (int) $request->input('id') >= 0) ? (int) $request->input('id') : false;
35
            $altUsername = ($request->has('name') && $request->input('name') !== '') ? $request->input('name') : false;
36
37
            // If both 'id' and 'name' are specified, 'id' should take precedence.
38
            if ($altID === false && $altUsername !== false) {
39
                $user = User::getByUsername($altUsername);
40
                if ($user) {
41
                    $this->userdata = $user;
42
                    $altID = $user['id'];
43
                    $userID = $altID;
44
                }
45
            } elseif ($altID !== false) {
46
                $user = User::find($altID);
47
                if ($user) {
48
                    $this->userdata = $user;
49
                    $userID = $altID;
50
                    $publicView = true;
51
                }
52
            }
53
        }
54
55
        if ($this->userdata === null) {
56
            return $this->show404('No such user!');
57
        }
58
59
        // Check if the user selected a theme.
60
        if (! isset($this->userdata->style) || $this->userdata->style === 'None') {
61
            $this->userdata->style = 'Using the admin selected theme.';
62
        }
63
64
        // Get 24-hour hourly data for downloads and API requests
65
        $downloadsHourly = UserDownload::getHourlyDownloads($userID);
66
        $apiRequestsHourly = UserRequest::getHourlyApiRequests($userID);
67
68
        // Get role limits
69
        $userRole = $this->userdata->roles->first();
70
        $downloadLimit = $userRole->downloadrequests ?? 0;
71
        $apiLimit = $userRole->apirequests ?? 0;
72
73
        $this->viewData = array_merge($this->viewData, [
74
            'downloadlist' => UserDownload::getDownloadRequestsForUser($userID),
75
            'apirequests' => UserRequest::getApiRequests($userID),
76
            'grabstoday' => UserDownload::getDownloadRequests($userID),
77
            'userinvitedby' => ($this->userdata->invitedby && $this->userdata->invitedby !== '') ? User::find($this->userdata->invitedby) : null,
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->userdata->invitedby of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
78
            'user' => $this->userdata,
79
            'privateprofiles' => $privateProfiles,
80
            'publicview' => $publicView,
81
            'privileged' => $privileged,
82
            'isadmin' => $this->userdata->hasRole('Admin'),
83
            'commentslist' => ReleaseComment::getCommentsForUserRange($userID),
84
            'downloadsHourly' => $downloadsHourly,
85
            'apiRequestsHourly' => $apiRequestsHourly,
86
            'downloadLimit' => $downloadLimit,
87
            'apiLimit' => $apiLimit,
88
            'meta_title' => 'View User Profile',
89
            'meta_keywords' => 'view,profile,user,details',
90
            'meta_description' => 'View User Profile for '.$this->userdata->username,
91
        ]);
92
93
        return view('profile.index', $this->viewData);
94
    }
95
96
    /**
97
     * @return Factory|\Illuminate\View\View|View
98
     *
99
     * @throws \Exception
100
     */
101
    public function edit(Request $request)
102
    {
103
104
        $action = $request->input('action') ?? 'view';
105
106
        $userid = $this->userdata->id;
107
        if (! $this->userdata) {
108
            $this->show404('No such user!');
109
        }
110
111
        $errorStr = '';
112
        $success_2fa = $request->session()->get('success');
113
        $error_2fa = $request->session()->get('error');
114
115
        // Generate 2FA QR code URL if 2FA is set up but not enabled
116
        $google2fa_url = '';
117
        if ($this->userdata->passwordSecurity()->exists() && ! $this->userdata->passwordSecurity->google2fa_enable) {
0 ignored issues
show
Bug introduced by
The property google2fa_enable does not seem to exist on App\Models\PasswordSecurity. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
118
            $google2fa_url = \Google2FA::getQRCodeInline(
0 ignored issues
show
Bug introduced by
The type Google2FA was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
119
                config('app.name'),
120
                $this->userdata->email,
121
                $this->userdata->passwordSecurity->google2fa_secret
0 ignored issues
show
Bug introduced by
The property google2fa_secret does not seem to exist on App\Models\PasswordSecurity. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
122
            );
123
        }
124
125
        switch ($action) {
126
            case 'newapikey':
127
                User::updateRssKey($userid);
128
129
                return redirect()->to('profile');
0 ignored issues
show
Bug Best Practice introduced by
The expression return redirect()->to('profile') returns the type Illuminate\Http\RedirectResponse which is incompatible with the documented return type Illuminate\Contracts\Vie...ew|Illuminate\View\View.
Loading history...
130
            case 'clearcookies':
131
                return redirect()->to('profileedit');
0 ignored issues
show
Bug Best Practice introduced by
The expression return redirect()->to('profileedit') returns the type Illuminate\Http\RedirectResponse which is incompatible with the documented return type Illuminate\Contracts\Vie...ew|Illuminate\View\View.
Loading history...
132
            case 'submit':
133
                $validator = Validator::make($request->all(), [
134
                    'email' => ['nullable', 'string', 'email', 'max:255', 'unique:users,email,'.$userid, 'indisposable'],
135
                    'password' => ['nullable', 'string', 'min:8', 'confirmed', 'regex:/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$/'],
136
                ]);
137
138
                if ($validator->fails()) {
139
                    $errorStr = implode('', Arr::collapse($validator->errors()->toArray()));
140
                } else {
141
                    User::updateUser(
142
                        $userid,
143
                        $this->userdata->username,
144
                        $request->input('email'),
145
                        $this->userdata->grabs,
146
                        $this->userdata->roles_id,
147
                        $this->userdata->notes,
148
                        $this->userdata->invites,
149
                        $request->has('viewmovies') ? 1 : 0,
150
                        $request->has('viewaudio') ? 1 : 0,
151
                        $request->has('viewpc') ? 1 : 0,
152
                        $request->has('viewadult') ? 1 : 0,
153
                        $request->has('viewconsole') ? 1 : 0,
154
                        $request->has('viewbooks') ? 1 : 0,
155
                        'None',
156
                    );
157
158
                    // Update theme preference
159
                    if ($request->has('theme_preference')) {
160
                        $themeValue = $request->input('theme_preference');
161
                        if (in_array($themeValue, ['light', 'dark', 'system'])) {
162
                            User::where('id', $userid)->update(['theme_preference' => $themeValue]);
163
                        }
164
                    }
165
166
                    // Update timezone preference
167
                    if ($request->has('timezone')) {
168
                        $timezoneValue = $request->input('timezone');
169
                        $validTimezones = array_merge(['UTC'], ...array_values(getAvailableTimezones()));
170
                        if (in_array($timezoneValue, $validTimezones)) {
171
                            User::where('id', $userid)->update(['timezone' => $timezoneValue]);
172
                        }
173
                    }
174
175
                    // Handle Console permission
176
                    if ($request->has('viewconsole')) {
177
                        if (! $this->userdata->hasDirectPermission('view console')) {
178
                            $this->userdata->givePermissionTo('view console');
179
                        }
180
                    } else {
181
                        if ($this->userdata->hasPermissionTo('view console')) {
182
                            $this->userdata->revokePermissionTo('view console');
183
                        }
184
                    }
185
186
                    // Handle Movies permission
187
                    if ($request->has('viewmovies')) {
188
                        if (! $this->userdata->hasDirectPermission('view movies')) {
189
                            $this->userdata->givePermissionTo('view movies');
190
                        }
191
                    } else {
192
                        if ($this->userdata->hasPermissionTo('view movies')) {
193
                            $this->userdata->revokePermissionTo('view movies');
194
                        }
195
                    }
196
197
                    // Handle Audio permission
198
                    if ($request->has('viewaudio')) {
199
                        if (! $this->userdata->hasDirectPermission('view audio')) {
200
                            $this->userdata->givePermissionTo('view audio');
201
                        }
202
                    } else {
203
                        if ($this->userdata->hasPermissionTo('view audio')) {
204
                            $this->userdata->revokePermissionTo('view audio');
205
                        }
206
                    }
207
208
                    // Handle PC/Games permission
209
                    if ($request->has('viewpc')) {
210
                        if (! $this->userdata->hasDirectPermission('view pc')) {
211
                            $this->userdata->givePermissionTo('view pc');
212
                        }
213
                    } else {
214
                        if ($this->userdata->hasPermissionTo('view pc')) {
215
                            $this->userdata->revokePermissionTo('view pc');
216
                        }
217
                    }
218
219
                    // Handle TV permission
220
                    if ($request->has('viewtv')) {
221
                        if (! $this->userdata->hasDirectPermission('view tv')) {
222
                            $this->userdata->givePermissionTo('view tv');
223
                        }
224
                    } else {
225
                        if ($this->userdata->hasPermissionTo('view tv')) {
226
                            $this->userdata->revokePermissionTo('view tv');
227
                        }
228
                    }
229
230
                    // Handle Adult permission
231
                    if ($request->has('viewadult')) {
232
                        if (! $this->userdata->hasDirectPermission('view adult')) {
233
                            $this->userdata->givePermissionTo('view adult');
234
                        }
235
                    } else {
236
                        if ($this->userdata->hasPermissionTo('view adult')) {
237
                            $this->userdata->revokePermissionTo('view adult');
238
                        }
239
                    }
240
241
                    // Handle Books permission
242
                    if ($request->has('viewbooks')) {
243
                        if (! $this->userdata->hasDirectPermission('view books')) {
244
                            $this->userdata->givePermissionTo('view books');
245
                        }
246
                    } else {
247
                        if ($this->userdata->hasPermissionTo('view books')) {
248
                            $this->userdata->revokePermissionTo('view books');
249
                        }
250
                    }
251
252
                    // Handle Other permission
253
                    if ($request->has('viewother')) {
254
                        if (! $this->userdata->hasDirectPermission('view other')) {
255
                            $this->userdata->givePermissionTo('view other');
256
                        }
257
                    } else {
258
                        if ($this->userdata->hasPermissionTo('view other')) {
259
                            $this->userdata->revokePermissionTo('view other');
260
                        }
261
                    }
262
263
                    if ($request->has('password') && ! empty($request->input('password'))) {
264
                        User::updatePassword($userid, $request->input('password'));
265
                    }
266
267
                    if (! $this->userdata->hasRole('Admin')) {
268
                        if (! empty($request->input('email')) && $this->userdata->email !== $request->input('email')) {
269
                            $this->userdata->email = $request->input('email');
270
271
                            $verify_user = $this->userdata;
272
273
                            UserVerification::generate($verify_user);
274
275
                            UserVerification::send($verify_user, 'User email verification required');
276
277
                            Auth::logout();
278
279
                            return redirect()->to('login')->with('info', 'You will be able to login after you verify your new email address');
0 ignored issues
show
Bug Best Practice introduced by
The expression return redirect()->to('l...our new email address') returns the type Illuminate\Http\RedirectResponse which is incompatible with the documented return type Illuminate\Contracts\Vie...ew|Illuminate\View\View.
Loading history...
280
                        }
281
                    }
282
283
                    return redirect()->to('profile')->with('success', 'Profile changes saved');
0 ignored issues
show
Bug Best Practice introduced by
The expression return redirect()->to('p...Profile changes saved') returns the type Illuminate\Http\RedirectResponse which is incompatible with the documented return type Illuminate\Contracts\Vie...ew|Illuminate\View\View.
Loading history...
284
                }
285
                break;
286
287
            case 'view':
288
            default:
289
                break;
290
        }
291
292
        $this->viewData = array_merge($this->viewData, [
293
            'error' => $errorStr,
294
            'user' => $this->userdata,
295
            'userexccat' => User::getCategoryExclusionById($userid),
296
            'success_2fa' => $success_2fa,
297
            'error_2fa' => $error_2fa,
298
            'google2fa_url' => $google2fa_url,
299
            'yesno_ids' => [1, 0],
300
            'yesno_names' => ['Yes', 'No'],
301
            'publicview' => false,
302
            'privileged' => $this->userdata->hasRole('Admin') || $this->userdata->hasRole('Moderator'),
303
            'userinvitedby' => ($this->userdata->invitedby && $this->userdata->invitedby !== '') ? User::find($this->userdata->invitedby) : null,
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->userdata->invitedby of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
304
            'meta_title' => 'Edit User Profile',
305
            'meta_keywords' => 'edit,profile,user,details',
306
            'meta_description' => 'Edit User Profile for '.$this->userdata->username,
307
        ]);
308
309
        return view('profile.edit', $this->viewData);
310
    }
311
312
    /**
313
     * @throws \Exception
314
     */
315
    public function destroy(Request $request): Application|View|Factory|RedirectResponse|\Illuminate\Contracts\Foundation\Application
316
    {
317
        $userId = $request->input('id');
318
319
        if ($userId !== null && (int) $userId === $this->userdata->id && ! $this->userdata->hasRole('Admin')) {
320
            $user = User::find($userId);
321
            SendAccountDeletedEmail::dispatch($user);
322
            Auth::logout();
323
            $user->delete();
324
        }
325
326
        if ($this->userdata->hasRole('Admin')) {
327
            return redirect()->to('profile');
328
        }
329
330
        return view('errors.503')->with('warning', 'Dont try to delete another user account!');
331
    }
332
333
    /**
334
     * Update user's dark mode preference
335
     */
336
    public function updateTheme(Request $request)
337
    {
338
        $user = Auth::user();
339
340
        if (! $user) {
341
            return response()->json(['success' => false, 'message' => 'User not authenticated'], 401);
342
        }
343
344
        $validator = Validator::make($request->all(), [
345
            'theme_preference' => ['required', 'in:light,dark,system'],
346
        ]);
347
348
        if ($validator->fails()) {
349
            return response()->json(['success' => false, 'message' => 'Invalid input'], 400);
350
        }
351
352
        $user->theme_preference = $request->input('theme_preference');
0 ignored issues
show
Bug introduced by
Accessing theme_preference on the interface Illuminate\Contracts\Auth\Authenticatable suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
353
        $user->save();
354
355
        return response()->json(['success' => true, 'theme_preference' => $user->theme_preference]);
356
    }
357
}
358