Passed
Push — master ( 48c78c...6f5874 )
by Darko
06:40
created

User::signUp()   B

Complexity

Conditions 7
Paths 9

Size

Total Lines 34
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 19
dl 0
loc 34
rs 8.8333
c 0
b 0
f 0
cc 7
nc 9
nop 10

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace App\Models;
4
5
use App\Jobs\SendAccountExpiredEmail;
6
use App\Jobs\SendAccountWillExpireEmail;
7
use App\Jobs\SendInviteEmail;
8
use Carbon\CarbonImmutable;
9
use Illuminate\Database\Eloquent\Builder;
10
use Illuminate\Database\Eloquent\Collection;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, App\Models\Collection. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
11
use Illuminate\Database\Eloquent\Model;
12
use Illuminate\Database\Eloquent\ModelNotFoundException;
13
use Illuminate\Database\Eloquent\Relations\BelongsTo;
14
use Illuminate\Database\Eloquent\Relations\HasMany;
15
use Illuminate\Database\Eloquent\Relations\HasOne;
16
use Illuminate\Foundation\Auth\User as Authenticatable;
17
use Illuminate\Http\Request;
18
use Illuminate\Notifications\Notifiable;
19
use Illuminate\Support\Arr;
20
use Illuminate\Support\Carbon;
21
use Illuminate\Support\Facades\Hash;
22
use Illuminate\Support\Facades\Password;
23
use Illuminate\Support\Facades\Validator;
24
use Illuminate\Support\Str;
25
use Jrean\UserVerification\Traits\UserVerification;
26
use Junaidnasir\Larainvite\Facades\Invite;
27
use Junaidnasir\Larainvite\InviteTrait;
28
use Spatie\Permission\Models\Role;
29
use Spatie\Permission\Traits\HasRoles;
0 ignored issues
show
Bug introduced by
The type Spatie\Permission\Traits\HasRoles 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...
30
31
/**
32
 * App\Models\User.
33
 *
34
 * App\Models\User.
35
 *
36
 * @property int $id
37
 * @property string $username
38
 * @property string|null $firstname
39
 * @property string|null $lastname
40
 * @property string $email
41
 * @property string $password
42
 * @property int $user_roles_id FK to roles.id
43
 * @property string|null $host
44
 * @property int $grabs
45
 * @property string $rsstoken
46
 * @property \Carbon\Carbon|null $created_at
47
 * @property \Carbon\Carbon|null $updated_at
48
 * @property string|null $resetguid
49
 * @property string|null $lastlogin
50
 * @property string|null $apiaccess
51
 * @property int $invites
52
 * @property int|null $invitedby
53
 * @property int $movieview
54
 * @property int $xxxview
55
 * @property int $musicview
56
 * @property int $consoleview
57
 * @property int $bookview
58
 * @property int $gameview
59
 * @property string|null $saburl
60
 * @property string|null $sabapikey
61
 * @property bool|null $sabapikeytype
62
 * @property bool|null $sabpriority
63
 * @property string|null $nzbgeturl
64
 * @property string|null $nzbgetusername
65
 * @property string|null $nzbgetpassword
66
 * @property string|null $nzbvortex_api_key
67
 * @property string|null $nzbvortex_server_url
68
 * @property string $userseed
69
 * @property string $notes
70
 * @property string|null $cp_url
71
 * @property string|null $cp_api
72
 * @property string|null $style
73
 * @property string|null $rolechangedate When does the role expire
74
 * @property string|null $remember_token
75
 * @property-read Collection|\App\Models\ReleaseComment[] $comment
76
 * @property-read Collection|\App\Models\UserDownload[] $download
77
 * @property-read Collection|\App\Models\DnzbFailure[] $failedRelease
78
 * @property-read Collection|\App\Models\Invitation[] $invitation
79
 * @property-read \Illuminate\Notifications\DatabaseNotificationCollection|\Illuminate\Notifications\DatabaseNotification[] $notifications
80
 * @property-read Collection|\App\Models\UsersRelease[] $release
81
 * @property-read Collection|\App\Models\UserRequest[] $request
82
 * @property-read Collection|\App\Models\UserSerie[] $series
83
 *
84
 * @method static Builder|\App\Models\User whereApiaccess($value)
85
 * @method static Builder|\App\Models\User whereBookview($value)
86
 * @method static Builder|\App\Models\User whereConsoleview($value)
87
 * @method static Builder|\App\Models\User whereCpApi($value)
88
 * @method static Builder|\App\Models\User whereCpUrl($value)
89
 * @method static Builder|\App\Models\User whereCreatedAt($value)
90
 * @method static Builder|\App\Models\User whereEmail($value)
91
 * @method static Builder|\App\Models\User whereFirstname($value)
92
 * @method static Builder|\App\Models\User whereGameview($value)
93
 * @method static Builder|\App\Models\User whereGrabs($value)
94
 * @method static Builder|\App\Models\User whereHost($value)
95
 * @method static Builder|\App\Models\User whereId($value)
96
 * @method static Builder|\App\Models\User whereInvitedby($value)
97
 * @method static Builder|\App\Models\User whereInvites($value)
98
 * @method static Builder|\App\Models\User whereLastlogin($value)
99
 * @method static Builder|\App\Models\User whereLastname($value)
100
 * @method static Builder|\App\Models\User whereMovieview($value)
101
 * @method static Builder|\App\Models\User whereMusicview($value)
102
 * @method static Builder|\App\Models\User whereNotes($value)
103
 * @method static Builder|\App\Models\User whereNzbgetpassword($value)
104
 * @method static Builder|\App\Models\User whereNzbgeturl($value)
105
 * @method static Builder|\App\Models\User whereNzbgetusername($value)
106
 * @method static Builder|\App\Models\User whereNzbvortexApiKey($value)
107
 * @method static Builder|\App\Models\User whereNzbvortexServerUrl($value)
108
 * @method static Builder|\App\Models\User wherePassword($value)
109
 * @method static Builder|\App\Models\User whereRememberToken($value)
110
 * @method static Builder|\App\Models\User whereResetguid($value)
111
 * @method static Builder|\App\Models\User whereRolechangedate($value)
112
 * @method static Builder|\App\Models\User whereRsstoken($value)
113
 * @method static Builder|\App\Models\User whereSabapikey($value)
114
 * @method static Builder|\App\Models\User whereSabapikeytype($value)
115
 * @method static Builder|\App\Models\User whereSabpriority($value)
116
 * @method static Builder|\App\Models\User whereSaburl($value)
117
 * @method static Builder|\App\Models\User whereStyle($value)
118
 * @method static Builder|\App\Models\User whereUpdatedAt($value)
119
 * @method static Builder|\App\Models\User whereUserRolesId($value)
120
 * @method static Builder|\App\Models\User whereUsername($value)
121
 * @method static Builder|\App\Models\User whereUserseed($value)
122
 * @method static Builder|\App\Models\User whereXxxview($value)
123
 * @method static Builder|\App\Models\User whereVerified($value)
124
 * @method static Builder|\App\Models\User whereApiToken($value)
125
 *
126
 * @mixin \Eloquent
127
 *
128
 * @property int $roles_id FK to roles.id
129
 * @property string $api_token
130
 * @property int $rate_limit
131
 * @property string|null $email_verified_at
132
 * @property int $verified
133
 * @property string|null $verification_token
134
 * @property-read Collection|\Junaidnasir\Larainvite\Models\LaraInviteModel[] $invitationPending
135
 * @property-read Collection|\Junaidnasir\Larainvite\Models\LaraInviteModel[] $invitationSuccess
136
 * @property-read Collection|\Junaidnasir\Larainvite\Models\LaraInviteModel[] $invitations
137
 * @property-read Collection|\Spatie\Permission\Models\Permission[] $permissions
138
 * @property-read Role $role
139
 * @property-read Collection|\Spatie\Permission\Models\Role[] $roles
140
 *
141
 * @method static Builder|\App\Models\User newModelQuery()
142
 * @method static Builder|\App\Models\User newQuery()
143
 * @method static Builder|\App\Models\User permission($permissions)
144
 * @method static Builder|\App\Models\User query()
145
 * @method static Builder|\App\Models\User whereEmailVerifiedAt($value)
146
 * @method static Builder|\App\Models\User whereRateLimit($value)
147
 * @method static Builder|\App\Models\User whereRolesId($value)
148
 * @method static Builder|\App\Models\User whereVerificationToken($value)
149
 */
