Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 81 |
|
0.00% |
0 / 9 |
CRAP | |
0.00% |
0 / 1 |
HTMLDateTimeField | |
0.00% |
0 / 80 |
|
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 / 13 |
|
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 | protected static $patterns = [ |
30 | 'date' => '[0-9]{4}-[01][0-9]-[0-3][0-9]', |
31 | 'time' => '[0-2][0-9]:[0-5][0-9]:[0-5][0-9](?:\.[0-9]+)?', |
32 | '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?', |
33 | ]; |
34 | |
35 | protected $mType = 'datetime'; |
36 | |
37 | /** |
38 | * @stable to call |
39 | * @inheritDoc |
40 | */ |
41 | public function __construct( $params ) { |
42 | parent::__construct( $params ); |
43 | |
44 | $this->mType = $params['type'] ?? 'datetime'; |
45 | |
46 | if ( !in_array( $this->mType, [ 'date', 'time', 'datetime' ] ) ) { |
47 | throw new InvalidArgumentException( "Invalid type '$this->mType'" ); |
48 | } |
49 | |
50 | if ( $this->mPlaceholder === '' ) { |
51 | // Messages: htmlform-date-placeholder htmlform-time-placeholder htmlform-datetime-placeholder |
52 | $this->mPlaceholder = $this->msg( "htmlform-{$this->mType}-placeholder" )->text(); |
53 | } |
54 | |
55 | $this->mClass .= ' mw-htmlform-datetime-field'; |
56 | } |
57 | |
58 | public function getAttributes( array $list ) { |
59 | $parentList = array_diff( $list, [ 'min', 'max' ] ); |
60 | $ret = parent::getAttributes( $parentList ); |
61 | |
62 | if ( in_array( 'min', $list ) && isset( $this->mParams['min'] ) ) { |
63 | $min = $this->parseDate( $this->mParams['min'] ); |
64 | if ( $min ) { |
65 | $ret['min'] = $this->formatDate( $min ); |
66 | } |
67 | } |
68 | if ( in_array( 'max', $list ) && isset( $this->mParams['max'] ) ) { |
69 | $max = $this->parseDate( $this->mParams['max'] ); |
70 | if ( $max ) { |
71 | $ret['max'] = $this->formatDate( $max ); |
72 | } |
73 | } |
74 | |
75 | $ret['step'] = 1; |
76 | |
77 | $ret['type'] = $this->mType; |
78 | $ret['pattern'] = static::$patterns[$this->mType]; |
79 | |
80 | return $ret; |
81 | } |
82 | |
83 | public function loadDataFromRequest( $request ) { |
84 | if ( !$request->getCheck( $this->mName ) ) { |
85 | return $this->getDefault(); |
86 | } |
87 | |
88 | $value = $request->getText( $this->mName ); |
89 | $date = $this->parseDate( $value ); |
90 | return $date ? $this->formatDate( $date ) : $value; |
91 | } |
92 | |
93 | public function validate( $value, $alldata ) { |
94 | $p = parent::validate( $value, $alldata ); |
95 | |
96 | if ( $p !== true ) { |
97 | return $p; |
98 | } |
99 | |
100 | if ( $value === '' ) { |
101 | // required was already checked by parent::validate |
102 | return true; |
103 | } |
104 | |
105 | $date = $this->parseDate( $value ); |
106 | if ( !$date ) { |
107 | // Messages: htmlform-date-invalid htmlform-time-invalid htmlform-datetime-invalid |
108 | return $this->msg( "htmlform-{$this->mType}-invalid" ); |
109 | } |
110 | |
111 | if ( isset( $this->mParams['min'] ) ) { |
112 | $min = $this->parseDate( $this->mParams['min'] ); |
113 | if ( $min && $date < $min ) { |
114 | // Messages: htmlform-date-toolow htmlform-time-toolow htmlform-datetime-toolow |
115 | return $this->msg( "htmlform-{$this->mType}-toolow", $this->formatDate( $min ) ); |
116 | } |
117 | } |
118 | |
119 | if ( isset( $this->mParams['max'] ) ) { |
120 | $max = $this->parseDate( $this->mParams['max'] ); |
121 | if ( $max && $date > $max ) { |
122 | // Messages: htmlform-date-toohigh htmlform-time-toohigh htmlform-datetime-toohigh |
123 | return $this->msg( "htmlform-{$this->mType}-toohigh", $this->formatDate( $max ) ); |
124 | } |
125 | } |
126 | |
127 | return true; |
128 | } |
129 | |
130 | protected function parseDate( $value ) { |
131 | $value = trim( $value ?? '' ); |
132 | if ( $value === '' ) { |
133 | return false; |
134 | } |
135 | |
136 | if ( $this->mType === 'date' ) { |
137 | $value .= ' T00:00:00+0000'; |
138 | } |
139 | if ( $this->mType === 'time' ) { |
140 | $value = '1970-01-01 ' . $value . '+0000'; |
141 | } |
142 | |
143 | try { |
144 | $date = new DateTime( $value, new DateTimeZone( 'GMT' ) ); |
145 | return $date->getTimestamp(); |
146 | } catch ( TimeoutException $e ) { |
147 | throw $e; |
148 | } catch ( Exception $ex ) { |
149 | return false; |
150 | } |
151 | } |
152 | |
153 | protected function formatDate( $value ) { |
154 | switch ( $this->mType ) { |
155 | case 'date': |
156 | return gmdate( 'Y-m-d', $value ); |
157 | |
158 | case 'time': |
159 | return gmdate( 'H:i:s', $value ); |
160 | |
161 | case 'datetime': |
162 | return gmdate( 'Y-m-d\\TH:i:s\\Z', $value ); |
163 | } |
164 | } |
165 | |
166 | public function getInputOOUI( $value ) { |
167 | $params = [ |
168 | 'type' => $this->mType, |
169 | 'value' => $value, |
170 | 'name' => $this->mName, |
171 | 'id' => $this->mID, |
172 | ]; |
173 | |
174 | $params += \OOUI\Element::configFromHtmlAttributes( |
175 | $this->getAttributes( [ 'disabled', 'readonly', 'min', 'max' ] ) |
176 | ); |
177 | |
178 | if ( $this->mType === 'date' ) { |
179 | $this->mParent->getOutput()->addModuleStyles( 'mediawiki.widgets.DateInputWidget.styles' ); |
180 | return new \MediaWiki\Widget\DateInputWidget( $params ); |
181 | } else { |
182 | return new \MediaWiki\Widget\DateTimeInputWidget( $params ); |
183 | } |
184 | } |
185 | |
186 | protected function getOOUIModules() { |
187 | if ( $this->mType === 'date' ) { |
188 | return [ 'mediawiki.widgets.DateInputWidget' ]; |
189 | } else { |
190 | return [ 'mediawiki.widgets.datetime' ]; |
191 | } |
192 | } |
193 | |
194 | protected function shouldInfuseOOUI() { |
195 | return true; |
196 | } |
197 | |
198 | } |
199 | |
200 | /** @deprecated class alias since 1.42 */ |
201 | class_alias( HTMLDateTimeField::class, 'HTMLDateTimeField' ); |