Completed
Push — dev ( 327b6b...3b3111 )
by Darko
09:05
created

User::getCategoryExclusionById()   C

Complexity

Conditions 12
Paths 2

Size

Total Lines 49
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 156

Importance

Changes 0
Metric Value
eloc 35
dl 0
loc 49
ccs 0
cts 20
cp 0
rs 6.9666
c 0
b 0
f 0
cc 12
nc 2
nop 1
crap 156

How to fix   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\Models;
4
5
use Blacklight\ColorCLI;
6
use Illuminate\Http\Request;
7
use Illuminate\Support\Str;
8
use App\Jobs\SendInviteEmail;
9
use Illuminate\Support\Facades\DB;
10
use Spatie\Permission\Models\Role;
11
use Illuminate\Support\Facades\Hash;
12
use App\Jobs\SendAccountExpiredEmail;
13
use Spatie\Permission\Traits\HasRoles;
14
use Illuminate\Notifications\Notifiable;
15
use Illuminate\Support\Facades\Password;
16
use Illuminate\Support\Facades\Validator;
17
use Jrean\UserVerification\Traits\UserVerification;
18
use Illuminate\Foundation\Auth\User as Authenticatable;
19
20
/**
21
 * App\Models\User.
22
 *
23
 * @property int $id
24
 * @property string $username
25
 * @property string|null $firstname
26
 * @property string|null $lastname
27
 * @property string $email
28
 * @property string $password
29
 * @property int $user_roles_id FK to roles.id
30
 * @property string|null $host
31
 * @property int $grabs
32
 * @property string $rsstoken
33
 * @property \Carbon\Carbon|null $created_at
34
 * @property \Carbon\Carbon|null $updated_at
35
 * @property string|null $resetguid
36
 * @property string|null $lastlogin
37
 * @property string|null $apiaccess
38
 * @property int $invites
39
 * @property int|null $invitedby
40
 * @property int $movieview
41
 * @property int $xxxview
42
 * @property int $musicview
43
 * @property int $consoleview
44
 * @property int $bookview
45
 * @property int $gameview
46
 * @property string|null $saburl
47
 * @property string|null $sabapikey
48
 * @property bool|null $sabapikeytype
49
 * @property bool|null $sabpriority
50
 * @property bool $queuetype Type of queue, Sab or NZBGet
51
 * @property string|null $nzbgeturl
52
 * @property string|null $nzbgetusername
53
 * @property string|null $nzbgetpassword
54
 * @property string|null $nzbvortex_api_key
55
 * @property string|null $nzbvortex_server_url
56
 * @property string $userseed
57
 * @property string $notes
58
 * @property string|null $cp_url
59
 * @property string|null $cp_api
60
 * @property string|null $style
61
 * @property string|null $rolechangedate When does the role expire
62
 * @property string|null $remember_token
63
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ReleaseComment[] $comment
64
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\UserDownload[] $download
65
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\DnzbFailure[] $failedRelease
66
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Invitation[] $invitation
67
 * @property-read \Illuminate\Notifications\DatabaseNotificationCollection|\Illuminate\Notifications\DatabaseNotification[] $notifications
68
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\UsersRelease[] $release
69
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\UserRequest[] $request
70
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\UserSerie[] $series
71
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereApiaccess($value)
72
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereBookview($value)
73
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereConsoleview($value)
74
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereCpApi($value)
75
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereCpUrl($value)
76
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereCreatedAt($value)
77
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereEmail($value)
78
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereFirstname($value)
79
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereGameview($value)
80
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereGrabs($value)
81
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereHost($value)
82
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereId($value)
83
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereInvitedby($value)
84
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereInvites($value)
85
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereLastlogin($value)
86
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereLastname($value)
87
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereMovieview($value)
88
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereMusicview($value)
89
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereNotes($value)
90
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereNzbgetpassword($value)
91
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereNzbgeturl($value)
92
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereNzbgetusername($value)
93
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereNzbvortexApiKey($value)
94
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereNzbvortexServerUrl($value)
95
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User wherePassword($value)
96
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereQueuetype($value)
97
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereRememberToken($value)
98
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereResetguid($value)
99
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereRolechangedate($value)
100
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereRsstoken($value)
101
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereSabapikey($value)
102
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereSabapikeytype($value)
103
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereSabpriority($value)
104
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereSaburl($value)
105
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereStyle($value)
106
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereUpdatedAt($value)
107
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereUserRolesId($value)
108
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereUsername($value)
109
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereUserseed($value)
110
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereXxxview($value)
111
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereVerified($value)
112
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User whereApiToken($value)
113
 * @mixin \Eloquent
114
 */
