Passed
Push — master ( 2d40fa...9e2e38 )
by Darko
10:19
created

color()   A

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
use App\Models\Release;
4
use Blacklight\NZB;
5
use Blacklight\utility\Utility;
6
use Blacklight\XXX;
7
use GuzzleHttp\Client;
8
use GuzzleHttp\Cookie\CookieJar;
9
use GuzzleHttp\Cookie\SetCookie;
10
use GuzzleHttp\Exception\RequestException;
11
use Illuminate\Support\Facades\DB;
12
use Illuminate\Support\Facades\Log;
13
use Illuminate\Support\Str;
14
use sspat\ESQuerySanitizer\Sanitizer;
15
use Symfony\Component\Process\Process;
16
use Zip as ZipStream;
17
18
if (! function_exists('getRawHtml')) {
19
    /**
20
     * @param  bool  $cookie
21
     * @return bool|mixed|string
22
     */
23
    function getRawHtml($url, $cookie = false)
24
    {
25
        $cookieJar = new CookieJar;
26
        $client = new Client(['headers' => ['User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246']]);
27
        if ($cookie !== false) {
28
            $cookie = $cookieJar->setCookie(SetCookie::fromString($cookie));
0 ignored issues
show
Bug introduced by
$cookie of type true is incompatible with the type string expected by parameter $cookie of GuzzleHttp\Cookie\SetCookie::fromString(). ( Ignorable by Annotation )

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

28
            $cookie = $cookieJar->setCookie(SetCookie::fromString(/** @scrutinizer ignore-type */ $cookie));
Loading history...
29
            $client = new Client(['cookies' => $cookie, 'headers' => ['User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246']]);
30
        }
31
        try {
32
            $response = $client->get($url)->getBody()->getContents();
33
            $jsonResponse = json_decode($response, true);
34
            if (json_last_error() === JSON_ERROR_NONE) {
35
                $response = $jsonResponse;
36
            }
37
        } catch (RequestException $e) {
38
            if (config('app.debug') === true) {
39
                Log::error($e->getMessage());
40
            }
41
            $response = false;
42
        } catch (RuntimeException $e) {
43
            if (config('app.debug') === true) {
44
                Log::error($e->getMessage());
45
            }
46
            $response = false;
47
        }
48
49
        return $response;
50
    }
51
}
52
53
if (! function_exists('makeFieldLinks')) {
54
    /**
55
     * @return string
56
     *
57
     * @throws Exception
58
     */
59
    function makeFieldLinks($data, $field, $type)
60
    {
61
        $tmpArr = explode(', ', $data[$field]);
62
        $newArr = [];
63
        $i = 0;
64
        foreach ($tmpArr as $ta) {
65
            if (trim($ta) === '') {
66
                continue;
67
            }
68
            if ($type === 'xxx' && $field === 'genre') {
69
                $ta = (new XXX)->getGenres(true, $ta);
70
                $ta = $ta['title'] ?? '';
71
            }
72
            if ($i > 7) {
73
                break;
74
            }
75
            $newArr[] = '<a href="'.url('/'.ucfirst($type).'?'.$field.'='.urlencode($ta)).'" title="'.$ta.'">'.$ta.'</a>';
76
            $i++;
77
        }
78
79
        return implode(', ', $newArr);
80
    }
81
}
82
83
if (! function_exists('getUserBrowseOrder')) {
84
    /**
85
     * @param  string  $orderBy
86
     */
87
    function getUserBrowseOrder($orderBy): array
88
    {
89
        $order = ($orderBy === '' ? 'username_desc' : $orderBy);
90
        $orderArr = explode('_', $order);
91
        $orderField = match ($orderArr[0]) {
92
            'email' => 'email',
93
            'host' => 'host',
94
            'createdat' => 'created_at',
95
            'lastlogin' => 'lastlogin',
96
            'apiaccess' => 'apiaccess',
97
            'apirequests' => 'apirequests',
98
            'grabs' => 'grabs',
99
            'roles_id' => 'users_role_id',
100
            'rolechangedate' => 'rolechangedate',
101
            default => 'username',
102
        };
103
        $orderSort = (isset($orderArr[1]) && preg_match('/^asc|desc$/i', $orderArr[1])) ? $orderArr[1] : 'desc';
104
105
        return [$orderField, $orderSort];
106
    }
107
}
108
109
if (! function_exists('getUserBrowseOrdering')) {
110
    function getUserBrowseOrdering(): array
111
    {
112
        return [
113
            'username_asc',
114
            'username_desc',
115
            'email_asc',
116
            'email_desc',
117
            'host_asc',
118
            'host_desc',
119
            'createdat_asc',
120
            'createdat_desc',
121
            'lastlogin_asc',
122
            'lastlogin_desc',
123
            'apiaccess_asc',
124
            'apiaccess_desc',
125
            'apirequests_asc',
126
            'apirequests_desc',
127
            'grabs_asc',
128
            'grabs_desc',
129
            'role_asc',
130
            'role_desc',
131
            'rolechangedate_asc',
132
            'rolechangedate_desc',
133
            'verification_asc',
134
            'verification_desc',
135
        ];
136
    }
137
}
138
139
if (! function_exists('getSimilarName')) {
140
    /**
141
     * @param  string  $name
142
     */
143
    function getSimilarName($name): string
144
    {
145
        return implode(' ', \array_slice(str_word_count(str_replace(['.', '_', '-'], ' ', $name), 2), 0, 2));
0 ignored issues
show
Bug introduced by
It seems like str_word_count(str_repla..., '-'), ' ', $name), 2) can also be of type integer; however, parameter $array of array_slice() does only seem to accept array, 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

