174 lines
5.8 KiB
PHP
174 lines
5.8 KiB
PHP
<?php
|
|
|
|
namespace MediaWiki\HTMLForm\Field;
|
|
|
|
use DateTime;
|
|
use DateTimeZone;
|
|
use InvalidArgumentException;
|
|
use MediaWiki\Context\RequestContext;
|
|
use MediaWiki\MainConfigNames;
|
|
use MediaWiki\MediaWikiServices;
|
|
use MediaWiki\User\UserTimeCorrection;
|
|
use MediaWiki\Utils\MWTimestamp;
|
|
use Wikimedia\Message\ITextFormatter;
|
|
use Wikimedia\Message\MessageValue;
|
|
|
|
/**
|
|
* Dropdown widget that allows the user to select a timezone, either by choosing a geographic zone, by using the wiki
|
|
* default, or by manually specifying an offset. It also has an option to fill the value from the browser settings.
|
|
* The value of this field is in a format accepted by UserTimeCorrection.
|
|
*/
|
|
class HTMLTimezoneField extends HTMLSelectOrOtherField {
|
|
private const FIELD_CLASS = 'mw-htmlform-timezone-field';
|
|
|
|
/** @var ITextFormatter */
|
|
private $msgFormatter;
|
|
|
|
/**
|
|
* @stable to call
|
|
* @inheritDoc
|
|
* Note that no options should be specified.
|
|
*/
|
|
public function __construct( $params ) {
|
|
if ( isset( $params['options'] ) ) {
|
|
throw new InvalidArgumentException( "Options should not be provided to " . __CLASS__ );
|
|
}
|
|
$params['placeholder-message'] ??= 'timezone-useoffset-placeholder';
|
|
$params['options'] = [];
|
|
parent::__construct( $params );
|
|
$lang = $this->mParent ? $this->mParent->getLanguage() : RequestContext::getMain()->getLanguage();
|
|
$langCode = $lang->getCode();
|
|
$this->msgFormatter = MediaWikiServices::getInstance()->getMessageFormatterFactory()
|
|
->getTextFormatter( $langCode );
|
|
$this->mOptions = $this->getTimezoneOptions();
|
|
}
|
|
|
|
/**
|
|
* @return array<string|string[]>
|
|
*/
|
|
private function getTimezoneOptions(): array {
|
|
$opt = [];
|
|
|
|
$localTZoffset = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::LocalTZoffset );
|
|
$timeZoneList = $this->getTimeZoneList();
|
|
|
|
$timestamp = MWTimestamp::getLocalInstance();
|
|
// Check that the LocalTZoffset is the same as the local time zone offset
|
|
if ( $localTZoffset === (int)$timestamp->format( 'Z' ) / 60 ) {
|
|
$timezoneName = $timestamp->getTimezone()->getName();
|
|
// Localize timezone
|
|
if ( isset( $timeZoneList[$timezoneName] ) ) {
|
|
$timezoneName = $timeZoneList[$timezoneName]['name'];
|
|
}
|
|
$server_tz_msg = $this->msgFormatter->format(
|
|
MessageValue::new( 'timezoneuseserverdefault', [ $timezoneName ] )
|
|
);
|
|
} else {
|
|
$tzstring = UserTimeCorrection::formatTimezoneOffset( $localTZoffset );
|
|
$server_tz_msg = $this->msgFormatter->format(
|
|
MessageValue::new( 'timezoneuseserverdefault', [ $tzstring ] )
|
|
);
|
|
}
|
|
$opt[$server_tz_msg] = "System|$localTZoffset";
|
|
$opt[$this->msgFormatter->format( MessageValue::new( 'timezoneuseoffset' ) )] = 'other';
|
|
$opt[$this->msgFormatter->format( MessageValue::new( 'guesstimezone' ) )] = 'guess';
|
|
|
|
foreach ( $timeZoneList as $timeZoneInfo ) {
|
|
$region = $timeZoneInfo['region'];
|
|
if ( !isset( $opt[$region] ) ) {
|
|
$opt[$region] = [];
|
|
}
|
|
$opt[$region][$timeZoneInfo['name']] = $timeZoneInfo['timecorrection'];
|
|
}
|
|
return $opt;
|
|
}
|
|
|
|
/**
|
|
* Get a list of all time zones
|
|
* @return string[][] A list of all time zones. The system name of the time zone is used as key and
|
|
* the value is an array which contains localized name, the timecorrection value used for
|
|
* preferences and the region
|
|
*/
|
|
private function getTimeZoneList(): array {
|
|
$identifiers = DateTimeZone::listIdentifiers();
|
|
'@phan-var array|false $identifiers'; // See phan issue #3162
|
|
if ( $identifiers === false ) {
|
|
return [];
|
|
}
|
|
sort( $identifiers );
|
|
|
|
$tzRegions = [
|
|
'Africa' => $this->msgFormatter->format( MessageValue::new( 'timezoneregion-africa' ) ),
|
|
'America' => $this->msgFormatter->format( MessageValue::new( 'timezoneregion-america' ) ),
|
|
'Antarctica' => $this->msgFormatter->format( MessageValue::new( 'timezoneregion-antarctica' ) ),
|
|
'Arctic' => $this->msgFormatter->format( MessageValue::new( 'timezoneregion-arctic' ) ),
|
|
'Asia' => $this->msgFormatter->format( MessageValue::new( 'timezoneregion-asia' ) ),
|
|
'Atlantic' => $this->msgFormatter->format( MessageValue::new( 'timezoneregion-atlantic' ) ),
|
|
'Australia' => $this->msgFormatter->format( MessageValue::new( 'timezoneregion-australia' ) ),
|
|
'Europe' => $this->msgFormatter->format( MessageValue::new( 'timezoneregion-europe' ) ),
|
|
'Indian' => $this->msgFormatter->format( MessageValue::new( 'timezoneregion-indian' ) ),
|
|
'Pacific' => $this->msgFormatter->format( MessageValue::new( 'timezoneregion-pacific' ) ),
|
|
];
|
|
asort( $tzRegions );
|
|
|
|
$timeZoneList = [];
|
|
|
|
$now = new DateTime();
|
|
|
|
foreach ( $identifiers as $identifier ) {
|
|
$parts = explode( '/', $identifier, 2 );
|
|
|
|
// DateTimeZone::listIdentifiers() returns a number of
|
|
// backwards-compatibility entries. This filters them out of the
|
|
// list presented to the user.
|
|
if ( count( $parts ) !== 2 || !array_key_exists( $parts[0], $tzRegions ) ) {
|
|
continue;
|
|
}
|
|
|
|
// Localize region
|
|
$parts[0] = $tzRegions[$parts[0]];
|
|
|
|
$dateTimeZone = new DateTimeZone( $identifier );
|
|
$minDiff = floor( $dateTimeZone->getOffset( $now ) / 60 );
|
|
|
|
$display = str_replace( '_', ' ', $parts[0] . '/' . $parts[1] );
|
|
$value = "ZoneInfo|$minDiff|$identifier";
|
|
|
|
$timeZoneList[$identifier] = [
|
|
'name' => $display,
|
|
'timecorrection' => $value,
|
|
'region' => $parts[0],
|
|
];
|
|
}
|
|
|
|
return $timeZoneList;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function validate( $value, $alldata ) {
|
|
$p = parent::validate( $value, $alldata );
|
|
if ( $p !== true ) {
|
|
return $p;
|
|
}
|
|
|
|
if ( !( new UserTimeCorrection( $value ) )->isValid() ) {
|
|
return $this->mParent->msg( 'timezone-invalid' )->escaped();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
protected function getFieldClasses(): array {
|
|
$classes = parent::getFieldClasses();
|
|
$classes[] = self::FIELD_CLASS;
|
|
return $classes;
|
|
}
|
|
}
|
|
|
|
/** @deprecated class alias since 1.42 */
|
|
class_alias( HTMLTimezoneField::class, 'HTMLTimezoneField' );
|