150
class User extends Authenticatable
151
{
152
    use HasRoles, InviteTrait, Notifiable, UserVerification;
153
154
    public const ERR_SIGNUP_BADUNAME = -1;
155
156
    public const ERR_SIGNUP_BADPASS = -2;
157
158
    public const ERR_SIGNUP_BADEMAIL = -3;
159
160
    public const ERR_SIGNUP_UNAMEINUSE = -4;
161
162
    public const ERR_SIGNUP_EMAILINUSE = -5;
163
164
    public const ERR_SIGNUP_BADINVITECODE = -6;
165
166
    public const SUCCESS = 1;
167
168
    public const ROLE_USER = 1;
169
170
    public const ROLE_ADMIN = 2;
171
172
    /**
173
     * Users SELECT queue type.
174
     */
175
    public const QUEUE_NONE = 0;
176
177
    public const QUEUE_SABNZBD = 1;
178
179
    public const QUEUE_NZBGET = 2;
180
181
    /**
182
     * @var string
183
     */
184
185
    /**
186
     * @var bool
187
     */
188
    protected $dateFormat = false;
189
190
    /**
191
     * @var array
192
     */
193
    protected $hidden = ['remember_token', 'password'];
194
195
    /**
196
     * @var array
197
     */
198
    protected $guarded = [];
199
200
    protected function getDefaultGuardName(): string
201
    {
202
        return 'web';
203
    }
204
205
    public function role(): BelongsTo
206
    {
207
        return $this->belongsTo(Role::class, 'roles_id');
208
    }
209
210
    public function request(): HasMany
211
    {
212
        return $this->hasMany(UserRequest::class, 'users_id');
213
    }
214
215
    public function download(): HasMany
216
    {
217
        return $this->hasMany(UserDownload::class, 'users_id');
218
    }
219
220
    public function release(): HasMany
221
    {
222
        return $this->hasMany(UsersRelease::class, 'users_id');
223
    }
224
225
    public function series(): HasMany
226
    {
227
        return $this->hasMany(UserSerie::class, 'users_id');
228
    }
229
230
    public function invitation(): HasMany
231
    {
232
        return $this->hasMany(Invitation::class, 'users_id');
233
    }
234
235
    public function failedRelease(): HasMany
236
    {
237
        return $this->hasMany(DnzbFailure::class, 'users_id');
238
    }
239
240
    public function comment(): HasMany
241
    {
242
        return $this->hasMany(ReleaseComment::class, 'users_id');
243
    }
244
245
    /**
246
     * @throws \Exception
247
     */
248
    public static function deleteUser($id): void
249
    {
250
        self::find($id)->delete();
251
    }
252
253
    public static function getCount(?string $role = null, ?string $username = '', ?string $host = '', ?string $email = ''): int
254
    {
255
        $res = self::query()->where('email', '<>', '[email protected]');
256
257
        if (! empty($role)) {
258
            $res->where('roles_id', $role);
259
        }
260
261
        if ($username !== '') {
262
            $res->where('username', 'like', '%'.$username.'%');
263
        }
264
265
        if ($host !== '') {
266
            $res->where('host', 'like', '%'.$host.'%');
267
        }
268
269
        if ($email !== '') {
270
            $res->where('email', 'like', '%'.$email.'%');
271
        }
272
273
        return $res->count(['id']);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $res->count(array('id')) could return the type Illuminate\Database\Eloquent\Builder which is incompatible with the type-hinted return integer. Consider adding an additional type-check to rule them out.
Loading history...
274
    }
275
276
    public static function updateUser(int $id, string $userName, ?string $email, int $grabs, int $role, ?string $notes, int $invites, int $movieview, int $musicview, int $gameview, int $xxxview, int $consoleview, int $bookview, string $style = 'None'): int
277
    {
278
        $userName = trim($userName);
279
280
        $rateLimit = Role::query()->where('id', $role)->first();
281
282
        $sql = [
283
            'username' => $userName,
284
            'grabs' => $grabs,
285
            'roles_id' => $role,
286
            'notes' => substr($notes, 0, 255),
0 ignored issues
show
Bug introduced by
It seems like $notes can also be of type null; however, parameter $string of substr() 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

286
            'notes' => substr(/** @scrutinizer ignore-type */ $notes, 0, 255),
Loading history...
287
            'invites' => $invites,
288
            'movieview' => $movieview,
289
            'musicview' => $musicview,
290
            'gameview' => $gameview,
291
            'xxxview' => $xxxview,
292
            'consoleview' => $consoleview,
293
            'bookview' => $bookview,
294
            'style' => $style,
295
            'rate_limit' => $rateLimit ? $rateLimit['rate_limit'] : 60,
296
        ];
297
298
        if (! empty($email)) {
299
            $email = trim($email);
300
            $sql += ['email' => $email];
301
        }
302
303
        $user = self::find($id);
304
        $user->update($sql);
305
        $user->syncRoles([$rateLimit['name']]);
306
307
        return self::SUCCESS;
308
    }
309
310
    /**
311
     * @return User|Builder|Model|object|null
312
     */
313
    public static function getByUsername(string $userName)
314
    {
315
        return self::whereUsername($userName)->first();
316
    }
317
318
    /**
319
     * @return Model|static
320
     *
321
     * @throws ModelNotFoundException
322
     */
323
    public static function getByEmail(string $email)
324
    {
325
        return self::whereEmail($email)->first();
326
    }
327
328
    public static function updateUserRole(int $uid, int|string $role): bool
329
    {
330
        if (is_int($role)) {
331
            $roleQuery = Role::query()->where('id', $role)->first();
332
        } else {
333
            $roleQuery = Role::query()->where('name', $role)->first();
334
        }
335
        $roleName = $roleQuery->name;
336
337
        $user = self::find($uid);
338
        $user->syncRoles([$roleName]);
339
340
        return self::find($uid)->update(['roles_id' => $roleQuery->id]);
341
    }
342
343
    public static function updateUserRoleChangeDate(int $uid, $date = '', int $addYear = 0): void
344
    {
345
        $user = self::find($uid);
346
        $currRoleExp = $user->rolechangedate ?? now()->toDateTimeString();
347
        if (! empty($date)) {
348
            $user->update(['rolechangedate' => $date]);
349
        }
350
        if (empty($date) && ! empty($addYear)) {
351
            $user->update(['rolechangedate' => Carbon::createFromDate($currRoleExp)->addYears($addYear)]);
352
        }
353
    }
354
355
    public static function updateExpiredRoles(): void
356
    {
357
        $now = CarbonImmutable::now();
358
        $period = [
359
            'day' => $now->addDay(),
360
            'week' => $now->addWeek(),
361
            'month' => $now->addMonth(),
362
        ];
363
364
        foreach ($period as $value) {
365
            $users = self::query()->whereDate('rolechangedate', '=', $value)->get();
366
            $days = $now->diffInDays($value, true);
367
            foreach ($users as $user) {
368
                SendAccountWillExpireEmail::dispatch($user, $days)->onQueue('emails');
369
            }
370
        }
371
        foreach (self::query()->whereDate('rolechangedate', '<', $now)->get() as $expired) {
372
            $expired->update(['roles_id' => self::ROLE_USER, 'rolechangedate' => null]);
373
            $expired->syncRoles(['User']);
374
            SendAccountExpiredEmail::dispatch($expired)->onQueue('emails');
375
        }
376
    }
377
378
    /**
379
     * @throws \Throwable
380
     */
381
    public static function getRange($start, $offset, $orderBy, ?string $userName = '', ?string $email = '', ?string $host = '', ?string $role = '', bool $apiRequests = false): Collection
382
    {
383
        if ($apiRequests) {
384
            UserRequest::clearApiRequests(false);
385
            $query = "
386
				SELECT users.*, roles.name AS rolename, COUNT(user_requests.id) AS apirequests
387
				FROM users
388
				INNER JOIN roles ON roles.id = users.roles_id
389
				LEFT JOIN user_requests ON user_requests.users_id = users.id
390
				WHERE users.id != 0 %s %s %s %s
391
				AND email != '[email protected]'
392
				GROUP BY users.id
393
				ORDER BY %s %s %s ";
394
        } else {
395
            $query = '
396
				SELECT users.*, roles.name AS rolename
397
				FROM users
398
				INNER JOIN roles ON roles.id = users.roles_id
399
				WHERE 1=1 %s %s %s %s
400
				ORDER BY %s %s %s';
401
        }
402
        $order = self::getBrowseOrder($orderBy);
403
404
        return self::fromQuery(
405
            sprintf(
406
                $query,
407
                ! empty($userName) ? 'AND users.username '.'LIKE '.escapeString('%'.$userName.'%') : '',
408
                ! empty($email) ? 'AND users.email '.'LIKE '.escapeString('%'.$email.'%') : '',
409
                ! empty($host) ? 'AND users.host '.'LIKE '.escapeString('%'.$host.'%') : '',
410
                (! empty($role) ? ('AND users.roles_id = '.$role) : ''),
411
                $order[0],
412
                $order[1],
413
                ($start === false ? '' : ('LIMIT '.$offset.' OFFSET '.$start))
414
            )
415
        );
416
    }
417
418
    /**
419
     * Get sort types for sorting users on the web page user list.
420
     *
421
     * @return string[]
422
     */
423
    public static function getBrowseOrder($orderBy): array
424
    {
425
        $order = (empty($orderBy) ? 'username_desc' : $orderBy);
426
        $orderArr = explode('_', $order);
427
        $orderField = match ($orderArr[0]) {
428
            'email' => 'email',
429
            'host' => 'host',
430
            'createdat' => 'created_at',
431
            'lastlogin' => 'lastlogin',
432
            'apiaccess' => 'apiaccess',
433
            'grabs' => 'grabs',
434
            'role' => 'rolename',
435
            'rolechangedate' => 'rolechangedate',
436
            'verification' => 'verified',
437
            default => 'username',
438
        };
439
        $orderSort = (isset($orderArr[1]) && preg_match('/^asc|desc$/i', $orderArr[1])) ? $orderArr[1] : 'desc';
440
441
        return [$orderField, $orderSort];
442
    }
443
444
    /**
445
     * Verify a password against a hash.
446
     *
447
     * Automatically update the hash if it needs to be.
448
     *
449
     * @param  string  $password  Password to check against hash.
450
     * @param  bool|string  $hash  Hash to check against password.
451
     * @param  int  $userID  ID of the user.
452
     */
453
    public static function checkPassword(string $password, bool|string $hash, int $userID = -1): bool
454
    {
455
        if (Hash::check($password, $hash) === false) {
456
            return false;
457
        }
458
459
        // Update the hash if it needs to be.
460
        if (is_numeric($userID) && $userID > 0 && Hash::needsRehash($hash)) {
461
            $hash = self::hashPassword($password);
462
463
            if ($hash !== false) {
0 ignored issues
show
introduced by
The condition $hash !== false is always true.
Loading history...
464
                self::find($userID)->update(['password' => $hash]);
465
            }
466
        }
467
468
        return true;
469
    }
470
471
    public static function updateRssKey($uid): int
472
    {
473
        self::find($uid)->update(['api_token' => md5(Password::getRepository()->createNewToken())]);
0 ignored issues
show
Bug introduced by
The method createNewToken() does not exist on Illuminate\Auth\Passwords\TokenRepositoryInterface. Did you maybe mean create()? ( Ignorable by Annotation )

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

473
        self::find($uid)->update(['api_token' => md5(Password::getRepository()->/** @scrutinizer ignore-call */ createNewToken())]);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
474
475
        return self::SUCCESS;
476
    }
477
478
    public static function updatePassResetGuid($id, $guid): int
479
    {
480
        self::find($id)->update(['resetguid' => $guid]);
481
482
        return self::SUCCESS;
483
    }
484
485
    public static function updatePassword(int $id, string $password): int
486
    {
487
        self::find($id)->update(['password' => self::hashPassword($password), 'userseed' => md5(Str::uuid()->toString())]);
488
489
        return self::SUCCESS;
490
    }
491
492
    public static function hashPassword($password): string
493
    {
494
        return Hash::make($password);
495
    }
496
497
    /**
498
     * @return Model|static
499
     *
500
     * @throws ModelNotFoundException
501
     */
502
    public static function getByPassResetGuid(string $guid)
503
    {
504
        return self::whereResetguid($guid)->first();
505
    }
506
507
    public static function incrementGrabs(int $id, int $num = 1): void
508
    {
509
        self::find($id)->increment('grabs', $num);
510
    }
511
512
    /**
513
     * @return Model|null|static
514
     */
515
    public static function getByRssToken(string $rssToken)
516
    {
517
        return self::whereApiToken($rssToken)->first();
518
    }
519
520
    public static function isValidUrl($url): bool
521
    {
522
        return (! preg_match('/^(http|https|ftp):\/\/([A-Z0-9][A-Z0-9_-]*(?:\.[A-Z0-9][A-Z0-9_-]*)+):?(\d+)?\/?/i', $url)) ? false : true;
523
    }
524
525
    /**
526
     * @throws \Exception
527
     */
528
    public static function generatePassword(int $length = 15): string
529
    {
530
        return Str::password($length);
531
    }
532
533
    /**
534
     * @throws \Exception
535
     */
536
    public static function signUp($userName, $password, $email, $host, $notes, int $invites = Invitation::DEFAULT_INVITES, string $inviteCode = '', bool $forceInviteMode = false, int $role = self::ROLE_USER, bool $validate = true): bool|int|string
537
    {
538
        $user = [
539
            'username' => trim($userName),
540
            'password' => trim($password),
541
            'email' => trim($email),
542
        ];
543
544
        if ($validate) {
545
            $validator = Validator::make($user, [
546
                'username' => ['required', 'string', 'min:5', 'max:255', 'unique:users'],
547
                'email' => ['required', 'string', 'email', 'max:255', 'unique:users', 'indisposable'],
548
                'password' => ['required', 'string', 'min:8', 'confirmed', 'regex:/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$/'],
549
            ]);
550
551
            if ($validator->fails()) {
552
                return implode('', Arr::collapse($validator->errors()->toArray()));
553
            }
554
        }
555
556
        // Make sure this is the last check, as if a further validation check failed, the invite would still have been used up.
557
        $invitedBy = 0;
558
        if (! $forceInviteMode && (int) Settings::settingValue('..registerstatus') === Settings::REGISTER_STATUS_INVITE) {
559
            if ($inviteCode === '') {
560
                return self::ERR_SIGNUP_BADINVITECODE;
561
            }
562
563
            $invitedBy = self::checkAndUseInvite($inviteCode);
564
            if ($invitedBy < 0) {
565
                return self::ERR_SIGNUP_BADINVITECODE;
566
            }
567
        }
568
569
        return self::add($user['username'], $user['password'], $user['email'], $role, $notes, $host, $invites, $invitedBy);
570
    }
571
572
    /**
573
     * If a invite is used, decrement the person who invited's invite count.
574
     */
575
    public static function checkAndUseInvite(string $inviteCode): int
576
    {
577
        $invite = Invitation::getInvite($inviteCode);
578
        if (! $invite) {
579
            return -1;
580
        }
581
582
        self::query()->where('id', $invite['users_id'])->decrement('invites');
583
        Invitation::deleteInvite($inviteCode);
584
585
        return $invite['users_id'];
586
    }
587
588
    /**
589
     * @return false|int|mixed
590
     */
591
    public static function add(string $userName, string $password, string $email, int $role, ?string $notes = '', string $host = '', int $invites = Invitation::DEFAULT_INVITES, int $invitedBy = 0)
592
    {
593
        $password = self::hashPassword($password);
594
        if (! $password) {
595
            return false;
596
        }
597
598
        $storeips = (int) Settings::settingValue('..storeuserips') === 1 ? $host : '';
599
600
        $user = self::create(
601
            [
602
                'username' => $userName,
603
                'password' => $password,
604
                'email' => $email,
605
                'host' => $storeips,
606
                'roles_id' => $role,
607
                'invites' => $invites,
608
                'invitedby' => (int) $invitedBy === 0 ? null : $invitedBy,
609
                'notes' => $notes,
610
            ]
611
        );
612
613
        return $user->id;
614
    }
615
616
    /**
617
     * Get the list of categories the user has excluded.
618
     *
619
     * @param  int  $userID  ID of the user.
620
     *
621
     * @throws \Exception
622
     */
623
    public static function getCategoryExclusionById(int $userID): array
624
    {
625
        $ret = [];
626
627
        $user = self::find($userID);
628
629
        $userAllowed = $user->getDirectPermissions()->pluck('name')->toArray();
630
        $roleAllowed = $user->getAllPermissions()->pluck('name')->toArray();
631
632
        $allowed = array_intersect($roleAllowed, $userAllowed);
633
634
        $cats = ['view console', 'view movies', 'view audio', 'view tv', 'view pc', 'view adult', 'view books', 'view other'];
635
636
        if (! empty($allowed)) {
637
            foreach ($cats as $cat) {
638
                if (! \in_array($cat, $allowed, false)) {
639
                    $ret[] = match ($cat) {
640
                        'view console' => 1000,
641
                        'view movies' => 2000,
642
                        'view audio' => 3000,
643
                        'view pc' => 4000,
644
                        'view tv' => 5000,
645
                        'view adult' => 6000,
646
                        'view books' => 7000,
647
                        'view other' => 1,
648
                    };
649
                }
650
            }
651
        }
652
653
        return Category::query()->whereIn('root_categories_id', $ret)->pluck('id')->toArray();
654
    }
655
656
    /**
657
     * @throws \Exception
658
     */
659
    public static function getCategoryExclusionForApi(Request $request): array
660
    {
661
        $apiToken = $request->has('api_token') ? $request->input('api_token') : $request->input('apikey');
662
        $user = self::getByRssToken($apiToken);
663
664
        return self::getCategoryExclusionById($user->id);
665
    }
666
667
    /**
668
     * @throws \Exception
669
     */
670
    public static function sendInvite($serverUrl, $uid, $emailTo): string
671
    {
672
        $user = self::find($uid);
673
        $token = Invite::invite($emailTo, $user->id);
674
        $url = $serverUrl.'/register?invitecode='.$token;
675
676
        Invitation::addInvite($uid, $token);
677
        SendInviteEmail::dispatch($emailTo, $user, $url)->onQueue('emails');
678
679
        return $url;
680
    }
681
682
    /**
683
     * Deletes users that have not verified their accounts for 3 or more days.
684
     */
685
    public static function deleteUnVerified(): void
686
    {
687
        static::whereVerified(0)->where('created_at', '<', now()->subDays(3))->delete();
688
    }
689
690
    public function passwordSecurity(): HasOne
691
    {
692
        return $this->hasOne(PasswordSecurity::class);
693
    }
694
}
695