UsenetGroup::getGroupByID()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace App\Models;
4
5
use Blacklight\ColorCLI;
6
use Blacklight\NNTP;
7
use Blacklight\NZB;
8
use Blacklight\ReleaseImage;
9
use Blacklight\Releases;
10
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
11
use Illuminate\Database\Eloquent\Model;
12
use Illuminate\Database\Eloquent\Relations\HasMany;
13
use Illuminate\Support\Facades\DB;
14
15
/**
16
 * App\Models\Group.
17
 *
18
 * @property int $id
19
 * @property string $name
20
 * @property int $backfill_target
21
 * @property int $first_record
22
 * @property string|null $first_record_postdate
23
 * @property int $last_record
24
 * @property string|null $last_record_postdate
25
 * @property string|null $last_updated
26
 * @property int|null $minfilestoformrelease
27
 * @property int|null $minsizetoformrelease
28
 * @property bool $active
29
 * @property bool $backfill
30
 * @property string|null $description
31
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Release[] $release
32
 *
33
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UsenetGroup whereActive($value)
34
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UsenetGroup whereBackfill($value)
35
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UsenetGroup whereBackfillTarget($value)
36
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UsenetGroup whereDescription($value)
37
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UsenetGroup whereFirstRecord($value)
38
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UsenetGroup whereFirstRecordPostdate($value)
39
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UsenetGroup whereId($value)
40
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UsenetGroup whereLastRecord($value)
41
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UsenetGroup whereLastRecordPostdate($value)
42
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UsenetGroup whereLastUpdated($value)
43
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UsenetGroup whereMinfilestoformrelease($value)
44
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UsenetGroup whereMinsizetoformrelease($value)
45
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UsenetGroup whereName($value)
46
 *
47
 * @mixin \Eloquent
48
 *
49
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UsenetGroup newModelQuery()
50
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UsenetGroup newQuery()
51
 * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\UsenetGroup query()
52
 */