145
        return implode(' ', \array_slice(/** @scrutinizer ignore-type */ str_word_count(str_replace(['.', '_', '-'], ' ', $name), 2), 0, 2));
Loading history...
146
    }
147
}
148
149
if (! function_exists('human_filesize')) {
150
    /**
151
     * @param  int  $decimals
152
     */
153
    function human_filesize($bytes, $decimals = 0): string
154
    {
155
        $size = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
156
        $factor = floor((\strlen($bytes) - 1) / 3);
157
158
        return round(sprintf("%.{$decimals}f", $bytes / (1024 ** $factor)), $decimals).@$size[$factor];
0 ignored issues
show
Bug introduced by
sprintf('%.'.$decimals.'...ytes / 1024 ** $factor) of type string is incompatible with the type double|integer expected by parameter $num of round(). ( Ignorable by Annotation )

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

158
        return round(/** @scrutinizer ignore-type */ sprintf("%.{$decimals}f", $bytes / (1024 ** $factor)), $decimals).@$size[$factor];
Loading history...
159
    }
160
}
161
162
if (! function_exists('bcdechex')) {
163
    /**
164
     * @return string
165
     */
166
    function bcdechex($dec)
167
    {
168
        $hex = '';
169
        do {
170
            $last = bcmod($dec, 16);
171
            $hex = dechex($last).$hex;
0 ignored issues
show
Bug introduced by
$last of type null|string is incompatible with the type integer expected by parameter $num of dechex(). ( Ignorable by Annotation )

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

171
            $hex = dechex(/** @scrutinizer ignore-type */ $last).$hex;
Loading history...
172
            $dec = bcdiv(bcsub($dec, $last), 16);
173
        } while ($dec > 0);
174
175
        return $hex;
176
    }
177
}
178
179
if (! function_exists('runCmd')) {
180
    /**
181
     * Run CLI command.
182
     *
183
     *
184
     * @param  string  $command
185
     * @param  bool  $debug
186
     * @return string
187
     */
188
    function runCmd($command, $debug = false)
189
    {
190
        if ($debug) {
191
            echo '-Running Command: '.PHP_EOL.'   '.$command.PHP_EOL;
192
        }
193
194
        $process = Process::fromShellCommandline('exec '.$command);
195
        $process->setTimeout(1800);
196
        $process->run();
197
        $output = $process->getOutput();
198
199
        if ($debug) {
200
            echo '-Command Output: '.PHP_EOL.'   '.$output.PHP_EOL;
201
        }
202
203
        return $output;
204
    }
205
}
206
207
if (! function_exists('escapeString')) {
208
209
    function escapeString($string): string
210
    {
211
        return DB::connection()->getPdo()->quote($string);
212
    }
213
}
214
215
if (! function_exists('realDuration')) {
216
217
    function realDuration($milliseconds): string
218
    {
219
        $time = round($milliseconds / 1000);
220
221
        return sprintf('%02dh:%02dm:%02ds', floor($time / 3600), floor($time / 60 % 60), $time % 60);
222
    }
223
}
224
225
if (! function_exists('is_it_json')) {
226
    /**
227
     * @throws JsonException
228
     */
229
    function is_it_json($isIt): bool
230
    {
231
        if (is_array($isIt)) {
232
            return false;
233
        }
234
        json_decode($isIt, true, 512, JSON_THROW_ON_ERROR);
235
236
        return json_last_error() === JSON_ERROR_NONE;
237
    }
238
}
239
240
if (! function_exists('getStreamingZip')) {
241
    /**
242
     * @throws Exception
243
     */
244
    function getStreamingZip(array $guids = []): STS\ZipStream\Builder
245
    {
246
        $nzb = new NZB;
247
        $zipped = ZipStream::create(now()->format('Ymdhis').'.zip');
248
        foreach ($guids as $guid) {
249
            $nzbPath = $nzb->NZBPath($guid);
250
            if ($nzbPath) {
251
                $nzbContents = Utility::unzipGzipFile($nzbPath);
252
                if ($nzbContents) {
253
                    $filename = $guid;
254
                    $r = Release::query()->where('guid', $guid)->first();
255
                    if ($r) {
256
                        $filename = $r['searchname'];
257
                    }
258
                    $zipped->addRaw($nzbContents, $filename.'.nzb');
259
                }
260
            }
261
        }
262
263
        return $zipped;
264
    }
265
}
266
267
if (! function_exists('release_flag')) {
268
    // Function inspired by c0r3@newznabforums adds country flags on the browse page.
269
    /**
270
     * @param  string  $text  Text to match against.
271
     * @param  string  $page  Type of page. browse or search.
272
     */
273
    function release_flag(string $text, string $page): bool|string
274
    {
275
        $code = $language = '';
276
277
        switch (true) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match('/\bThai\b/i', $text) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match('/German(bed)?|\bger\b/i', $text) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match('/Tagalog|Filipino/i', $text) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match('/Japanese|\bjp\b/i', $text) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match('/\bGreek\b/i', $text) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match('/Italian|\bita\b/i', $text) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match('/French|Vostfr|Multi/i', $text) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match('/Korean|\bkr\b/i', $text) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match('/Swe(dish|sub)/i', $text) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match('/\bCzech\b/i', $text) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match('/Flemish|\b(...|nl)\b|NlSub/i', $text) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match('/Chinese|Man...in|\bc[hn]\b/i', $text) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match('/Hungarian|\bhun\b/i', $text) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match('/Hebrew|Yiddish/i', $text) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match('/\bHindi\b/i', $text) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
278
            case stripos($text, 'Arabic') !== false:
279
                $code = 'PK';
280
                $language = 'Arabic';
281
                break;
282
            case stripos($text, 'Cantonese') !== false:
283
                $code = 'TW';
284
                $language = 'Cantonese';
285
                break;
286
            case preg_match('/Chinese|Mandarin|\bc[hn]\b/i', $text):
287
                $code = 'CN';
288
                $language = 'Chinese';
289
                break;
290
            case preg_match('/\bCzech\b/i', $text):
291
                $code = 'CZ';
292
                $language = 'Czech';
293
                break;
294
            case stripos($text, 'Danish') !== false:
295
                $code = 'DK';
296
                $language = 'Danish';
297
                break;
298
            case stripos($text, 'Finnish') !== false:
299
                $code = 'FI';
300
                $language = 'Finnish';
301
                break;
302
            case preg_match('/Flemish|\b(Dutch|nl)\b|NlSub/i', $text):
303
                $code = 'NL';
304
                $language = 'Dutch';
305
                break;
306
            case preg_match('/French|Vostfr|Multi/i', $text):
307
                $code = 'FR';
308
                $language = 'French';
309
                break;
310
            case preg_match('/German(bed)?|\bger\b/i', $text):
311
                $code = 'DE';
312
                $language = 'German';
313
                break;
314
            case preg_match('/\bGreek\b/i', $text):
315
                $code = 'GR';
316
                $language = 'Greek';
317
                break;
318
            case preg_match('/Hebrew|Yiddish/i', $text):
319
                $code = 'IL';
320
                $language = 'Hebrew';
321
                break;
322
            case preg_match('/\bHindi\b/i', $text):
323
                $code = 'IN';
324
                $language = 'Hindi';
325
                break;
326
            case preg_match('/Hungarian|\bhun\b/i', $text):
327
                $code = 'HU';
328
                $language = 'Hungarian';
329
                break;
330
            case preg_match('/Italian|\bita\b/i', $text):
331
                $code = 'IT';
332
                $language = 'Italian';
333
                break;
334
            case preg_match('/Japanese|\bjp\b/i', $text):
335
                $code = 'JP';
336
                $language = 'Japanese';
337
                break;
338
            case preg_match('/Korean|\bkr\b/i', $text):
339
                $code = 'KR';
340
                $language = 'Korean';
341
                break;
342
            case stripos($text, 'Norwegian') !== false:
343
                $code = 'NO';
344
                $language = 'Norwegian';
345
                break;
346
            case stripos($text, 'Polish') !== false:
347
                $code = 'PL';
348
                $language = 'Polish';
349
                break;
350
            case stripos($text, 'Portuguese') !== false:
351
                $code = 'PT';
352
                $language = 'Portugese';
353
                break;
354
            case stripos($text, 'Romanian') !== false:
355
                $code = 'RO';
356
                $language = 'Romanian';
357
                break;
358
            case stripos($text, 'Spanish') !== false:
359
                $code = 'ES';
360
                $language = 'Spanish';
361
                break;
362
            case preg_match('/Swe(dish|sub)/i', $text):
363
                $code = 'SE';
364
                $language = 'Swedish';
365
                break;
366
            case preg_match('/Tagalog|Filipino/i', $text):
367
                $code = 'PH';
368
                $language = 'Tagalog|Filipino';
369
                break;
370
            case preg_match('/\bThai\b/i', $text):
371
                $code = 'TH';
372
                $language = 'Thai';
373
                break;
374
            case stripos($text, 'Turkish') !== false:
375
                $code = 'TR';
376
                $language = 'Turkish';
377
                break;
378
            case stripos($text, 'Russian') !== false:
379
                $code = 'RU';
380
                $language = 'Russian';
381
                break;
382
            case stripos($text, 'Vietnamese') !== false:
383
                $code = 'VN';
384
                $language = 'Vietnamese';
385
                break;
386
        }
387
388
        if ($code !== '' && $page === 'browse') {
389
            return '<img title="'.$language.'" alt="'.$language.'" src="'.asset('/assets/images/flags/'.$code.'.png').'"/>';
390
        }
391
392
        if ($page === 'search') {
393
            if ($code === '') {
394
                return false;
395
            }
396
397
            return $code;
398
        }
399
400
        return '';
401
    }
402
}
403
404
if (! function_exists('getReleaseCover')) {
405
    /**
406
     * Get the cover image URL for a release based on its type and ID
407
     *
408
     * @param  object|array  $release  The release object or array
409
     * @return string The cover image URL or placeholder if no cover exists
410
     */
411
    function getReleaseCover($release): string
412
    {
413
        $coverType = null;
414
        $coverId = null;
415
416
        // Helper function to get value from object or array
417
        $getValue = function ($data, $key) {
418
            if (is_array($data)) {
419
                return $data[$key] ?? null;
420
            } elseif (is_object($data)) {
421
                return $data->$key ?? null;
422
            }
423
424
            return null;
425
        };
426
427
        // Determine cover type and ID based on category
428
        $imdbid = $getValue($release, 'imdbid');
429
        $musicinfo_id = $getValue($release, 'musicinfo_id');
430
        $consoleinfo_id = $getValue($release, 'consoleinfo_id');
431
        $bookinfo_id = $getValue($release, 'bookinfo_id');
432
        $gamesinfo_id = $getValue($release, 'gamesinfo_id');
433
        $xxxinfo_id = $getValue($release, 'xxxinfo_id');
434
        $anidbid = $getValue($release, 'anidbid');
435
436
        if (! empty($imdbid) && $imdbid > 0) {
437
            $coverType = 'movies';
438
            $coverId = str_pad($imdbid, 7, '0', STR_PAD_LEFT);
439
        } elseif (! empty($musicinfo_id)) {
440
            $coverType = 'music';
441
            $coverId = $musicinfo_id;
442
        } elseif (! empty($consoleinfo_id)) {
443
            $coverType = 'console';
444
            $coverId = $consoleinfo_id;
445
        } elseif (! empty($bookinfo_id)) {
446
            $coverType = 'book';
447
            $coverId = $bookinfo_id;
448
        } elseif (! empty($gamesinfo_id)) {
449
            $coverType = 'games';
450
            $coverId = $gamesinfo_id;
451
        } elseif (! empty($xxxinfo_id)) {
452
            $coverType = 'xxx';
453
            $coverId = $xxxinfo_id;
454
        } elseif (! empty($anidbid)) {
455
            $coverType = 'anime';
456
            $coverId = $anidbid;
457
        }
458
459
        // Return the cover URL if we have a type and ID
460
        // The CoverController will handle serving the file or returning a placeholder
461
        if ($coverType && $coverId) {
462
            return url("/covers/{$coverType}/{$coverId}-cover.jpg");
463
        }
464
465
        // Return placeholder image if no cover type/ID found
466
        return asset('assets/images/no-cover.png');
467
    }
468
}
469
470
if (! function_exists('sanitize')) {
471
    function sanitize(array|string $phrases, array $doNotSanitize = []): string
472
    {
473
        if (! is_array($phrases)) {
0 ignored issues
show
introduced by
The condition is_array($phrases) is always true.
Loading history...
474
            $wordArray = explode(' ', str_replace('.', ' ', $phrases));
475
        } else {
476
            $wordArray = $phrases;
477
        }
478
479
        $keywords = [];
480
        $tempWords = [];
481
        foreach ($wordArray as $words) {
482
            $words = preg_split('/\s+/', $words);
483
            foreach ($words as $st) {
484
                if (Str::startsWith($st, ['!', '+', '-', '?', '*']) && Str::length($st) > 1 && ! preg_match('/([!+?\-*]){2,}/', $st)) {
485
                    $str = $st;
486
                } elseif (Str::endsWith($st, ['+', '-', '?', '*']) && Str::length($st) > 1 && ! preg_match('/([!+?\-*]){2,}/', $st)) {
487
                    $str = $st;
488
                } else {
489
                    $str = Sanitizer::escape($st, $doNotSanitize);
490
                }
491
                $tempWords[] = $str;
492
            }
493
494
            $keywords = $tempWords;
495
        }
496
497
        return implode(' ', $keywords);
498
    }
499
}
500
501
if (! function_exists('formatBytes')) {
502
    /**
503
     * Format bytes into human-readable file size.
504
     *
505
     * @param  int|float|null  $bytes
506
     */
507
    function formatBytes($bytes = 0): string
508
    {
509
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];
510
        $bytes = max((int) $bytes, 0);
511
        $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
512
        $pow = min($pow, count($units) - 1);
513
514
        $bytes /= pow(1024, $pow);
515
516
        return round($bytes, 2).' '.$units[$pow];
517
    }
