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

DateTimePicker::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 3
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Framework\Container;
6
7
/**
8
 * Form element to select a date and hour.
9
 */
10
class DateTimePicker extends HTML_QuickForm_text
11
{
12
    /**
13
     * DateTimePicker constructor.
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
        parent::__construct($elementName, $elementLabel, $attributes);
26
        $this->_appendName = true;
27
    }
28
29
    /**
30
     * HTML code to display this datepicker.
31
     *
32
     * @return string
33
     */
34
    public function toHtml()
35
    {
36
        if ($this->_flagFrozen) {
37
            return $this->getFrozenHtml();
38
        }
39
40
        $id = $this->getAttribute('id');
41
        $value = $this->getValue();
42
43
        $formattedValue = '';
44
        if (!empty($value)) {
45
            $formattedValue = api_format_date($value, DATE_TIME_FORMAT_LONG_24H);
46
        }
47
48
        $label = $this->getLabel();
49
        if (is_array($label) && isset($label[0])) {
50
            $label = $label[0];
51
        }
52
53
        //$resetFieldX = sprintf(get_lang('Reset %s'), $label);
54
55
        return '<input '.$this->_getAttrString($this->_attributes).' />'.$this->getElementJS();
56
    }
57
58
    /**
59
     * @param string $value
60
     */
61
    public function setValue($value)
62
    {
63
        $value = substr($value, 0, 16);
64
        $this->updateAttributes(['value' => $value]);
65
    }
66
67
    /**
68
     * Injects the JS for initializing flatpickr with Intl-driven localization.
69
     * All comments/console messages are in English.
70
     */
71
    private function getElementJS(): string
72
    {
73
        $localeCode = $this->getLocaleCode();
74
        $id = $this->getAttribute('id');
75
76
        $baseLang = strtolower(explode('-', $localeCode)[0] ?? $localeCode);
77
        $altFormat = ($baseLang === 'en') ? 'F d, Y - H:i' : 'd F, Y - H:i';
78
79
        $js = "<script>
80
            document.addEventListener('DOMContentLoaded', function () {
81
              var input = document.getElementById('{$id}');
82
              if (!input) return;
83
84
              function cap(s){ return s ? s.charAt(0).toUpperCase() + s.slice(1) : s; }
85
86
              // Build a flatpickr locale object using Intl (no external l10n files)
87
              function buildFlatpickrLocale(loc) {
88
                try {
89
                  var fmtWeekLong  = new Intl.DateTimeFormat(loc, { weekday: 'long' });
90
                  var fmtWeekShort = new Intl.DateTimeFormat(loc, { weekday: 'short' });
91
                  var fmtMonthLong = new Intl.DateTimeFormat(loc, { month: 'long' });
92
                  var fmtMonthShort= new Intl.DateTimeFormat(loc, { month: 'short' });
93
94
                  // Weekdays: flatpickr expects 0..6 starting on Sunday
95
                  var sun = new Date(Date.UTC(2020, 0, 5)); // a Sunday
96
                  var weekdaysLong = [], weekdaysShort = [];
97
                  for (var i=0;i<7;i++){
98
                    var d = new Date(sun); d.setUTCDate(sun.getUTCDate()+i);
99
                    weekdaysLong.push(cap(fmtWeekLong.format(d)));
100
                    weekdaysShort.push(cap(fmtWeekShort.format(d)));
101
                  }
102
103
                  // Months 0..11
104
                  var monthsLong = [], monthsShort = [];
105
                  for (var m=0;m<12;m++){
106
                    var dm = new Date(Date.UTC(2020, m, 1));
107
                    monthsLong.push(cap(fmtMonthLong.format(dm)));
108
                    monthsShort.push(cap(fmtMonthShort.format(dm)));
109
                  }
110
111
                  // First day of week (fallback to Monday if not available)
112
                  var firstDay = 1; // 0=Sun, 1=Mon
113
                  try {
114
                    if (window.Intl && Intl.Locale) {
115
                      var inf = new Intl.Locale(loc);
116
                      if (inf.weekInfo && inf.weekInfo.firstDay) {
117
                        firstDay = (inf.weekInfo.firstDay === 7) ? 0 : inf.weekInfo.firstDay;
118
                      }
119
                    }
120
                  } catch(e){}
121
122
                  return {
123
                    weekdays: { shorthand: weekdaysShort, longhand: weekdaysLong },
124
                    months:   { shorthand: monthsShort,  longhand: monthsLong  },
125
                    firstDayOfWeek: firstDay,
126
                    weekAbbreviation: 'Wk',
127
                    rangeSeparator: ' – ',
128
                    time_24hr: true
129
                  };
130
                } catch(e) {
131
                  return 'en';
132
                }
133
              }
134
135
              function initialize() {
136
                try {
137
                  if (!window.flatpickr) return;
138
                  if (input._flatpickr) { input._flatpickr.destroy(); }
139
140
                  var loc = buildFlatpickrLocale('{$localeCode}');
141
142
                  // Set as global default when possible
143
                  if (typeof flatpickr.localize === 'function' && typeof loc === 'object') {
144
                    flatpickr.localize(loc);
145
                  }
146
147
                  // Also register under the key for string-based locale usage
148
                  if (flatpickr.l10ns && typeof loc === 'object') {
149
                    flatpickr.l10ns['{$localeCode}'] = loc;
150
                  }
151
152
                  var instance = flatpickr('#{$id}', {
153
                    locale: loc,
154
                    altInput: true,
155
                    altFormat: '{$altFormat}',
156
                    enableTime: true,
157
                    dateFormat: 'Y-m-d H:i',
158
                    time_24hr: true,
159
                    wrap: false,
160
                    onReady: function(selectedDates, dateStr, fp) {
161
                      const btn = document.createElement('button');
162
                      btn.textContent = '".get_lang('Validate')."';
163
                      btn.className = 'flatpickr-validate-btn';
164
                      btn.type = 'button';
165
                      btn.onclick = function(){ fp.close(); };
166
                      fp.calendarContainer.appendChild(btn);
167
168
                      try { fp.redraw(); } catch(e){}
169
                    }
170
                  });
171
172
                  // Ensure l10n is applied and redraw if needed
173
                  try {
174
                    if (instance && instance.l10n && typeof loc === 'object') {
175
                      Object.assign(instance.l10n, loc);
176
                      instance.redraw();
177
                    }
178
                  } catch(e){}
179
180
                  // Debug (optional):
181
                  // console.log('[DateTimePicker]', '{$localeCode}', instance && instance.l10n);
182
                } catch(e) {
183
                  console.error('[DateTimePicker] flatpickr init error', e);
184
                }
185
              }
186
187
              initialize();
188
            });
189
            </script>";
190
191
        return $js;
192
    }
193
194
    /**
195
     * Returns a normalized 2-letter locale to be used by JS/Intl.
196
     * Priority: course > user > platform.
197
     */
198
    private function getLocaleCode(): string
199
    {
200
        $raw = '';
201
202
        if (class_exists('\Chamilo\CoreBundle\Framework\Container')) {
203
            $req = Container::getRequest();
204
            if ($req && $req->getLocale()) {
205
                $raw = $req->getLocale();
206
            }
207
        }
208
209
        if ($raw === '' && !empty($_SESSION['_locale']) && is_string($_SESSION['_locale'])) {
210
            $raw = $_SESSION['_locale'];
211
        }
212
213
        if ($raw === '') {
214
            $raw = (string) api_get_language_isocode();
215
        }
216
217
        $s = str_replace('_', '-', trim($raw));
218
        if ($s === '') {
219
            return 'en-US';
220
        }
221
222
        if (preg_match('/^([A-Za-z]{2,3})-([A-Za-z]{2})$/', $s, $m)) {
223
            return strtolower($m[1]).'-'.strtoupper($m[2]);
224
        }
225
226
        if (preg_match('/^([A-Za-z]{2,3})$/', $s, $m)) {
227
            return strtolower($m[1]);
228
        }
229
230
        if (preg_match('/^([A-Za-z]{2,3})[-_].+$/', $s, $m)) {
231
            return strtolower($m[1]);
232
        }
233
234
        return 'en-US';
235
    }
236
}
237