53
class UsenetGroup extends Model
54
{
55
    /**
56
     * @var bool
57
     */
58
    protected $dateFormat = false;
59
60
    /**
61
     * @var bool
62
     */
63
    public $timestamps = false;
64
65
    /**
66
     * @var array
67
     */
68
    protected $guarded = [];
69
70
    /**
71
     * @var array
72
     */
73
    protected static $cbpm = ['collections', 'binaries', 'parts', 'missed_parts'];
74
75
    /**
76
     * @var array
77
     */
78
    protected static $cbppTableNames;
79
80
    /**
81
     * @var bool
82
     */
83
    protected $allasmgr;
84
85
    /**
86
     * Group constructor.
87
     *
88
     * @throws \Exception
89
     */
90
    public function __construct()
91
    {
92
        parent::__construct();
93
    }
94
95
    public function release(): HasMany
96
    {
97
        return $this->hasMany(Release::class, 'groups_id');
98
    }
99
100
    /**
101
     * Returns an associative array of groups for list selection.
102
     */
103
    public static function getGroupsForSelect(): array
104
    {
105
        $groups = self::getActive();
106
107
        $temp_array = [];
108
        $temp_array[-1] = '--Please Select--';
109
110
        foreach ($groups as $group) {
111
            $temp_array[$group['name']] = $group['name'];
112
        }
113
114
        return $temp_array;
115
    }
116
117
    /**
118
     * Get all properties of a single group by its ID.
119
     *
120
     *
121
     * @return Model|null|static
122
     */
123
    public static function getGroupByID($id)
124
    {
125
        return self::query()->where('id', $id)->first();
126
    }
127
128
    /**
129
     * @return \Illuminate\Database\Eloquent\Collection|static[]
130
     */
131
    public static function getActive()
132
    {
133
        return self::query()->where('active', '=', 1)->orderBy('name')->get();
134
    }
135
136
    /**
137
     * Get active backfill groups ordered by name ascending.
138
     *
139
     *
140
     * @return array|\Illuminate\Database\Eloquent\Collection|static[]
141
     */
142
    public static function getActiveBackfill($order)
143
    {
144
        switch ($order) {
145
            case '':
146
            case 'normal':
147
                return self::query()->where('backfill', '=', 1)->where('last_record', '<>', 0)->orderBy('name')->get();
148
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
149
            case 'date':
150
                return self::query()->where('backfill', '=', 1)->where('last_record', '<>', 0)->orderByDesc('first_record_postdate')->get();
151
                break;
152
            default:
153
                return [];
154
        }
155
    }
156
157
    /**
158
     * Get all active group IDs.
159
     *
160
     *
161
     * @return \Illuminate\Database\Eloquent\Collection|static[]
162
     */
163
    public static function getActiveIDs()
164
    {
165
        return self::query()->where('active', '=', 1)->orderBy('name')->get(['id']);
166
    }
167
168
    /**
169
     * Get all group columns by Name.
170
     *
171
     *
172
     * @return Model|null|static
173
     */
174
    public static function getByName($grp)
175
    {
176
        return self::query()->where('name', $grp)->first();
177
    }
178
179
    /**
180
     * Get a group name using its ID.
181
     *
182
     * @param  int|string  $id  The group ID.
183
     * @return string Empty string on failure, groupName on success.
184
     */
185
    public static function getNameByID($id): string
186
    {
187
        $res = self::query()->where('id', $id)->first(['name']);
188
189
        return $res !== null ? $res->name : '';
190
    }
191
192
    /**
193
     * Get a group ID using its name.
194
     *
195
     * @param  string  $name  The group name.
196
     * @return false|int false on failure, groups_id on success.
197
     */
198
    public static function getIDByName(string $name)
199
    {
200
        $res = self::query()->where('name', $name)->first(['id']);
201
202
        return $res === null ? false : $res->id;
203
    }
204
205
    /**
206
     * Gets a count of all groups in the table limited by parameters.
207
     *
208
     * @param  string  $groupname  Constrain query to specific group name
209
     * @param  int  $active  Constrain query to active status
210
     * @return mixed
211
     */
212
    public static function getGroupsCount(string $groupname = '', int $active = -1)
213
    {
214
        $res = self::query();
215
216
        if ($groupname !== '') {
217
            $res->where('name', 'like', '%'.$groupname.'%');
218
        }
219
220
        if ($active > -1) {
221
            $res->where('active', $active);
222
        }
223
224
        return $res === null ? 0 : $res->count(['id']);
225
    }
226
227
    public static function getGroupsRange(string $groupname = '', $active = null): LengthAwarePaginator
228
    {
229
        $groups = self::query()->groupBy('id')->orderBy('name');
0 ignored issues
show
Bug introduced by
'name' of type string is incompatible with the type Closure|Illuminate\Datab...\Database\Query\Builder expected by parameter $column of Illuminate\Database\Query\Builder::orderBy(). ( Ignorable by Annotation )

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

229
        $groups = self::query()->groupBy('id')->orderBy(/** @scrutinizer ignore-type */ 'name');
Loading history...
230
231
        if ($groupname !== '') {
232
            $groups->where('name', 'like', '%'.$groupname.'%');
233
        }
234
235
        if ($active === true) {
236
            $groups->where('active', '=', 1);
237
        } elseif ($active === false) {
238
            $groups->where('active', '=', 0);
239
        }
240
241
        return $groups->paginate(config('nntmux.items_per_page'));
242
    }
243
244
    /**
245
     * Update an existing group.
246
     */
247
    public static function updateGroup($group): int
248
    {
249
        return self::query()->where('id', $group['id'])->update(
250
            [
251
                'name' => trim($group['name']),
252
                'description' => trim($group['description']),
253
                'backfill_target' => $group['backfill_target'],
254
                'first_record' => $group['first_record'],
255
                'last_record' => $group['last_record'],
256
                'last_updated' => now(),
257
                'active' => $group['active'],
258
                'backfill' => $group['backfill'],
259
                'minsizetoformrelease' => $group['minsizetoformrelease'] === '' ? null : $group['minsizetoformrelease'],
260
                'minfilestoformrelease' => $group['minfilestoformrelease'] === '' ? null : $group['minfilestoformrelease'],
261
            ]
262
        );
263
    }
264
265
    /**
266
     * Checks group name is standard and replaces any shorthand prefixes.
267
     *
268
     * @param  string  $groupName  The full name of the usenet group being evaluated
269
     * @return string|bool The name of the group replacing shorthand prefix or false if groupname was malformed
270
     */
271
    public static function isValidGroup(string $groupName)
272
    {
273
        if (preg_match('/^([\w\-]+\.)+[\w\-]+$/i', $groupName)) {
274
            return preg_replace('/^a\.b\./i', 'alt.binaries.', $groupName, 1);
275
        }
276
277
        return false;
278
    }
279
280
    /**
281
     * Add a new group.
282
     *
283
     *
284
     * @return int|mixed
285
     */
286
    public static function addGroup($group)
287
    {
288
        $checkOld = UsenetGroup::query()->where('name', trim($group['name']))->first();
289
        if (empty($checkOld)) {
290
            return self::query()->insertGetId([
291
                'name' => trim($group['name']),
292
                'description' => isset($group['description']) ? trim($group['description']) : '',
293
                'backfill_target' => $group['backfill_target'] ?? 1,
294
                'first_record' => $group['first_record'] ?? 0,
295
                'last_record' => $group['last_record'] ?? 0,
296
                'active' => $group['active'] ?? 0,
297
                'backfill' => $group['backfill'] ?? 0,
298
                'minsizetoformrelease' => $group['minsizetoformrelease'] ?? null,
299
                'minfilestoformrelease' => $group['minfilestoformrelease'] ?? null,
300
            ]);
301
        }
302
303
        return $checkOld->id;
304
    }
305
306
    /**
307
     * Delete a group.
308
     *
309
     * @param  int|string  $id  ID of the group.
310
     *
311
     * @throws \Exception
312
     */
313
    public static function deleteGroup($id): bool
314
    {
315
        self::purge($id);
316
317
        return self::query()->where('id', $id)->delete();
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::query()->where('id', $id)->delete() could return the type integer which is incompatible with the type-hinted return boolean. Consider adding an additional type-check to rule them out.
Loading history...
318
    }
319
320
    /**
321
     * Reset a group.
322
     *
323
     * @param  string|int  $id  The group ID.
324
     *
325
     * @throws \Exception
326
     */
327
    public static function reset($id): bool
328
    {
329
        // Remove rows from part repair.
330
        MissedPart::query()->where('groups_id', $id)->delete();
331
332
        // Reset the group stats.
333
        return self::query()->where('id', $id)->update(
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::query()->wh...> null, 'active' => 0)) returns the type integer which is incompatible with the type-hinted return boolean.
Loading history...
334
            [
335
                'backfill_target' => 1,
336
                'first_record' => 0,
337
                'first_record_postdate' => null,
338
                'last_record' => 0,
339
                'last_record_postdate' => null,
340
                'last_updated' => null,
341
                'active' => 0,
342
            ]
343
        );
