Passed
Push — master ( 01df40...1206ca )
by
unknown
10:07 queued 10s
created

DatePicker::toHtml()   B

Complexity

Conditions 8
Paths 17

Size

Total Lines 42
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 23
nc 17
nop 0
dl 0
loc 42
rs 8.4444
c 0
b 0
f 0
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Enums\ActionIcon;
6
use Chamilo\CoreBundle\Enums\ObjectIcon;
7
use Chamilo\CoreBundle\Framework\Container;
8
9
/**
10
 * Form element to select a date.
11
 */
12
class DatePicker extends HTML_QuickForm_text
13
{
14
    /**
15
     * @param string       $elementName
16
     * @param string|array $elementLabel
17
     * @param array        $attributes
18
     */
19
    public function __construct($elementName, $elementLabel = null, $attributes = null)
20
    {
21
        if (!isset($attributes['id'])) {
22
            $attributes['id'] = $elementName;
23
        }
24
        $attributes['class'] = 'p-component p-inputtext p-filled';
25
26
        parent::__construct($elementName, $elementLabel, $attributes);
27
        $this->_appendName = true;
28
    }
29
30
    /**
31
     * HTML code to display this datepicker.
32
     *
33
     * @return string
34
     */
35
    public function toHtml(): string
36
    {
37
        if ($this->_flagFrozen) {
38
            return $this->getFrozenHtml();
39
        }
40
41
        $id = $this->getAttribute('id');
42
        $value = $this->getValue();
43
44
        if (!empty($value)) {
45
            $value = api_format_date($value, DATE_FORMAT_LONG_NO_DAY);
46
        }
47
48
        $label = $this->getLabel();
49
        $settingRequiredFields = api_get_setting('registration.required_extra_fields_in_inscription', true);
50
        $requiredFields = 'false' !== $settingRequiredFields ? $settingRequiredFields : [];
51
52
        if (!empty($requiredFields) && $requiredFields['options']) {
53
            $requiredFields = $requiredFields['options'];
54
        }
55
        $variable = str_replace('extra_', '',$id);
56
        $requiredSymbol = '';
57
        if (!empty($requiredFields) && in_array($variable, $requiredFields)) {
58
            $requiredSymbol = '<span class="form_required">*</span>';
59
        }
60
61
        $this->setAttribute('placeholder', get_lang('Select date'));
62
63
        return '
64
        <label>'.$requiredSymbol.$label.'</label>
65
        <div id="'.$id.'_container" class="flex items-center mt-1 flatpickr-wrapper" data-wrap="true">
66
            <input '.$this->_getAttrString($this->_attributes).' value="'.$value.'" data-input>
67
            <div class="flex space-x-1 ml-2" id="button-addon3">
68
                <button class="btn btn--secondary-outline mr-2" type="button" data-toggle>
69
                  '.Display::getMdiIcon(ObjectIcon::AGENDA).'
70
                </button>
71
                <button class="btn btn--secondary-outline" type="button" data-clear>
72
                  '.Display::getMdiIcon(ActionIcon::CLOSE).'
73
                </button>
74
            </div>
75
        </div>
76
    '.$this->getElementJS();
77
    }
78
79
    /**
80
     * @param string $value
81
     */
82
    public function setValue($value)
83
    {
84
        if (empty($value)) {
85
            return;
86
        }
87
88
        $value = substr($value, 0, 16);
89
        $this->updateAttributes(
90
            [
91
                'value' => $value,
92
            ]
93
        );
94
    }
95
96
    /**
97
     * Get the necessary javascript for this datepicker.
98
     */
99
    private function getElementJS(): string
100
    {
101
        $localeCode = $this->getLocaleCode();
102
        $id = $this->getAttribute('id');
103
104
        $baseLang = strtolower(explode('-', $localeCode)[0] ?? $localeCode);
105
        $altFormat = ($baseLang === 'en') ? 'F d, Y' : 'd F, Y';
106
107
        return "<script>
108
            window.addEventListener('load', function () {
109
              var container = document.getElementById('{$id}_container');
110
              if (!container) return;
111
112
              function cap(s){ return s ? s.charAt(0).toUpperCase() + s.slice(1) : s; }
113
114
              // Build a flatpickr locale object using Intl (no external l10n files)
115
              function buildFlatpickrLocale(loc) {
116
                try {
117
                  var fmtWeekLong  = new Intl.DateTimeFormat(loc, { weekday: 'long' });
118
                  var fmtWeekShort = new Intl.DateTimeFormat(loc, { weekday: 'short' });
119
                  var fmtMonthLong = new Intl.DateTimeFormat(loc, { month: 'long' });
120
                  var fmtMonthShort= new Intl.DateTimeFormat(loc, { month: 'short' });
121
122
                  // Weekdays 0..6 starting on Sunday (as flatpickr expects)
123
                  var sun = new Date(Date.UTC(2020, 0, 5));
124
                  var weekdaysLong = [], weekdaysShort = [];
125
                  for (var i=0;i<7;i++){
126
                    var d = new Date(sun); d.setUTCDate(sun.getUTCDate()+i);
127
                    weekdaysLong.push(cap(fmtWeekLong.format(d)));
128
                    weekdaysShort.push(cap(fmtWeekShort.format(d)));
129
                  }
130
131
                  // Months 0..11
132
                  var monthsLong = [], monthsShort = [];
133
                  for (var m=0;m<12;m++){
134
                    var dm = new Date(Date.UTC(2020, m, 1));
135
                    monthsLong.push(cap(fmtMonthLong.format(dm)));
136
                    monthsShort.push(cap(fmtMonthShort.format(dm)));
137
                  }
138
139
                  // First day of week (fallback to Monday)
140
                  var firstDay = 1;
141
                  try {
142
                    if (window.Intl && Intl.Locale) {
143
                      var inf = new Intl.Locale(loc);
144
                      if (inf.weekInfo && inf.weekInfo.firstDay) {
145
                        firstDay = (inf.weekInfo.firstDay === 7) ? 0 : inf.weekInfo.firstDay; // 0=Sun
146
                      }
147
                    }
148
                  } catch(e){}
149
150
                  return {
151
                    weekdays: { shorthand: weekdaysShort, longhand: weekdaysLong },
152
                    months:   { shorthand: monthsShort,  longhand: monthsLong  },
153
                    firstDayOfWeek: firstDay,
154
                    weekAbbreviation: 'Wk',
155
                    rangeSeparator: ' \u2013 ',
156
                    time_24hr: true
157
                  };
158
                } catch(e) {
159
                  return 'en';
160
                }
161
              }
162
163
              function initialize() {
164
                try {
165
                  if (!window.flatpickr) return;
166
167
                  // If already initialized, destroy before re-init (in case something set EN earlier)
168
                  var input = container.querySelector('[data-input]');
169
                  if (input && input._flatpickr) { input._flatpickr.destroy(); }
170
171
                  var loc = buildFlatpickrLocale('{$localeCode}');
172
173
                  // Set as global default when possible
174
                  if (typeof flatpickr.localize === 'function' && typeof loc === 'object') {
175
                    flatpickr.localize(loc);
176
                  }
177
                  if (flatpickr.l10ns && typeof loc === 'object') {
178
                    flatpickr.l10ns['{$localeCode}'] = loc;
179
                  }
180
181
                  var instance = flatpickr('#{$id}_container', {
182
                    locale: loc,
183
                    altInput: true,
184
                    altFormat: '{$altFormat}',
185
                    enableTime: false,
186
                    dateFormat: 'Y-m-d',
187
                    time_24hr: true,
188
                    wrap: true
189
                  });
190
191
                  try {
192
                    if (instance && instance.l10n && typeof loc === 'object') {
193
                      Object.assign(instance.l10n, loc);
194
                      instance.redraw();
195
                    }
196
                  } catch(e){}
197
                } catch(e) {
198
                  console.error('[DatePicker] flatpickr init error', e);
199
                }
200
              }
201
202
              initialize();
203
204
              // Hide original label if present (kept from your original code)
205
              try {
206
                var lbl = document.querySelector('label[for=\"{$id}\"]');
207
                if (lbl) { lbl.style.display = 'none'; lbl.classList.add('datepicker-label'); }
208
              } catch(e){}
209
            });
210
            </script>";
211
    }
212
213
    /**
214
     * Returns a normalized 2-letter locale to be used by JS/Intl.
215
     * Priority: course > user > platform.
216
     */
217
    private function getLocaleCode(): string
218
    {
219
        $raw = '';
220
221
        if (class_exists('\Chamilo\CoreBundle\Framework\Container')) {
222
            $req = Container::getRequest();
223
            if ($req && $req->getLocale()) {
224
                $raw = $req->getLocale();
225
            }
226
        }
227
228
        if ($raw === '' && !empty($_SESSION['_locale']) && is_string($_SESSION['_locale'])) {
229
            $raw = $_SESSION['_locale'];
230
        }
231
232
        if ($raw === '') {
233
            $raw = (string) api_get_language_isocode();
234
        }
235
236
        $s = str_replace('_', '-', trim($raw));
237
        if ($s === '') {
238
            return 'en-US';
239
        }
240
241
        if (preg_match('/^([A-Za-z]{2,3})-([A-Za-z]{2})$/', $s, $m)) {
242
            return strtolower($m[1]).'-'.strtoupper($m[2]);
243
        }
244
245
        if (preg_match('/^([A-Za-z]{2,3})$/', $s, $m)) {
246
            return strtolower($m[1]);
247
        }
248
249
        if (preg_match('/^([A-Za-z]{2,3})[-_].+$/', $s, $m)) {
250
            return strtolower($m[1]);
251
        }
252
253
        return 'en-US';
254
    }
255
}
256