Passed
Pull Request — master (#6468)
by
unknown
08:13
created

ChamiloHelper::saveUserTermsAcceptance()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 29
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 18
nc 3
nop 2
dl 0
loc 29
rs 9.6666
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/* For licensing terms, see /license.txt */
6
7
namespace Chamilo\CoreBundle\Helpers;
8
9
use Chamilo\CoreBundle\Framework\Container;
10
use ChamiloSession as Session;
11
use DateTime;
12
use Display;
13
use Database;
14
use DateInterval;
15
use DateTimeZone;
16
use Event;
17
use ExtraFieldValue;
18
use FormValidator;
19
use LegalManager;
20
use MessageManager;
21
use Template;
22
use UserManager;
23
24
use const PHP_SAPI;
25
26
class ChamiloHelper
27
{
28
    public const COURSE_MANAGER = 1;
29
    public const SESSION_ADMIN = 3;
30
    public const DRH = 4;
31
    public const STUDENT = 5;
32
    public const ANONYMOUS = 6;
33
34
    private static array $configuration;
35
36
    public function setConfiguration(array $configuration): void
37
    {
38
        self::$configuration = $configuration;
39
    }
40
41
    public static function getConfigurationArray(): array
42
    {
43
        return self::$configuration;
44
    }
45
46
    public static function getConfigurationValue(string $variable): mixed
47
    {
48
        $configuration = self::getConfigurationArray();
49
        if (\array_key_exists($variable, $configuration)) {
50
            return $configuration[$variable];
51
        }
52
53
        return false;
54
    }
55
56
    /**
57
     * Returns an array of resolutions that can be used for the conversion of documents to images.
58
     */
59
    public static function getDocumentConversionSizes(): array
60
    {
61
        return [
62
            '540x405' => '540x405 (3/4)',
63
            '640x480' => '640x480 (3/4)',
64
            '720x540' => '720x540 (3/4)',
65
            '800x600' => '800x600 (3/4)',
66
            '1024x576' => '1024x576 (16/9)',
67
            '1024x768' => '1000x750 (3/4)',
68
            '1280x720' => '1280x720 (16/9)',
69
            '1280x860' => '1280x960 (3/4)',
70
            '1400x1050' => '1400x1050 (3/4)',
71
            '1600x900' => '1600x900 (16/9)',
72
        ];
73
    }
74
75
    /**
76
     * Get the platform logo path.
77
     *
78
     * @deprecated
79
     *
80
     * @throws Exception
81
     */
82
    public static function getPlatformLogoPath(
83
        string $theme = '',
84
        bool $getSysPath = false,
85
        bool $forcedGetter = false
86
    ): ?string {
87
        static $logoPath;
88
89
        // If call from CLI it should be reloaded.
90
        if ('cli' === PHP_SAPI) {
91
            $logoPath = null;
92
        }
93
94
        if (!isset($logoPath) || $forcedGetter) {
95
            $theme = empty($theme) ? api_get_visual_theme() : $theme;
96
            $accessUrlId = api_get_current_access_url_id();
97
            if ('cli' === PHP_SAPI) {
98
                $accessUrl = api_get_configuration_value('access_url');
99
                if (!empty($accessUrl)) {
100
                    $accessUrlId = $accessUrl;
101
                }
102
            }
103
            $themeDir = Template::getThemeDir($theme);
104
            $customLogoPath = $themeDir.\sprintf('images/header-logo-custom%s.png', $accessUrlId);
105
106
            $svgIcons = api_get_setting('icons_mode_svg');
107
            if ('true' === $svgIcons) {
108
                $customLogoPathSVG = substr($customLogoPath, 0, -3).'svg';
109
                if (file_exists(api_get_path(SYS_PUBLIC_PATH).\sprintf('css/%s', $customLogoPathSVG))) {
110
                    if ($getSysPath) {
111
                        return api_get_path(SYS_PUBLIC_PATH).\sprintf('css/%s', $customLogoPathSVG);
112
                    }
113
114
                    return api_get_path(WEB_CSS_PATH).$customLogoPathSVG;
115
                }
116
            }
117
            if (file_exists(api_get_path(SYS_PUBLIC_PATH).\sprintf('css/%s', $customLogoPath))) {
118
                if ($getSysPath) {
119
                    return api_get_path(SYS_PUBLIC_PATH).\sprintf('css/%s', $customLogoPath);
120
                }
121
122
                return api_get_path(WEB_CSS_PATH).$customLogoPath;
123
            }
124
125
            $originalLogoPath = $themeDir.'images/header-logo.png';
126
            if ('true' === $svgIcons) {
127
                $originalLogoPathSVG = $themeDir.'images/header-logo.svg';
128
                if (file_exists(api_get_path(SYS_CSS_PATH).$originalLogoPathSVG)) {
129
                    if ($getSysPath) {
130
                        return api_get_path(SYS_CSS_PATH).$originalLogoPathSVG;
131
                    }
132
133
                    return api_get_path(WEB_CSS_PATH).$originalLogoPathSVG;
134
                }
135
            }
136
137
            if (file_exists(api_get_path(SYS_CSS_PATH).$originalLogoPath)) {
138
                if ($getSysPath) {
139
                    return api_get_path(SYS_CSS_PATH).$originalLogoPath;
140
                }
141
142
                return api_get_path(WEB_CSS_PATH).$originalLogoPath;
143
            }
144
            $logoPath = '';
145
        }
146
147
        return $logoPath;
148
    }
149
150
    /**
151
     * Get the platform logo.
152
     * Return a <img> if the logo image exists.
153
     * Otherwise, return a <h2> with the institution name.
154
     *
155
     * @throws Exception
156
     */
157
    public static function getPlatformLogo(
158
        string $theme = '',
159
        array $imageAttributes = [],
160
        bool $getSysPath = false,
161
        bool $forcedGetter = false
162
    ): string {
163
        $logoPath = Container::getThemeHelper()->getThemeAssetUrl('images/header-logo.svg');
164
165
        if (empty($logoPath)) {
166
            $logoPath = Container::getThemeHelper()->getThemeAssetUrl('images/header-logo.png');
167
        }
168
169
        $institution = api_get_setting('Institution');
170
        $institutionUrl = api_get_setting('InstitutionUrl');
171
        $siteName = api_get_setting('siteName');
172
173
        if (null === $logoPath) {
174
            $headerLogo = Display::url($siteName, api_get_path(WEB_PATH).'index.php');
175
176
            if (!empty($institutionUrl) && !empty($institution)) {
177
                $headerLogo .= ' - '.Display::url($institution, $institutionUrl);
178
            }
179
180
            $courseInfo = api_get_course_info();
181
            if (isset($courseInfo['extLink']) && !empty($courseInfo['extLink']['name'])) {
182
                $headerLogo .= '<span class="extLinkSeparator"> - </span>';
183
184
                if (!empty($courseInfo['extLink']['url'])) {
185
                    $headerLogo .= Display::url(
186
                        $courseInfo['extLink']['name'],
187
                        $courseInfo['extLink']['url'],
188
                        [
189
                            'class' => 'extLink',
190
                        ]
191
                    );
192
                } elseif (!empty($courseInfo['extLink']['url'])) {
193
                    $headerLogo .= $courseInfo['extLink']['url'];
194
                }
195
            }
196
197
            return Display::tag('h2', $headerLogo, [
198
                'class' => 'text-left',
199
            ]);
200
        }
201
202
        $image = Display::img($logoPath, $institution, $imageAttributes);
203
204
        return Display::url($image, api_get_path(WEB_PATH).'index.php');
205
    }
206
207
    /**
208
     * Like strip_tags(), but leaves an additional space and removes only the given tags.
209
     *
210
     * @param array $tags Tags to be removed
211
     *
212
     * @return string The original string without the given tags
213
     */
214
    public static function stripGivenTags(string $string, array $tags): string
215
    {
216
        foreach ($tags as $tag) {
217
            $string2 = preg_replace('#</\b'.$tag.'\b[^>]*>#i', ' ', $string);
218
            if ($string2 !== $string) {
219
                $string = preg_replace('/<\b'.$tag.'\b[^>]*>/i', ' ', $string2);
220
            }
221
        }
222
223
        return $string;
224
    }
225
226
    /**
227
     * Adds or Subtract a time in hh:mm:ss to a datetime.
228
     *
229
     * @param string $time      Time to add or substract in hh:mm:ss format
230
     * @param string $datetime  Datetime to be modified as accepted by the Datetime class constructor
231
     * @param bool   $operation True for Add, False to Subtract
232
     *
233
     * @throws Exception
234
     */
235
    public static function addOrSubTimeToDateTime(
236
        string $time,
237
        string $datetime = 'now',
238
        bool $operation = true
239
    ): string {
240
        $date = new DateTime($datetime);
241
        $hours = 0;
242
        $minutes = 0;
243
        $seconds = 0;
244
        sscanf($time, '%d:%d:%d', $hours, $minutes, $seconds);
245
        $timeSeconds = isset($seconds) ? $hours * 3600 + $minutes * 60 + $seconds : $hours * 60 + $minutes;
246
        if ($operation) {
247
            $date->add(new DateInterval('PT'.$timeSeconds.'S'));
248
        } else {
249
            $date->sub(new DateInterval('PT'.$timeSeconds.'S'));
250
        }
251
252
        return $date->format('Y-m-d H:i:s');
253
    }
254
255
    /**
256
     * Returns the course id (integer) for the given course directory or the current ID if no directory is defined.
257
     *
258
     * @param string|null $directory The course directory/path that appears in the URL
259
     *
260
     * @throws Exception
261
     */
262
    public static function getCourseIdByDirectory(?string $directory = null): int
263
    {
264
        if (!empty($directory)) {
265
            $directory = Database::escape_string($directory);
266
            $row = Database::select(
267
                'id',
268
                Database::get_main_table(TABLE_MAIN_COURSE),
269
                [
270
                    'where' => [
271
                        'directory = ?' => [$directory],
272
                    ],
273
                ],
274
                'first'
275
            );
276
277
            if (\is_array($row) && isset($row['id'])) {
278
                return $row['id'];
279
            }
280
281
            return 0;
282
        }
283
284
        return (int) Session::read('_real_cid', 0);
285
    }
286
287
    /**
288
     * Check if the current HTTP request is by AJAX.
289
     */
290
    public static function isAjaxRequest(): bool
291
    {
292
        $requestedWith = $_SERVER['HTTP_X_REQUESTED_WITH'] ?? null;
293
294
        return 'XMLHttpRequest' === $requestedWith;
295
    }
296
297
    /**
298
     * Get a variable name for language file from a text.
299
     */
300
    public static function getLanguageVar(string $text, string $prefix = ''): string
301
    {
302
        $text = api_replace_dangerous_char($text);
303
        $text = str_replace(['-', ' ', '.'], '_', $text);
304
        $text = preg_replace('/_+/', '_', $text);
305
        // $text = str_replace('_', '', $text);
306
        $text = api_underscore_to_camel_case($text);
307
308
        return $prefix.$text;
309
    }
310
311
    /**
312
     * Get the stylesheet path for HTML blocks created with CKEditor.
313
     */
314
    public static function getEditorBlockStylePath(): string
315
    {
316
        $visualTheme = api_get_visual_theme();
317
318
        $cssFile = api_get_path(SYS_CSS_PATH).\sprintf('themes/%s/editor_content.css', $visualTheme);
319
320
        if (is_file($cssFile)) {
321
            return api_get_path(WEB_CSS_PATH).\sprintf('themes/%s/editor_content.css', $visualTheme);
322
        }
323
324
        return api_get_path(WEB_CSS_PATH).'editor_content.css';
325
    }
326
327
    /**
328
     * Get a list of colors from the palette at main/palette/pchart/default.color
329
     * and return it as an array of strings.
330
     *
331
     * @param bool     $decimalOpacity Whether to return the opacity as 0..100 or 0..1
332
     * @param bool     $wrapInRGBA     Whether to return it as 1,1,1,100 or rgba(1,1,1,100)
333
     * @param int|null $fillUpTo       If the number of colors is smaller than this number, generate more colors
334
     *
335
     * @return array An array of string colors
336
     */
337
    public static function getColorPalette(
338
        bool $decimalOpacity = false,
339
        bool $wrapInRGBA = false,
340
        ?int $fillUpTo = null
341
    ): array {
342
        // Get the common colors from the palette used for pchart
343
        $paletteFile = api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color';
344
        $palette = file($paletteFile);
345
        if ($decimalOpacity) {
346
            // Because the pchart palette has transparency as integer values
347
            // (0..100) and chartjs uses percentage (0.0..1.0), we need to divide
348
            // the last value by 100, which is a bit overboard for just one chart
349
            foreach ($palette as $index => $color) {
350
                $components = explode(',', trim($color));
351
                $components[3] = round((int) $components[3] / 100, 1);
352
                $palette[$index] = implode(',', $components);
353
            }
354
        }
355
        if ($wrapInRGBA) {
356
            foreach ($palette as $index => $color) {
357
                $color = trim($color);
358
                $palette[$index] = 'rgba('.$color.')';
359
            }
360
        }
361
        // If we want more colors, loop through existing colors
362
        $count = \count($palette);
363
        if (isset($fillUpTo) && $fillUpTo > $count) {
364
            for ($i = $count; $i < $fillUpTo; $i++) {
365
                $palette[$i] = $palette[$i % $count];
366
            }
367
        }
368
369
        return $palette;
370
    }
371
372
    /**
373
     * Get the local time for the midnight.
374
     *
375
     * @param null|string $utcTime Optional. The time to ve converted.
376
     *                             See api_get_local_time.
377
     *
378
     * @throws Exception
379
     */
380
    public static function getServerMidnightTime(?string $utcTime = null): DateTime
381
    {
382
        $localTime = api_get_local_time($utcTime);
383
        $localTimeZone = api_get_timezone();
384
385
        $localMidnight = new DateTime($localTime, new DateTimeZone($localTimeZone));
386
        $localMidnight->modify('midnight');
387
388
        return $localMidnight;
389
    }
390
391
    /**
392
     * Get JavaScript code necessary to load quiz markers-rolls in medialement's Markers Rolls plugin.
393
     */
394
    public static function getQuizMarkersRollsJS(): string
395
    {
396
        $webCodePath = api_get_path(WEB_CODE_PATH);
397
        $cidReq = api_get_cidreq(true, true, 'embeddable');
398
        $colorPalette = self::getColorPalette(false, true);
399
400
        return "
401
            var \$originalNode = $(originalNode),
402
                    qMarkersRolls = \$originalNode.data('q-markersrolls') || [],
403
                    qMarkersColor = \$originalNode.data('q-markersrolls-color') || '$colorPalette[0]';
404
405
                if (0 == qMarkersRolls.length) {
406
                    return;
407
                }
408
409
                instance.options.markersRollsColor = qMarkersColor;
410
                instance.options.markersRollsWidth = 2;
411
                instance.options.markersRolls = {};
412
413
                qMarkersRolls.forEach(function (qMarkerRoll) {
414
                    var url = '{$webCodePath}exercise/exercise_submit.php?$cidReq&'
415
                        + $.param({
416
                            exerciseId: qMarkerRoll[1],
417
                            learnpath_id: 0,
418
                            learnpath_item_id: 0,
419
                            learnpath_item_view_id: 0
420
                        });
421
422
                    instance.options.markersRolls[qMarkerRoll[0]] = url;
423
                });
424
425
                instance.buildmarkersrolls(instance, instance.controls, instance.layers, instance.media);
426
        ";
427
    }
428
429
    /**
430
     * Performs a redirection to the specified URL.
431
     *
432
     * This method sends a direct HTTP Location header to the client,
433
     * causing the browser to navigate to the specified URL. It should be
434
     * used with caution and only in scenarios where Symfony's standard
435
     * response handling is not applicable. The method terminates script
436
     * execution after sending the header.
437
     */
438
    public static function redirectTo(string $url): void
439
    {
440
        if (!empty($url)) {
441
            header("Location: $url");
442
443
            exit;
444
        }
445
    }
446
447
    /**
448
     * Checks if the current user has accepted the Terms & Conditions.
449
     */
450
    public static function userHasAcceptedTerms(): bool
451
    {
452
        $termRegistered = Session::read('term_and_condition');
453
454
        return isset($termRegistered['user_id']);
455
    }
456
457
    /**
458
     * Redirects to the Terms and Conditions page.
459
     */
460
    public static function redirectToTermsAndConditions(): void
461
    {
462
        $url = self::getTermsAndConditionsUrl();
463
        self::redirectTo($url);
464
    }
465
466
    /**
467
     * Returns the URL of the Terms and Conditions page.
468
     */
469
    public static function getTermsAndConditionsUrl(): string
470
    {
471
        return api_get_path(WEB_PATH) . 'main/auth/tc.php';
472
    }
473
474
    /**
475
     * Returns the URL of the Registration page.
476
     */
477
    public static function getRegistrationUrl(): string
478
    {
479
        return api_get_path(WEB_PATH) . 'main/auth/registration.php';
480
    }
481
482
    /**
483
     * Adds legal terms acceptance fields into a registration form.
484
     */
485
    public static function addLegalTermsFields(FormValidator $form, bool $userAlreadyRegisteredShowTerms): void
486
    {
487
        if ('true' !== api_get_setting('allow_terms_conditions') || $userAlreadyRegisteredShowTerms) {
488
            return;
489
        }
490
491
        $languageIso  = api_get_language_isocode();
492
        $languageId   = api_get_language_id($languageIso);
493
        $termPreview  = LegalManager::get_last_condition($languageId);
494
495
        if (!$termPreview) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $termPreview of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
496
            $platformLang  = api_get_setting('language.platform_language');
497
            $languageId    = api_get_language_id($platformLang);
498
            $termPreview   = LegalManager::get_last_condition($languageId);
499
        }
500
        if (!$termPreview) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $termPreview of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
501
            return;
502
        }
503
504
        // hidden inputs to track version/language
505
        $form->addElement('hidden', 'legal_accept_type', $termPreview['version'] . ':' . $termPreview['language_id']);
506
        $form->addElement('hidden', 'legal_info',        $termPreview['id']      . ':' . $termPreview['language_id']);
507
508
        if (1 == $termPreview['type']) {
509
            // simple checkbox linking out to full T&C
510
            $form->addElement(
511
                'checkbox',
512
                'legal_accept',
513
                null,
514
                'I have read and agree to the <a href="tc.php" target="_blank">Terms and Conditions</a>'
515
            );
516
            $form->addRule('legal_accept', 'This field is required', 'required');
517
        } else {
518
            // full inline T&C panel with scroll
519
            $preview = LegalManager::show_last_condition($termPreview);
520
            $form->addHtml(
521
                '<div style="
522
                background-color: #f3f4f6;
523
                border: 1px solid #d1d5db;
524
                padding: 1rem;
525
                max-height: 16rem;
526
                overflow-y: auto;
527
                border-radius: 0.375rem;
528
                margin-bottom: 1rem;
529
            ">'
530
                . $preview .
531
                '</div>'
532
            );
533
534
            // any extra labels
535
            $extra  = new ExtraFieldValue('terms_and_condition');
536
            $values = $extra->getAllValuesByItem($termPreview['id']);
537
            foreach ($values as $value) {
538
                if (!empty($value['field_value'])) {
539
                    $form->addLabel($value['display_text'], $value['field_value']);
540
                }
541
            }
542
543
            // acceptance checkbox
544
            $form->addElement(
545
                'checkbox',
546
                'legal_accept',
547
                null,
548
                'I have read and agree to the Terms and Conditions'
549
            );
550
            $form->addRule('legal_accept', 'This field is required', 'required');
551
        }
552
    }