344
    }
345
346
    /**
347
     * Reset all groups.
348
     */
349
    public static function resetall(): bool
350
    {
351
        // Disable foreign key checks to allow truncating tables with foreign key constraints
352
        DB::statement('SET FOREIGN_KEY_CHECKS=0');
353
354
        try {
355
            // Truncate tables in reverse order to respect foreign key relationships
356
            // (child tables first: missed_parts, parts, binaries, then parent: collections)
357
            foreach (array_reverse(self::$cbpm) as $tablePrefix) {
358
                DB::statement("TRUNCATE TABLE {$tablePrefix}");
359
            }
360
        } finally {
361
            // Always re-enable foreign key checks, even if truncate fails
362
            DB::statement('SET FOREIGN_KEY_CHECKS=1');
363
        }
364
365
        // Reset the group stats.
366
367
        return self::query()->update(
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::query()->up...> null, 'active' => 0)) returns the type integer which is incompatible with the type-hinted return boolean.
Loading history...
368
            [
369
                'backfill_target' => 1,
370
                'first_record' => 0,
371
                'first_record_postdate' => null,
372
                'last_record' => 0,
373
                'last_record_postdate' => null,
374
                'last_updated' => null,
375
                'active' => 0,
376
            ]
377
        );
378
    }
379
380
    /**
381
     * Purge a single group or all groups.
382
     *
383
     * @param  int|string|bool  $id  The group ID. If false, purge all groups.
384
     *
385
     * @throws \Exception
386
     */