518
}
519
520
if (! function_exists('csp_nonce')) {
521
    /**
522
     * Generate a CSP nonce for inline scripts
523
     * This should be stored in the request and reused across the request lifecycle
524
     */
525
    function csp_nonce(): string
526
    {
527
        static $nonce = null;
528
529
        if ($nonce === null) {
530
            $nonce = base64_encode(random_bytes(16));
531
        }
532
533
        return $nonce;
534
    }
535
}
536
537
if (! function_exists('userDate')) {
538
    /**
539
     * Format a date/time string according to the authenticated user's timezone
540
     *
541
     * @param  string|null  $date  The date to format
542
     * @param  string  $format  The format string (default: 'M d, Y H:i')
543
     * @return string The formatted date in user's timezone
544
     */
545
    function userDate(?string $date, string $format = 'M d, Y H:i'): string
546
    {
547
        if (empty($date)) {
548
            return '';
549
        }
550
551
        try {
552
            // Parse the date in the app's timezone (which should be UTC)
553
            // If dates in DB are stored in server timezone, they'll be parsed correctly
554
            $appTimezone = config('app.timezone', 'UTC');
555
            $carbon = \Illuminate\Support\Carbon::parse($date, $appTimezone);
556
557
            // If user is authenticated and has a timezone set, convert to it
558
            if (\Illuminate\Support\Facades\Auth::check() && \Illuminate\Support\Facades\Auth::user()->timezone) {
0 ignored issues
show
Bug introduced by
Accessing timezone on the interface Illuminate\Contracts\Auth\Authenticatable suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
559
                $carbon->setTimezone(\Illuminate\Support\Facades\Auth::user()->timezone);
560
            }
561
562
            return $carbon->format($format);
563
        } catch (\Exception $e) {
564
            return $date;
565
        }
566
    }
567
}
568
569
if (! function_exists('userDateDiffForHumans')) {
570
    /**
571
     * Format a date/time string as a human-readable diff according to the authenticated user's timezone
572
     *
573
     * @param  string|null  $date  The date to format
574
     * @return string The formatted date diff in user's timezone
575
     */
576
    function userDateDiffForHumans(?string $date): string
577
    {
578
        if (empty($date)) {
579
            return '';
580
        }
581
582
        try {
583
            // Parse the date in the app's timezone (which should be UTC)
584
            // If dates in DB are stored in server timezone, they'll be parsed correctly
585
            $appTimezone = config('app.timezone', 'UTC');
586
            $carbon = \Illuminate\Support\Carbon::parse($date, $appTimezone);
587
588
            // If user is authenticated and has a timezone set, convert to it
589
            if (\Illuminate\Support\Facades\Auth::check() && \Illuminate\Support\Facades\Auth::user()->timezone) {
0 ignored issues
show
Bug introduced by
Accessing timezone on the interface Illuminate\Contracts\Auth\Authenticatable suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
590
                $carbon->setTimezone(\Illuminate\Support\Facades\Auth::user()->timezone);
591
            }
592
593
            return $carbon->diffForHumans();
594
        } catch (\Exception $e) {
595
            return $date;
596
        }
597
    }
598
}
599
600
if (! function_exists('getAvailableTimezones')) {
601
    /**
602
     * Get a list of available timezones grouped by region
603
     *
604
     * @return array Array of timezones grouped by region
605
     */
606
    function getAvailableTimezones(): array
607
    {
608
        $timezones = [];
609
        $regions = [
610
            'Africa' => \DateTimeZone::AFRICA,
611
            'America' => \DateTimeZone::AMERICA,
612
            'Antarctica' => \DateTimeZone::ANTARCTICA,
613
            'Arctic' => \DateTimeZone::ARCTIC,
614
            'Asia' => \DateTimeZone::ASIA,
615
            'Atlantic' => \DateTimeZone::ATLANTIC,
616
            'Australia' => \DateTimeZone::AUSTRALIA,
617
            'Europe' => \DateTimeZone::EUROPE,
618
            'Indian' => \DateTimeZone::INDIAN,
619
            'Pacific' => \DateTimeZone::PACIFIC,
620
        ];
621
622
        foreach ($regions as $name => $region) {
623
            $timezones[$name] = \DateTimeZone::listIdentifiers($region);
624
        }
625
626
        return $timezones;
627
    }
628
}
629