115
class User extends Authenticatable
116
{
117
    use Notifiable, UserVerification, HasRoles;
0 ignored issues
show
Bug introduced by
The trait Illuminate\Notifications\Notifiable requires the property $phone_number which is not provided by App\Models\User.
Loading history...
introduced by
The trait Spatie\Permission\Traits\HasRoles requires some properties which are not provided by App\Models\User: $name, $map, $permissions, $roles, $guard_name
Loading history...
introduced by
The trait Jrean\UserVerification\Traits\UserVerification requires some properties which are not provided by App\Models\User: $verified, $verification_token
Loading history...
118
119
    public const ERR_SIGNUP_BADUNAME = -1;
120
    public const ERR_SIGNUP_BADPASS = -2;
121
    public const ERR_SIGNUP_BADEMAIL = -3;
122
    public const ERR_SIGNUP_UNAMEINUSE = -4;
123
    public const ERR_SIGNUP_EMAILINUSE = -5;
124
    public const ERR_SIGNUP_BADINVITECODE = -6;
125
    public const ERR_SIGNUP_BADCAPTCHA = -7;
126
    public const SUCCESS = 1;
127
128
    public const ROLE_USER = 1;
129
    public const ROLE_ADMIN = 2;
130
    public const ROLE_DISABLED = 3;
131
    public const ROLE_MODERATOR = 4;
132
133
    /**
134
     * Users SELECT queue type.
135
     */
136
    public const QUEUE_NONE = 0;
137
    public const QUEUE_SABNZBD = 1;
138
    public const QUEUE_NZBGET = 2;
139
140
    /**
141
     * @var string
142
     */
143
    protected $table = 'users';
144
145
    /**
146
     * @var bool
147
     */
148
    protected $dateFormat = false;
149
150
    /**
151
     * @var array
152
     */
153
    protected $hidden = ['remember_token', 'password'];
154
155
    /**
156
     * @var array
157
     */
158
    protected $guarded = [];
159
160
    /**
161
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
162
     */
163
    public function role()
164
    {
165
        return $this->belongsTo(Role::class, 'roles_id');
166
    }
167
168
    /**
169
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
170
     */
171
    public function request()
172
    {
173
        return $this->hasMany(UserRequest::class, 'users_id');
174
    }
175
176
    /**
177
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
178
     */
179
    public function download()
180
    {
181
        return $this->hasMany(UserDownload::class, 'users_id');
182
    }
183
184
    /**
185
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
186
     */
187
    public function release()
188
    {
189
        return $this->hasMany(UsersRelease::class, 'users_id');
190
    }
191
192
    /**
193
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
194
     */
195
    public function series()
196
    {
197
        return $this->hasMany(UserSerie::class, 'users_id');
198
    }
199
200
    /**
201
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
202
     */
203
    public function invitation()
204
    {
205
        return $this->hasMany(Invitation::class, 'users_id');
206
    }
207
208
    /**
209
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
210
     */
211
    public function failedRelease()
212
    {
213
        return $this->hasMany(DnzbFailure::class, 'users_id');
214
    }
215
216
    /**
217
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
218
     */
219
    public function comment()
220
    {
221
        return $this->hasMany(ReleaseComment::class, 'users_id');
222
    }
223
224
    /**
225
     * @param $id
226
     * @throws \Exception
227
     */
228
    public static function deleteUser($id): void
229
    {
230
        self::whereId($id)->delete();
231
    }
232
233
    /**
234
     * @param string $role
235
     * @param string $username
236
     * @param string $host
237
     * @param string $email
238
     * @return int
239
     */
240
    public static function getCount($role = '', $username = '', $host = '', $email = ''): int
241
    {
242
        $res = self::query()->where('email', '<>', '[email protected]');
243
244
        if ($role !== '') {
245
            $res->where('roles_id', $role);
246
        }
247
248
        if ($username !== '') {
249
            $res->where('username', 'like', '%'.$username.'%');
250
        }
251
252
        if ($host !== '') {
253
            $res->where('host', 'like', '%'.$host.'%');
254
        }
255
256
        if ($email !== '') {
257
            $res->where('email', 'like', '%'.$email.'%');
258
        }
259
260
        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...
261
    }
262
263
    /**
264
     * @param  int      $id
265
     * @param  string      $userName
266
     * @param  string      $email
267
     * @param  int     $grabs
268
     * @param  int     $role
269
     * @param  string    $notes
270
     * @param  int     $invites
271
     * @param  int     $movieview
272
     * @param  int    $musicview
273
     * @param  int   $gameview
274
     * @param  int    $xxxview
275
     * @param  int    $consoleview
276
     * @param  int    $bookview
277
     * @param string $queueType
278
     * @param string $nzbgetURL
279
     * @param string $nzbgetUsername
280
     * @param string $nzbgetPassword
281
     * @param string $saburl
282
     * @param string $sabapikey
283
     * @param string $sabpriority
284
     * @param string $sabapikeytype
285
     * @param bool   $nzbvortexServerUrl
286
     * @param bool   $nzbvortexApiKey
287
     * @param bool   $cp_url
288
     * @param bool   $cp_api
289
     * @param string $style
290
     *
291
     * @return int
292
     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
293
     */
294
    public static function updateUser($id, $userName, $email, $grabs, $role, $notes, $invites, $movieview, $musicview, $gameview, $xxxview, $consoleview, $bookview, $queueType = '', $nzbgetURL = '', $nzbgetUsername = '', $nzbgetPassword = '', $saburl = '', $sabapikey = '', $sabpriority = '', $sabapikeytype = '', $nzbvortexServerUrl = false, $nzbvortexApiKey = false, $cp_url = false, $cp_api = false, $style = 'None'): int
295
    {
296
        $userName = trim($userName);
297
298
        $rateLimit = Role::query()->where('id', $role)->first();
299
300
        $sql = [
301
            'username' => $userName,
302
            'grabs' => $grabs,
303
            'roles_id' => $role,
304
            'notes' => substr($notes, 0, 255),
305
            'invites' => $invites,
306
            'movieview' => $movieview,
307
            'musicview' => $musicview,
308
            'gameview' => $gameview,
309
            'xxxview' => $xxxview,
310
            'consoleview' => $consoleview,
311
            'bookview' => $bookview,
312
            'style' => $style,
313
            'queuetype'  => $queueType,
314
            'nzbgeturl' => $nzbgetURL,
315
            'nzbgetusername' => $nzbgetUsername,
316
            'nzbgetpassword' => $nzbgetPassword,
317
            'saburl' => $saburl,
318
            'sabapikey' => $sabapikey,
319
            'sabapikeytype' => $sabapikeytype,
320
            'sabpriority' => $sabpriority,
321
            'nzbvortex_server_url' => $nzbvortexServerUrl,
322
            'nzbvortex_api_key' => $nzbvortexApiKey,
323
            'cp_url' => $cp_url,
324
            'cp_api' => $cp_api,
325
            'rate_limit' => $rateLimit ? $rateLimit['rate_limit'] : 60,
326
        ];
327
328
        if (! empty($email)) {
329
            $email = trim($email);
330
            $sql += ['email' => $email];
331
        }
332
333
        self::whereId($id)->update($sql);
334
335
        $user = self::find($id);
336
        $user->syncRoles([$rateLimit['name']]);
337
338
        return self::SUCCESS;
339
    }
340
341
    /**
342
     * @param string $userName
343
     * @return \Illuminate\Database\Eloquent\Model|null|static
344
     */
345
    public static function getByUsername(string $userName)
346
    {
347
        return self::whereUsername($userName)->first();
348
    }
349
350
    /**
351
     * @param string $email
352
     *
353
     * @return \Illuminate\Database\Eloquent\Model|static
354
     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
355
     */
356
    public static function getByEmail(string $email)
357
    {
358
        return self::whereEmail($email)->first();
359
    }
360
361
    /**
362
     * @param int $uid
363
     * @param int $role
364
     * @return int
365
     */
366
    public static function updateUserRole(int $uid, int $role): int
367
    {
368
        return self::whereId($uid)->update(['roles_id' => $role]);
369
    }
370
371
    /**
372
     * @param $uid
373
     * @param $date
374
     * @return int
375
     */
376
    public static function updateUserRoleChangeDate($uid, $date): int
377
    {
378
        return self::whereId($uid)->update(['rolechangedate' => $date]);
379
    }
380
381
    /**
382
     * @return int
383
     */
384
    public static function updateExpiredRoles(): int
385
    {
386
        $data = self::query()->whereDate('rolechangedate', '<', now())->get();
387
388
        foreach ($data as $u) {
389
            $user = self::find($u['id']);
390
            $user->update(['roles_id' => self::ROLE_USER, 'rolechangedate' => null]);
391
            $user->syncRoles('User');
392
            SendAccountExpiredEmail::dispatch($user);
393
        }
394
395
        return self::SUCCESS;
396
    }
397
398
    /**
399
     * @param        $start
400
     * @param        $offset
401
     * @param        $orderBy
402
     * @param string $userName
403
     * @param string $email
404
     * @param string $host
405
     * @param string $role
406
     * @param bool   $apiRequests
407
     *
408
     * @return array
409
     * @throws \Throwable
410
     */
411
    public static function getRange($start, $offset, $orderBy, $userName = '', $email = '', $host = '', $role = '', $apiRequests = false): array
412
    {
413
        if ($apiRequests) {
414
            UserRequest::clearApiRequests(false);
415
            $query = "
416
				SELECT users.*, roles.name AS rolename, COUNT(user_requests.id) AS apirequests
417
				FROM users
418
				INNER JOIN roles ON roles.id = users.roles_id
419
				LEFT JOIN user_requests ON user_requests.users_id = users.id
420
				WHERE users.id != 0 %s %s %s %s
421
				AND email != '[email protected]'
422
				GROUP BY users.id
423
				ORDER BY %s %s %s ";
424
        } else {
425
            $query = '
426
				SELECT users.*, roles.name AS rolename,
427
				FROM users
428
				INNER JOIN roles ON roles.id = users.roles_id
429
				WHERE 1=1 %s %s %s %s
430
				ORDER BY %s %s %s';
431
        }
432
        $order = self::getBrowseOrder($orderBy);
433
434
        return DB::select(
435
            sprintf(
436
                $query,
437
                ! empty($userName) ? 'AND users.username '.'LIKE '.escapeString('%'.$userName.'%') : '',
438
                ! empty($email) ? 'AND users.email '.'LIKE '.escapeString('%'.$email.'%') : '',
439
                ! empty($host) ? 'AND users.host '.'LIKE '.escapeString('%'.$host.'%') : '',
440
                (! empty($role) ? ('AND users.roles_id = '.$role) : ''),
441
                $order[0],
442
                $order[1],
443
                ($start === false ? '' : ('LIMIT '.$offset.' OFFSET '.$start))
444
            )
445
        );
446
    }
447
448
    /**
449
     * Get sort types for sorting users on the web page user list.
450
     *
451
     * @param $orderBy
452
     *
453
     * @return string[]
454
     */
455
    public static function getBrowseOrder($orderBy): array
456
    {
457
        $order = (empty($orderBy) ? 'username_desc' : $orderBy);
458
        $orderArr = explode('_', $order);
459
        switch ($orderArr[0]) {
460
            case 'username':
461
                $orderField = 'username';
462
                break;
463
            case 'email':
464
                $orderField = 'email';
465
                break;
466
            case 'host':
467
                $orderField = 'host';
468
                break;
469
            case 'createdat':
470
                $orderField = 'created_at';
471
                break;
472
            case 'lastlogin':
473
                $orderField = 'lastlogin';
474
                break;
475
            case 'apiaccess':
476
                $orderField = 'apiaccess';
477
                break;
478
            case 'grabs':
479
                $orderField = 'grabs';
480
                break;
481
            case 'role':
482
                $orderField = 'rolename';
483
                break;
484
            case 'rolechangedate':
485
                $orderField = 'rolechangedate';
486
                break;
487
            case 'verification':
488
                $orderField = 'verified';
489
                break;
490
            default:
491
                $orderField = 'username';
492
                break;
493
        }
494
        $orderSort = (isset($orderArr[1]) && preg_match('/^asc|desc$/i', $orderArr[1])) ? $orderArr[1] : 'desc';
495
496
        return [$orderField, $orderSort];
497
    }
498
499
    /**
500
     * Verify a password against a hash.
501
     *
502
     * Automatically update the hash if it needs to be.
503
     *
504
     * @param string $password Password to check against hash.
505
     * @param string|bool $hash     Hash to check against password.
506
     * @param int    $userID   ID of the user.
507
     *
508
     * @return bool
509
     */
510
    public static function checkPassword($password, $hash, $userID = -1): bool
511
    {
512
        if (Hash::check($password, $hash) === false) {
513
            return false;
514
        }
515
516
        // Update the hash if it needs to be.
517
        if (is_numeric($userID) && $userID > 0 && Hash::needsRehash($hash)) {
518
            $hash = self::hashPassword($password);
519
520
            if ($hash !== false) {
0 ignored issues
show
introduced by
The condition $hash !== false is always true.
Loading history...
521
                self::whereId($userID)->update(['password' => $hash]);
522
            }
523
        }
524
525
        return true;
526
    }
527
528
    /**
529
     * @param $uid
530
     *
531
     * @return int
532
     */
533
    public static function updateRssKey($uid): int
534
    {
535
        self::whereId($uid)->update(['api_token' => md5(Password::getRepository()->createNewToken())]);
536
537
        return self::SUCCESS;
538
    }
539
540
    /**
541
     * @param $id
542
     * @param $guid
543
     *
544
     * @return int
545
     */
546
    public static function updatePassResetGuid($id, $guid): int
547
    {
548
        self::whereId($id)->update(['resetguid' => $guid]);
549
550
        return self::SUCCESS;
551
    }
552
553
    /**
554
     * @param int    $id
555
     * @param string $password
556
     *
557
     * @return int
558
     */
559
    public static function updatePassword(int $id, string $password): int
560
    {
561
        self::whereId($id)->update(['password' => self::hashPassword($password), 'userseed' => md5(Str::uuid()->toString())]);
562
563
        return self::SUCCESS;
564
    }
565
566
    /**
567
     * @param $password
568
     * @return mixed
569
     */
570
    public static function hashPassword($password)
571
    {
572
        return Hash::make($password);
573
    }
574
575
    /**
576
     * @param $guid
577
     *
578
     * @return \Illuminate\Database\Eloquent\Model|static
579
     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
580
     */
581
    public static function getByPassResetGuid(string $guid)
582
    {
583
        return self::whereResetguid($guid)->first();
584
    }
585
586
    /**
587
     * @param     $id
588
     * @param int $num
589
     */
590
    public static function incrementGrabs(int $id, $num = 1): void
591
    {
592
        self::whereId($id)->increment('grabs', $num);
593
    }
594
595
    /**
596
     * Check if the user is in the database, and if their API key is good, return user data if so.
597
     *
598
     *
599
     * @param $userID
600
     * @param $rssToken
601
     *
602
     * @return bool|\Illuminate\Database\Eloquent\Model|null|static
603
     */
604
    public static function getByIdAndRssToken($userID, $rssToken)
605
    {
606
        $user = self::query()->where(['id' => $userID, 'api_token' => $rssToken])->first();
607
        if ($user === null) {
608
            return false;
609
        }
610
611
        return $user;
612
    }
613
614
    /**
615
     * @param string $rssToken
616
     * @return \Illuminate\Database\Eloquent\Model|null|static
617
     */
618
    public static function getByRssToken(string $rssToken)
619
    {
620
        return self::whereApiToken($rssToken)->first();
621
    }
622
623
    /**
624
     * @param $url
625
     *
626
     * @return bool
627
     */
628
    public static function isValidUrl($url): bool
629
    {
630
        return (! preg_match('/^(http|https|ftp):\/\/([A-Z0-9][A-Z0-9_-]*(?:\.[A-Z0-9][A-Z0-9_-]*)+):?(\d+)?\/?/i', $url)) ? false : true;
631
    }
632
633
    /**
634
     * Generate a random username.
635
     *
636
     *
637
     * @return string
638
     */
639
    public static function generateUsername(): string
640
    {
641
        return Str::random();
642
    }
643
644
    /**
645
     * @param int $length
646
     *
647
     * @return string
648
     * @throws \Exception
649
     */
650
    public static function generatePassword($length = 15): string
651
    {
652
        return \Token::random($length, true);
0 ignored issues
show
Bug introduced by
The type Token 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...
653
    }
654
655
    /**
656
     * Register a new user.
657
     *
658
     * @param        $userName
659
     * @param        $password
660
     * @param        $email
661
     * @param        $host
662
     * @param int    $role
663
     * @param        $notes
664
     * @param int    $invites
665
     * @param string $inviteCode
666
     * @param bool   $forceInviteMode
667
     *
668
     * @return bool|int
669
     * @throws \Exception
670
     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
671
     */
672
    public static function signUp($userName, $password, $email, $host, $notes, $invites = Invitation::DEFAULT_INVITES, $inviteCode = '', $forceInviteMode = false, $role = self::ROLE_USER)
673
    {
674
        $user = [
675
            'username' => trim($userName),
676
            'password' => trim($password),
677
            'email' => trim($email),
678
        ];
679
680
        $validator = Validator::make($user, [
681
            'username' => ['required', 'string', 'min:5', 'max:255', 'unique:users'],
682
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users', 'indisposable'],
683
            'password' => ['required', 'string', 'min:8', 'confirmed', 'regex:/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$/'],
684
        ]);
685
686
        if ($validator->fails()) {
687
            (new ColorCLI())->error(implode('', array_collapse($validator->errors()->toArray())));
688
        }
689
690
        // Make sure this is the last check, as if a further validation check failed, the invite would still have been used up.
691
        $invitedBy = 0;
692
        if (! $forceInviteMode && (int) Settings::settingValue('..registerstatus') === Settings::REGISTER_STATUS_INVITE) {
693
            if ($inviteCode === '') {
694
                return self::ERR_SIGNUP_BADINVITECODE;
695
            }
696
697
            $invitedBy = self::checkAndUseInvite($inviteCode);
698
            if ($invitedBy < 0) {
699
                return self::ERR_SIGNUP_BADINVITECODE;
700
            }
701
        }
702
703
        return self::add($user['userName'], $user['password'], $user['email'], $role, $notes, $host, $invites, $invitedBy);
704
    }
705
706
    /**
707
     * If a invite is used, decrement the person who invited's invite count.
708
     *
709
     * @param string $inviteCode
710
     *
711
     * @return int
712
     */
713
    public static function checkAndUseInvite(string $inviteCode): int
714
    {
715
        $invite = Invitation::getInvite($inviteCode);
716
        if (! $invite) {
717
            return -1;
718
        }
719
720
        self::query()->where('id', $invite['users_id'])->decrement('invites');
721
        Invitation::deleteInvite($inviteCode);
722
723
        return $invite['users_id'];
724
    }
725
726
    /**
727
     * Add a new user.
728
     *
729
     * @param string    $userName
730
     * @param  string   $password
731
     * @param  string   $email
732
     * @param int    $role
733
     * @param string    $notes
734
     * @param string    $host
735
     * @param int $invites
736
     * @param int $invitedBy
737
     *
738
     * @return bool|int
739
     * @throws \Exception
740
     */
741
    public static function add($userName, $password, $email, $role, $notes, $host, $invites = Invitation::DEFAULT_INVITES, $invitedBy = 0)
742
    {
743
        $password = self::hashPassword($password);
744
        if (! $password) {
745
            return false;
746
        }
747
748
        $roleData = Role::query()->where('id', $role);
749
        $rateLimit = $roleData->value('rate_limit');
750
        $roleName = $roleData->value('name');
751
752
        $storeips = (int) Settings::settingValue('..storeuserips') === 1 ? $host : '';
753
754
        $user = self::create(
755
            [
756
                'username' => $userName,
757
                'password' => $password,
758
                'email' => $email,
759
                'host' => $storeips,
760
                'roles_id' => $role,
761
                'api_token' => md5(Password::getRepository()->createNewToken()),
762
                'invites' => $invites,
763
                'invitedby' => (int) $invitedBy === 0 ? null : $invitedBy,
764
                'userseed' => md5(Str::uuid()->toString()),
765
                'notes' => $notes,
766
                'rate_limit' => $rateLimit,
767
            ]
768
        );
769
770
        $user->assignRole($roleName);
771
772
        return $user->id;
773
    }
774
775
    /**
776
     * When a user logs in, update the last time they logged in.
777
     *
778
     * @param int    $userID ID of the user.
779
     * @param string $host
780
     */
781
    public static function updateSiteAccessed($userID, $host = ''): void
782
    {
783
        self::whereId($userID)->update(
784
            [
785
                'lastlogin' => now(),
786
                'host' => $host,
787
            ]
788
        );
789
    }
790
791
    /**
792
     * @param $uid
793
     */
794
    public static function updateApiAccessed($uid): void
795
    {
796
        self::whereId($uid)->update(['apiaccess' => date('Y-m-d h:m:s')]);
797
    }
798
799
    /**
800
     * Get the list of categories the user has excluded.
801
     *
802
     * @param int $userID ID of the user.
803
     *
804
     * @return array
805
     * @throws \Exception
806
     */
807
    public static function getCategoryExclusionById($userID): array
808
    {
809
        $ret = [];
810
811
        $user = self::find($userID);
812
813
        $userAllowed = $user->getDirectPermissions()->pluck('name')->toArray();
814
        $roleAllowed = $user->getAllPermissions()->pluck('name')->toArray();
815
816
        $allowed = array_intersect($roleAllowed, $userAllowed);
817
818
        $cats = ['view console', 'view movies', 'view audio', 'view tv', 'view pc', 'view adult', 'view books', 'view other'];
819
820
        if (! empty($allowed)) {
821
            foreach ($cats as $cat) {
822
                if (! \in_array($cat, $allowed, false)) {
823
                    switch ($cat) {
824
                        case 'view console':
825
                            $ret[] = 1000;
826
                            continue 2;
827
                        case 'view movies':
828
                            $ret[] = 2000;
829
                            continue 2;
830
                        case 'view audio':
831
                            $ret[] = 3000;
832
                            continue 2;
833
                        case 'view tv':
834
                            $ret[] = 4000;
835
                            continue 2;
836
                        case 'view pc':
837
                            $ret[] = 5000;
838
                            continue 2;
839
                        case 'view adult':
840
                            $ret[] = 6000;
841
                            continue 2;
842
                        case 'view books':
843
                            $ret[] = 7000;
844
                            continue 2;
845
                        case 'view other':
846
                            $ret[] = 1;
847
848
                    }
849
                }
850
            }
851
        }
852
853
        $exclusion = Category::query()->whereIn('parentid', $ret)->pluck('id')->toArray();
854
855
        return $exclusion;
856
    }
857
858
    /**
859
     * @param \Illuminate\Http\Request $request
860
     * @return array
861
     * @throws \Exception
862
     */
863
    public static function getCategoryExclusionForApi(Request $request): array
864
    {
865
        $user = self::getByRssToken($request->input('api_token'));
866
        return self::getCategoryExclusionById($user->id);
867
    }
868
869
    /**
870
     * @return \Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection|static[]
871
     */
872
    public static function getTopGrabbers()
873
    {
874
        return self::query()->selectRaw('id, username, SUM(grabs) as grabs')->groupBy('id', 'username')->having('grabs', '>', 0)->orderBy('grabs', 'desc')->limit(10)->get();
875
    }
876
877
    /**
878
     * @return \Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection|static[]
879
     */
880
    public static function getUsersByMonth()
881
    {
882
        return self::query()->whereNotNull('created_at')->where('created_at', '<>', '0000-00-00 00:00:00')->selectRaw("DATE_FORMAT(created_at, '%M %Y') as mth, COUNT(id) as num")->groupBy(['mth'])->orderBy('created_at', 'desc')->get();
883
    }
884
885
    /**
886
     * @param $serverUrl
887
     * @param $uid
888
     * @param $emailTo
889
     *
890
     * @return string
891
     * @throws \Exception
892
     */
893
    public static function sendInvite($serverUrl, $uid, $emailTo): string
894
    {
895
        $token = \Token::randomString(40);
896
        $url = $serverUrl.'register?invitecode='.$token;
897
898
        Invitation::addInvite($uid, $token);
899
        SendInviteEmail::dispatch($emailTo, $uid, $url);
900
901
        return $url;
902
    }
903
904
    /**
905
     * Deletes old rows FROM the user_requests and user_downloads tables.
906
     * if site->userdownloadpurgedays SET to 0 then all release history is removed but
907
     * the download/request rows must remain for at least one day to allow the role based
908
     * limits to apply.
909
     *
910
     * @param int $days
911
     */
912
    public static function pruneRequestHistory($days = 0): void
913
    {
914
        if ($days === 0) {
915
            $days = 1;
916
            UserDownload::query()->update(['releases_id' => null]);
917
        }
918
919
        UserRequest::query()->where('timestamp', '<', now()->subDays($days))->delete();
920
        UserDownload::query()->where('timestamp', '<', now()->subDays($days))->delete();
921
    }
922
923
    /**
924
     * Deletes users that have not verified their accounts for 3 or more days.
925
     */
926
    public static function deleteUnVerified(): void
927
    {
928
        static::whereVerified(0)->where('created_at', '<', now()->subDays(3))->delete();
929
    }
930
}
931