387
    public static function purge($id = false)
388
    {
389
        if ($id === false) {
390
            self::resetall();
391
        } else {
392
            self::reset($id);
0 ignored issues
show
Bug introduced by
It seems like $id can also be of type true; however, parameter $id of App\Models\UsenetGroup::reset() does only seem to accept integer|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

392
            self::reset(/** @scrutinizer ignore-type */ $id);
Loading history...
393
        }
394
395
        $res = Release::query()->select(['id', 'guid']);
396
397
        if ($id !== false) {
398
            $res->where('groups_id', $id);
399
        }
400
401
        $res->get();
402
403
        $releases = new Releases;
404
        $nzb = new NZB;
405
        $releaseImage = new ReleaseImage;
406
        foreach ($res as $row) {
407
            $releases->deleteSingle(
408
                [
409
                    'g' => $row['guid'],
410
                    'i' => $row['id'],
411
                ],
412
                $nzb,
413
                $releaseImage
414
            );
415
        }
416
    }
417
418
    /**
419
     * Adds new newsgroups based on a regular expression match against USP available.
420
     *
421
     * @return array|string
422
     *
423
     * @throws \Exception
424
     */
425
    public static function addBulk(string $groupList, int $active = 1, int $backfill = 1)
426
    {
427
        if (preg_match('/^\s*$/m', $groupList)) {
428
            $ret = 'No group list provided.';
429
        } else {
430
            $nntp = new NNTP(['Echo' => false]);
0 ignored issues
show
Unused Code introduced by
The call to Blacklight\NNTP::__construct() has too many arguments starting with array('Echo' => false). ( Ignorable by Annotation )

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

430
            $nntp = /** @scrutinizer ignore-call */ new NNTP(['Echo' => false]);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
431
            if ($nntp->doConnect() !== true) {
432
                return 'Problem connecting to usenet.';
433
            }
434
            $groups = $nntp->getGroups();
435
            $nntp->doQuit();
436
437
            if ($nntp->isError($groups)) {
438
                return 'Problem fetching usenet_groups from usenet.';
439
            }
440
441
            $regFilter = '/'.$groupList.'/i';
442
443
            $ret = [];
444
445
            foreach ($groups as $group) {
446
                if (preg_match($regFilter, $group['group']) > 0) {
447
                    $res = self::getIDByName($group['group']);
448
                    if ($res === false) {
449
                        self::addGroup(
450
                            [
451
                                'name' => $group['group'],
452
                                'active' => $active,
453
                                'backfill' => $backfill,
454
                                'description' => 'Added by bulkAdd',
455
                            ]
456
                        );
457
                        $ret[] = ['group' => $group['group'], 'msg' => 'Created'];
458
                    }
459
                }
460
            }
461
462
            if (\count($ret) === 0) {
463
                $ret[] = ['group' => '', 'msg' => 'No groups found with your regex or groups already exist in database, try again!'];
464
            }
465
        }
466
467
        return $ret;
468
    }
469
470
    /**
471
     * Updates the group active/backfill status.
472
     *
473
     * @param  int  $id  Which group ID
474
     * @param  string  $column  Which column active/backfill
475
     * @param  int  $status  Which status we are setting
476
     */
477
    public static function updateGroupStatus(int $id, string $column, int $status = 0): string
478
    {
479
        self::query()->where('id', $id)->update(
480
            [
481
                $column => $status,
482
            ]
483
        );
484
485
        return "Group {$id} has been ".(($status === 0) ? 'deactivated' : 'activated').'.';
486
    }
487
488
    /**
489
     * Disable group that does not exist on USP server.
490
     *
491
     * @param  int  $id  The Group ID to disable
492
     */
493
    public static function disableIfNotExist(int $id): void
494
    {
495
        self::updateGroupStatus($id, 'active');
496
        (new ColorCLI)->error('Group does not exist on server, disabling');
497
    }
498
}
499