ProfileController::updateTheme()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 10
c 0
b 0
f 0
dl 0
loc 20
rs 9.9332
cc 3
nc 3
nop 1
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
        $this->viewData = array_merge($this->viewData, [
65
            'downloadlist' => UserDownload::getDownloadRequestsForUser($userID),
66
            'apirequests' => UserRequest::getApiRequests($userID),
67
            'grabstoday' => UserDownload::getDownloadRequests($userID),
68
            '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...
69
            'user' => $this->userdata,
70
            'privateprofiles' => $privateProfiles,
71
            'publicview' => $publicView,
72
            'privileged' => $privileged,
73
            'isadmin' => $this->userdata->hasRole('Admin'),
74
            'commentslist' => ReleaseComment::getCommentsForUserRange($userID),
75
            'meta_title' => 'View User Profile',
76
            'meta_keywords' => 'view,profile,user,details',
77
            'meta_description' => 'View User Profile for '.$this->userdata->username,
78
        ]);
79
80
        return view('profile.index', $this->viewData);
81
    }
82
83
    /**
84
     * @return Factory|\Illuminate\View\View|View
85
     *
86
     * @throws \Exception
87
     */
88
    public function edit(Request $request)
89
    {
90
91
        $action = $request->input('action') ?? 'view';
92
93
        $userid = $this->userdata->id;
94
        if (! $this->userdata) {
95
            $this->show404('No such user!');
96
        }
97
98
        $errorStr = '';
99
        $success_2fa = $request->session()->get('success');
100
        $error_2fa = $request->session()->get('error');
101
102
        // Generate 2FA QR code URL if 2FA is set up but not enabled
103
        $google2fa_url = '';
104
        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...
105
            $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...
106
                config('app.name'),
107
                $this->userdata->email,
108
                $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...
109
            );
110
        }
111
112
        switch ($action) {
113
            case 'newapikey':
114
                User::updateRssKey($userid);
115
116
                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...
117
            case 'clearcookies':
118
                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...
119
            case 'submit':
120
                $validator = Validator::make($request->all(), [
121
                    'email' => ['nullable', 'string', 'email', 'max:255', 'unique:users,email,'.$userid, 'indisposable'],
122
                    'password' => ['nullable', 'string', 'min:8', 'confirmed', 'regex:/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$/'],
123
                ]);
124
125
                if ($validator->fails()) {
126
                    $errorStr = implode('', Arr::collapse($validator->errors()->toArray()));
127
                } else {
128
                    User::updateUser(
129
                        $userid,
130
                        $this->userdata->username,
131
                        $request->input('email'),
132
                        $this->userdata->grabs,
133
                        $this->userdata->roles_id,
134
                        $this->userdata->notes,
135
                        $this->userdata->invites,
136
                        $request->has('viewmovies') ? 1 : 0,
137
                        $request->has('viewaudio') ? 1 : 0,
138
                        $request->has('viewpc') ? 1 : 0,
139
                        $request->has('viewadult') ? 1 : 0,
140
                        $request->has('viewconsole') ? 1 : 0,
141
                        $request->has('viewbooks') ? 1 : 0,
142
                        'None',
143
                    );
144
145
                    // Update theme preference
146
                    if ($request->has('theme_preference')) {
147
                        $themeValue = $request->input('theme_preference');
148
                        if (in_array($themeValue, ['light', 'dark', 'system'])) {
149
                            User::where('id', $userid)->update(['theme_preference' => $themeValue]);
150
                        }
151
                    }
152
153
                    // Handle Console permission
154
                    if ($request->has('viewconsole')) {
155
                        if (! $this->userdata->hasDirectPermission('view console')) {
156
                            $this->userdata->givePermissionTo('view console');
157
                        }
158
                    } else {
159
                        if ($this->userdata->hasPermissionTo('view console')) {
160
                            $this->userdata->revokePermissionTo('view console');
161
                        }
162
                    }
163
164
                    // Handle Movies permission
165
                    if ($request->has('viewmovies')) {
166
                        if (! $this->userdata->hasDirectPermission('view movies')) {
167
                            $this->userdata->givePermissionTo('view movies');
168
                        }
169
                    } else {
170
                        if ($this->userdata->hasPermissionTo('view movies')) {
171
                            $this->userdata->revokePermissionTo('view movies');
172
                        }
173
                    }
174
175
                    // Handle Audio permission
176
                    if ($request->has('viewaudio')) {
177
                        if (! $this->userdata->hasDirectPermission('view audio')) {
178
                            $this->userdata->givePermissionTo('view audio');
179
                        }
180
                    } else {
181
                        if ($this->userdata->hasPermissionTo('view audio')) {
182
                            $this->userdata->revokePermissionTo('view audio');
183
                        }
184
                    }
185
186
                    // Handle PC/Games permission
187
                    if ($request->has('viewpc')) {
188
                        if (! $this->userdata->hasDirectPermission('view pc')) {
189
                            $this->userdata->givePermissionTo('view pc');
190
                        }
191
                    } else {
192
                        if ($this->userdata->hasPermissionTo('view pc')) {
193
                            $this->userdata->revokePermissionTo('view pc');
194
                        }
195
                    }
196
197
                    // Handle TV permission
198
                    if ($request->has('viewtv')) {
199
                        if (! $this->userdata->hasDirectPermission('view tv')) {
200
                            $this->userdata->givePermissionTo('view tv');
201
                        }
202
                    } else {
203
                        if ($this->userdata->hasPermissionTo('view tv')) {
204
                            $this->userdata->revokePermissionTo('view tv');
205
                        }
206
                    }
207
208
                    // Handle Adult permission
209
                    if ($request->has('viewadult')) {
210
                        if (! $this->userdata->hasDirectPermission('view adult')) {
211
                            $this->userdata->givePermissionTo('view adult');
212
                        }
213
                    } else {
214
                        if ($this->userdata->hasPermissionTo('view adult')) {
215
                            $this->userdata->revokePermissionTo('view adult');
216
                        }
217
                    }
218
219
                    // Handle Books permission
220
                    if ($request->has('viewbooks')) {
221
                        if (! $this->userdata->hasDirectPermission('view books')) {
222
                            $this->userdata->givePermissionTo('view books');
223
                        }
224
                    } else {
225
                        if ($this->userdata->hasPermissionTo('view books')) {
226
                            $this->userdata->revokePermissionTo('view books');
227
                        }
228
                    }
229
230
                    // Handle Other permission
231
                    if ($request->has('viewother')) {
232
                        if (! $this->userdata->hasDirectPermission('view other')) {
233
                            $this->userdata->givePermissionTo('view other');
234
                        }
235
                    } else {
236
                        if ($this->userdata->hasPermissionTo('view other')) {
237
                            $this->userdata->revokePermissionTo('view other');
238
                        }
239
                    }
240
241
                    if ($request->has('password') && ! empty($request->input('password'))) {
242
                        User::updatePassword($userid, $request->input('password'));
243
                    }
244
245
                    if (! $this->userdata->hasRole('Admin')) {
246
                        if (! empty($request->input('email')) && $this->userdata->email !== $request->input('email')) {
247
                            $this->userdata->email = $request->input('email');
248
249
                            $verify_user = $this->userdata;
250
251
                            UserVerification::generate($verify_user);
252
253
                            UserVerification::send($verify_user, 'User email verification required');
254
255
                            Auth::logout();
256
257
                            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...
258
                        }
259
                    }
260
261
                    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...
262
                }
263
                break;
264
265
            case 'view':
266
            default:
267
                break;
268
        }
