Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 82 |
|
0.00% |
0 / 9 |
CRAP | |
0.00% |
0 / 1 |
HTMLDateTimeField | |
0.00% |
0 / 81 |
|
0.00% |
0 / 9 |
1482 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
getAttributes | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
56 | |||
loadDataFromRequest | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
validate | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
110 | |||
parseDate | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
42 | |||
formatDate | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
20 | |||
getInputOOUI | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
6 | |||
getOOUIModules | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
shouldInfuseOOUI | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\HTMLForm\Field; |
4 | |
5 | use DateTime; |
6 | use DateTimeZone; |
7 | use Exception; |
8 | use InvalidArgumentException; |
9 | use Wikimedia\RequestTimeout\TimeoutException; |
10 | |
11 | /** |
12 | * A field that will contain a date and/or time |
13 | * |
14 | * Currently recognizes only {YYYY}-{MM}-{DD}T{HH}:{MM}:{SS.S*}Z formatted dates. |
15 | * |
16 | * Besides the parameters recognized by HTMLTextField, additional recognized |
17 | * parameters in the field descriptor array include: |
18 | * type - 'date', 'time', or 'datetime' |
19 | * min - The minimum date to allow, in any recognized format. |
20 | * max - The maximum date to allow, in any recognized format. |
21 | * placeholder - The default comes from the htmlform-(date|time|datetime)-placeholder message. |
22 | * |
23 | * The result is a formatted date. |
24 | * |
25 | * @stable to extend |
26 | * @note This widget is not likely to work well in non-OOUI forms. |
27 | */ |
28 | class HTMLDateTimeField extends HTMLTextField { |
29 | /** @var string[] */ |
30 | protected static $patterns = [ |
31 | 'date' => '[0-9]{4}-[01][0-9]-[0-3][0-9]', |
32 | 'time' => '[0-2][0-9]:[0-5][0-9]:[0-5][0-9](?:\.[0-9]+)?', |
33 | 'datetime' => '[0-9]{4}-[01][0-9]-[0-3][0-9][T ][0-2][0-9]:[0-5][0-9]:[0-5][0-9](?:\.[0-9]+)?Z?', |
34 | ]; |
35 | |
36 | /** @var string */ |
37 | protected $mType = 'datetime'; |
38 | |
39 | /** |
40 | * @stable to call |
41 | * @inheritDoc |
42 | */ |
43 | public function __construct( $params ) { |
44 | parent::__construct( $params ); |
45 | |
46 | $this->mType = $params['type'] ?? 'datetime'; |
47 | |
48 | if ( !in_array( $this->mType, [ 'date', 'time', 'datetime' ] ) ) { |
49 | throw new InvalidArgumentException( "Invalid type '$this->mType'" ); |
50 | } |
51 | |
52 | if ( $this->mPlaceholder === '' ) { |
53 | // Messages: htmlform-date-placeholder htmlform-time-placeholder htmlform-datetime-placeholder |
54 | $this->mPlaceholder = $this->msg( "htmlform-{$this->mType}-placeholder" )->text(); |
55 | } |
56 | |
57 | $this->mClass .= ' mw-htmlform-datetime-field'; |
58 | } |
59 | |
60 | public function getAttributes( array $list ) { |
61 | $parentList = array_diff( $list, [ 'min', 'max' ] ); |
62 | $ret = parent::getAttributes( $parentList ); |
63 | |
64 | if ( in_array( 'min', $list ) && isset( $this->mParams['min'] ) ) { |
65 | $min = $this->parseDate( $this->mParams['min'] ); |
66 | if ( $min ) { |
67 | $ret['min'] = $this->formatDate( $min ); |
68 | } |
69 | } |
70 | if ( in_array( 'max', $list ) && isset( $this->mParams['max'] ) ) { |
71 | $max = $this->parseDate( $this->mParams['max'] ); |
72 | if ( $max ) { |
73 | $ret['max'] = $this->formatDate( $max ); |
74 | } |
75 | } |
76 | |
77 | $ret['step'] = 1; |
78 | |
79 | $ret['type'] = $this->mType; |
80 | $ret['pattern'] = static::$patterns[$this->mType]; |
81 | |
82 | return $ret; |
83 | } |
84 | |
85 | public function loadDataFromRequest( $request ) { |
86 | if ( !$request->getCheck( $this->mName ) ) { |
87 | return $this->getDefault(); |
88 | } |
89 | |
90 | $value = $request->getText( $this->mName ); |
91 | $date = $this->parseDate( $value ); |
92 | return $date ? $this->formatDate( $date ) : $value; |
93 | } |
94 | |
95 | public function validate( $value, $alldata ) { |
96 | $p = parent::validate( $value, $alldata ); |
97 | |
98 | if ( $p !== true ) { |
99 | return $p; |
100 | } |
101 | |
102 | if ( $value === '' ) { |
103 | // required was already checked by parent::validate |
104 | return true; |
105 | } |
106 | |
107 | $date = $this->parseDate( $value ); |
108 | if ( !$date ) { |
109 | // Messages: htmlform-date-invalid htmlform-time-invalid htmlform-datetime-invalid |
110 | return $this->msg( "htmlform-{$this->mType}-invalid" ); |
111 | } |
112 | |
113 | if ( isset( $this->mParams['min'] ) ) { |
114 | $min = $this->parseDate( $this->mParams['min'] ); |
115 | if ( $min && $date < $min ) { |
116 | // Messages: htmlform-date-toolow htmlform-time-toolow htmlform-datetime-toolow |
117 | return $this->msg( "htmlform-{$this->mType}-toolow", $this->formatDate( $min ) ); |
118 | } |
119 | } |
120 | |
121 | if ( isset( $this->mParams['max'] ) ) { |
122 | $max = $this->parseDate( $this->mParams['max'] ); |
123 | if ( $max && $date > $max ) { |
124 | // Messages: htmlform-date-toohigh htmlform-time-toohigh htmlform-datetime-toohigh |
125 | return $this->msg( "htmlform-{$this->mType}-toohigh", $this->formatDate( $max ) ); |
126 | } |
127 | } |
128 | |
129 | return true; |
130 | } |
131 | |
132 | protected function parseDate( $value ) { |
133 | $value = trim( $value ?? '' ); |
134 | if ( $value === '' ) { |
135 | return false; |
136 | } |
137 | |
138 | if ( $this->mType === 'date' ) { |
139 | $value .= ' T00:00:00+0000'; |
140 | } |
141 | if ( $this->mType === 'time' ) { |
142 | $value = '1970-01-01 ' . $value . '+0000'; |
143 | } |
144 | |
145 | try { |
146 | $date = new DateTime( $value, new DateTimeZone( 'GMT' ) ); |
147 | return $date->getTimestamp(); |
148 | } catch ( TimeoutException $e ) { |
149 | throw $e; |
150 | } catch ( Exception $ex ) { |
151 | return false; |
152 | } |
153 | } |
154 | |
155 | protected function formatDate( $value ) { |
156 | switch ( $this->mType ) { |
157 | case 'date': |
158 | return gmdate( 'Y-m-d', $value ); |
159 | |
160 | case 'time': |
161 | return gmdate( 'H:i:s', $value ); |
162 | |
163 | case 'datetime': |
164 | return gmdate( 'Y-m-d\\TH:i:s\\Z', $value ); |
165 | } |
166 | } |
167 | |
168 | public function getInputOOUI( $value ) { |
169 | $params = [ |
170 | 'type' => $this->mType, |
171 | 'value' => $value, |
172 | 'name' => $this->mName, |
173 | 'id' => $this->mID, |
174 | ]; |
175 | |
176 | $params += \OOUI\Element::configFromHtmlAttributes( |
177 | $this->getAttributes( [ 'disabled', 'readonly', 'min', 'max' ] ) |
178 | ); |
179 | |
180 | if ( $this->mType === 'date' ) { |
181 | $this->mParent->getOutput()->addModuleStyles( 'mediawiki.widgets.DateInputWidget.styles' ); |
182 | return new \MediaWiki\Widget\DateInputWidget( $params ); |
183 | } else { |
184 | $this->mParent->getOutput()->addModuleStyles( 'mediawiki.widgets.DateTimeInputWidget.styles' ); |
185 | return new \MediaWiki\Widget\DateTimeInputWidget( $params ); |
186 | } |
187 | } |
188 | |
189 | protected function getOOUIModules() { |
190 | if ( $this->mType === 'date' ) { |
191 | return [ 'mediawiki.widgets.DateInputWidget' ]; |
192 | } else { |
193 | return [ 'mediawiki.widgets.datetime' ]; |
194 | } |
195 | } |
196 | |
197 | protected function shouldInfuseOOUI() { |
198 | return true; |
199 | } |
200 | |
201 | } |
202 | |
203 | /** @deprecated class alias since 1.42 */ |
204 | class_alias( HTMLDateTimeField::class, 'HTMLDateTimeField' ); |