User::getRange()   B
last analyzed

Complexity

Conditions 9
Paths 2

Size

Total Lines 35
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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