269
270
        $this->viewData = array_merge($this->viewData, [
271
            'error' => $errorStr,
272
            'user' => $this->userdata,
273
            'userexccat' => User::getCategoryExclusionById($userid),
274
            'success_2fa' => $success_2fa,
275
            'error_2fa' => $error_2fa,
276
            'google2fa_url' => $google2fa_url,
277
            'yesno_ids' => [1, 0],
278
            'yesno_names' => ['Yes', 'No'],
279
            'publicview' => false,
280
            'privileged' => $this->userdata->hasRole('Admin') || $this->userdata->hasRole('Moderator'),
281
            '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...
282
            'meta_title' => 'Edit User Profile',
283
            'meta_keywords' => 'edit,profile,user,details',
284
            'meta_description' => 'Edit User Profile for '.$this->userdata->username,
285
        ]);
286
287
        return view('profile.edit', $this->viewData);
288
    }
289
290
    /**
291
     * @throws \Exception
292
     */
293
    public function destroy(Request $request): Application|View|Factory|RedirectResponse|\Illuminate\Contracts\Foundation\Application
294
    {
295
        $userId = $request->input('id');
296
297
        if ($userId !== null && (int) $userId === $this->userdata->id && ! $this->userdata->hasRole('Admin')) {
298
            $user = User::find($userId);
299
            SendAccountDeletedEmail::dispatch($user);
300
            Auth::logout();
301
            $user->delete();
302
        }
303
304
        if ($this->userdata->hasRole('Admin')) {
305
            return redirect()->to('profile');
306
        }
307
308
        return view('errors.503')->with('warning', 'Dont try to delete another user account!');
309
    }
310
311
    /**
312
     * Update user's dark mode preference
313
     */
314
    public function updateTheme(Request $request)
315
    {
316
        $user = Auth::user();
317
318
        if (! $user) {
319
            return response()->json(['success' => false, 'message' => 'User not authenticated'], 401);
320
        }
321
322
        $validator = Validator::make($request->all(), [
323
            'theme_preference' => ['required', 'in:light,dark,system'],
324
        ]);
325
326
        if ($validator->fails()) {
327
            return response()->json(['success' => false, 'message' => 'Invalid input'], 400);
328
        }
329
330
        $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...
331
        $user->save();
332
333
        return response()->json(['success' => true, 'theme_preference' => $user->theme_preference]);
334
    }
335
}
336