553
554
555
    /**
556
     * Saves the user’s acceptance of the Terms & Conditions.
557
     */
558
    /**
559
     * Persist a user’s acceptance of the last T&C version.
560
     */
561
    /**
562
     * Persists the user's acceptance of the terms & conditions.
563
     *
564
     * @param int    $userId
565
     * @param string $legalAcceptType version:language_id
566
     */
567
    public static function saveUserTermsAcceptance(int $userId, string $legalAcceptType): void
568
    {
569
        // **Split and build the stored value**
570
        [$version, $languageId] = explode(':', $legalAcceptType);
571
        $timestamp = time();
572
        $toSave = (int)$version . ':' . (int)$languageId . ':' . $timestamp;
573
574
        // **Save in extra-field**
575
        UserManager::update_extra_field_value($userId, 'legal_accept', $toSave);
576
577
        // **Log event**
578
        Event::addEvent(
0 ignored issues
show
Bug introduced by
The method addEvent() does not exist on Event. ( Ignorable by Annotation )

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

578
        Event::/** @scrutinizer ignore-call */ 
579
               addEvent(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
579
            LOG_TERM_CONDITION_ACCEPTED,
580
            LOG_USER_OBJECT,
581
            api_get_user_info($userId),
582
            api_get_utc_datetime()
583
        );
584
585
        // **Notificar a tutores si hace falta**
586
        $bossList = UserManager::getStudentBossList($userId);
587
        if (!empty($bossList)) {
588
            $bossIds = array_column($bossList, 'boss_id');
589
            $current = api_get_user_info($userId);
590
            $dateStr = api_get_local_time($timestamp);
591
592
            foreach ($bossIds as $bossId) {
593
                $subject = sprintf(get_lang('User %s signed the agreement.'), $current['complete_name']);
594
                $content = sprintf(get_lang('User %s signed the agreement on %s.'), $current['complete_name'], $dateStr);
595
                MessageManager::send_message_simple($bossId, $subject, $content, $userId);
596
            }
597
        }
598
    }
599
600
    /**
601
     * Displays the Terms and Conditions page.
602
     *
603
     * @param string $returnUrl The URL to redirect back to after acceptance
604
     */
605
    public static function displayLegalTermsPage(string $returnUrl = 'index.php'): void
606
    {
607
        $iso   = api_get_language_isocode();
608
        $langId = api_get_language_id($iso);
609
        $term  = LegalManager::get_last_condition($langId)
610
            ?: LegalManager::get_last_condition(api_get_language_id(api_get_setting('language.platform_language')));
611
612
        Display::display_header(get_lang('Terms and Conditions'));
613
614
        if (!empty($term['content'])) {
615
            echo '<div class="max-w-3xl mx-auto bg-white shadow p-8 rounded">';
616
            echo '<h1 class="text-2xl font-bold text-primary mb-6">' . get_lang('Terms and Conditions') . '</h1>';
617
            echo '<div class="prose prose-sm max-w-none mb-6">' . $term['content'] . '</div>';
618
619
            $extra = new ExtraFieldValue('terms_and_condition');
620
            foreach ($extra->getAllValuesByItem($term['id']) as $field) {
621
                if (!empty($field['field_value'])) {
622
                    echo '<div class="mb-4">';
623
                    echo '<h3 class="text-lg font-semibold text-primary">' . $field['display_text'] . '</h3>';
624
                    echo '<p class="text-gray-90 mt-1">' . $field['field_value'] . '</p>';
625
                    echo '</div>';
626
                }
627
            }
628
629
            $hide = api_get_setting('registration.hide_legal_accept_checkbox') === 'true';
630
631
            echo '<form method="post" action="tc.php?return=' . urlencode($returnUrl) . '" class="space-y-6">';
632
            echo '<input type="hidden" name="legal_accept_type" value="' . $term['version'] . ':' . $term['language_id'] . '">';
633
            echo '<input type="hidden" name="return" value="' . htmlspecialchars($returnUrl) . '">';
634
635
            if (!$hide) {
636
                echo '<label class="flex items-start space-x-2">';
637
                echo '<input type="checkbox" name="legal_accept" value="1" required class="rounded border-gray-300 text-primary focus:ring-primary">';
638
                echo '<span class="text-gray-90 text-sm">' . get_lang('I have read and agree to the') . ' ';
639
                echo '<a href="tc.php?preview=1" target="_blank" class="text-primary hover:underline">' . get_lang('Terms and Conditions') . '</a>';
640
                echo '</span>';
641
                echo '</label>';
642
            }
643
644
            echo '<div><button type="submit" class="inline-block bg-primary text-white font-semibold px-6 py-3 rounded hover:opacity-90 transition">' . get_lang('Accept terms and conditions') . '</button></div>';
645
            echo '</form>';
646
            echo '</div>';
647
        } else {
648
            echo '<div class="text-center text-gray-90 text-lg">' . get_lang('Coming soon...') . '</div>';
649
        }
650
651
        Display::display_footer();
652
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
653
    }
654
}
655