Don't catch and discard exceptions from the RequestTimeout library, except when the exception is properly handled and the code seems to be trying to wrap things up. In most cases the exception is rethrown. Ideally it should instead be done by narrowing the catch, and this was feasible in a few cases. But sometimes the exception being caught is an instance of the base class (notably DateTime::__construct()). Often Exception is the root of the hierarchy of exceptions being thrown and so is the obvious catch-all. Notes on specific callers: * In the case of ResourceLoader::respond(), exceptions were caught for API correctness, but processing continued. I added an outer try block for timeout handling so that termination would be more prompt. * In LCStoreCDB the Exception being caught was Cdb\Exception not \Exception. I added an alias to avoid confusion. * In ImageGallery I added a special exception class. * In Message::__toString() the rationale for catching disappears in PHP 7.4.0+, so I added a PHP version check. * In PoolCounterRedis, let the shutdown function do its thing, but rethrow the exception for logging. Change-Id: I4c3770b9efc76a1ce42ed9f59329c36de04d657c
192 lines
5.1 KiB
PHP
192 lines
5.1 KiB
PHP
<?php
|
|
|
|
use Wikimedia\RequestTimeout\TimeoutException;
|
|
|
|
/**
|
|
* A field that will contain a date and/or time
|
|
*
|
|
* Currently recognizes only {YYYY}-{MM}-{DD}T{HH}:{MM}:{SS.S*}Z formatted dates.
|
|
*
|
|
* Besides the parameters recognized by HTMLTextField, additional recognized
|
|
* parameters in the field descriptor array include:
|
|
* type - 'date', 'time', or 'datetime'
|
|
* min - The minimum date to allow, in any recognized format.
|
|
* max - The maximum date to allow, in any recognized format.
|
|
* placeholder - The default comes from the htmlform-(date|time|datetime)-placeholder message.
|
|
*
|
|
* The result is a formatted date.
|
|
*
|
|
* @stable to extend
|
|
* @note This widget is not likely to work well in non-OOUI forms.
|
|
*/
|
|
class HTMLDateTimeField extends HTMLTextField {
|
|
protected static $patterns = [
|
|
'date' => '[0-9]{4}-[01][0-9]-[0-3][0-9]',
|
|
'time' => '[0-2][0-9]:[0-5][0-9]:[0-5][0-9](?:\.[0-9]+)?',
|
|
'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?',
|
|
];
|
|
|
|
protected $mType = 'datetime';
|
|
|
|
/**
|
|
* @stable to call
|
|
* @inheritDoc
|
|
*/
|
|
public function __construct( $params ) {
|
|
parent::__construct( $params );
|
|
|
|
$this->mType = $params['type'] ?? 'datetime';
|
|
|
|
if ( !in_array( $this->mType, [ 'date', 'time', 'datetime' ] ) ) {
|
|
throw new InvalidArgumentException( "Invalid type '$this->mType'" );
|
|
}
|
|
|
|
if ( $this->mPlaceholder === '' ) {
|
|
// Messages: htmlform-date-placeholder htmlform-time-placeholder htmlform-datetime-placeholder
|
|
$this->mPlaceholder = $this->msg( "htmlform-{$this->mType}-placeholder" )->text();
|
|
}
|
|
|
|
$this->mClass .= ' mw-htmlform-datetime-field';
|
|
}
|
|
|
|
public function getAttributes( array $list ) {
|
|
$parentList = array_diff( $list, [ 'min', 'max' ] );
|
|
$ret = parent::getAttributes( $parentList );
|
|
|
|
if ( in_array( 'min', $list ) && isset( $this->mParams['min'] ) ) {
|
|
$min = $this->parseDate( $this->mParams['min'] );
|
|
if ( $min ) {
|
|
$ret['min'] = $this->formatDate( $min );
|
|
}
|
|
}
|
|
if ( in_array( 'max', $list ) && isset( $this->mParams['max'] ) ) {
|
|
$max = $this->parseDate( $this->mParams['max'] );
|
|
if ( $max ) {
|
|
$ret['max'] = $this->formatDate( $max );
|
|
}
|
|
}
|
|
|
|
$ret['step'] = 1;
|
|
|
|
$ret['type'] = $this->mType;
|
|
$ret['pattern'] = static::$patterns[$this->mType];
|
|
|
|
return $ret;
|
|
}
|
|
|
|
public function loadDataFromRequest( $request ) {
|
|
if ( !$request->getCheck( $this->mName ) ) {
|
|
return $this->getDefault();
|
|
}
|
|
|
|
$value = $request->getText( $this->mName );
|
|
$date = $this->parseDate( $value );
|
|
return $date ? $this->formatDate( $date ) : $value;
|
|
}
|
|
|
|
public function validate( $value, $alldata ) {
|
|
$p = parent::validate( $value, $alldata );
|
|
|
|
if ( $p !== true ) {
|
|
return $p;
|
|
}
|
|
|
|
if ( $value === '' ) {
|
|
// required was already checked by parent::validate
|
|
return true;
|
|
}
|
|
|
|
$date = $this->parseDate( $value );
|
|
if ( !$date ) {
|
|
// Messages: htmlform-date-invalid htmlform-time-invalid htmlform-datetime-invalid
|
|
return $this->msg( "htmlform-{$this->mType}-invalid" );
|
|
}
|
|
|
|
if ( isset( $this->mParams['min'] ) ) {
|
|
$min = $this->parseDate( $this->mParams['min'] );
|
|
if ( $min && $date < $min ) {
|
|
// Messages: htmlform-date-toolow htmlform-time-toolow htmlform-datetime-toolow
|
|
return $this->msg( "htmlform-{$this->mType}-toolow", $this->formatDate( $min ) );
|
|
}
|
|
}
|
|
|
|
if ( isset( $this->mParams['max'] ) ) {
|
|
$max = $this->parseDate( $this->mParams['max'] );
|
|
if ( $max && $date > $max ) {
|
|
// Messages: htmlform-date-toohigh htmlform-time-toohigh htmlform-datetime-toohigh
|
|
return $this->msg( "htmlform-{$this->mType}-toohigh", $this->formatDate( $max ) );
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
protected function parseDate( $value ) {
|
|
$value = trim( $value );
|
|
if ( $value === '' ) {
|
|
return false;
|
|
}
|
|
|
|
if ( $this->mType === 'date' ) {
|
|
$value .= ' T00:00:00+0000';
|
|
}
|
|
if ( $this->mType === 'time' ) {
|
|
$value = '1970-01-01 ' . $value . '+0000';
|
|
}
|
|
|
|
try {
|
|
$date = new DateTime( $value, new DateTimeZone( 'GMT' ) );
|
|
return $date->getTimestamp();
|
|
} catch ( TimeoutException $e ) {
|
|
throw $e;
|
|
} catch ( Exception $ex ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
protected function formatDate( $value ) {
|
|
switch ( $this->mType ) {
|
|
case 'date':
|
|
return gmdate( 'Y-m-d', $value );
|
|
|
|
case 'time':
|
|
return gmdate( 'H:i:s', $value );
|
|
|
|
case 'datetime':
|
|
return gmdate( 'Y-m-d\\TH:i:s\\Z', $value );
|
|
}
|
|
}
|
|
|
|
public function getInputOOUI( $value ) {
|
|
$params = [
|
|
'type' => $this->mType,
|
|
'value' => $value,
|
|
'name' => $this->mName,
|
|
'id' => $this->mID,
|
|
];
|
|
|
|
$params += OOUI\Element::configFromHtmlAttributes(
|
|
$this->getAttributes( [ 'disabled', 'readonly', 'min', 'max' ] )
|
|
);
|
|
|
|
if ( $this->mType === 'date' ) {
|
|
$this->mParent->getOutput()->addModuleStyles( 'mediawiki.widgets.DateInputWidget.styles' );
|
|
return new MediaWiki\Widget\DateInputWidget( $params );
|
|
} else {
|
|
return new MediaWiki\Widget\DateTimeInputWidget( $params );
|
|
}
|
|
}
|
|
|
|
protected function getOOUIModules() {
|
|
if ( $this->mType === 'date' ) {
|
|
return [ 'mediawiki.widgets.DateInputWidget' ];
|
|
} else {
|
|
return [ 'mediawiki.widgets.datetime' ];
|
|
}
|
|
}
|
|
|
|
protected function shouldInfuseOOUI() {
|
|
return true;
|
|
}
|
|
|
|
}
|