Completed
Push — dev ( bc6315...df572a )
by Darko
06:54
created

User::getCount()   A

Complexity

Conditions 5
Paths 16

Size

Total Lines 21
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

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