mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge pull request #6766 from open-sausages/pulls/4.0/6626-remove-jquery-datepicker
HTML5 Date Fields
This commit is contained in:
commit
32578e07d6
@ -4,7 +4,11 @@ summary: How to format and use the DateField class.
|
||||
# DateField
|
||||
|
||||
This `FormField` subclass lets you display an editable date, in a single text input field.
|
||||
It also provides a calendar date picker.
|
||||
It implements the [HTML5 input date type](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date)
|
||||
(with `type=date`). In supported browsers, this will cause a localised date picker to appear for users.
|
||||
HTML5 date fields present and save ISO 8601 date formats (`y-MM-dd`),
|
||||
since the browser takes care of converting to/from a localised presentation.
|
||||
Browsers without support receive an `<input type=text>` based polyfill.
|
||||
|
||||
The following example will add a simple DateField to your Page, allowing you to enter a date manually.
|
||||
|
||||
@ -34,65 +38,28 @@ The following example will add a simple DateField to your Page, allowing you to
|
||||
## Custom Date Format
|
||||
|
||||
A custom date format for a [api:DateField] can be provided through `setDateFormat`.
|
||||
This is only necessary if you want to opt-out of the built-in browser localisation via `type=date`.
|
||||
|
||||
:::php
|
||||
// will display a date in the following format: 31-06-2012
|
||||
DateField::create('MyDate')->setDateFormat('dd-MM-yyyy');
|
||||
// will display a date in the following format: 31/06/2012
|
||||
DateField::create('MyDate')
|
||||
->setHTML5(false)
|
||||
->setDateFormat('dd/MM/yyyy');
|
||||
|
||||
<div class="info" markdown="1">
|
||||
The formats are based on [CLDR format](http://userguide.icu-project.org/formatparse/datetime).
|
||||
The formats are based on [ICU format](http://www.icu-project.org/apiref/icu4c/classSimpleDateFormat.html#details).
|
||||
</div>
|
||||
|
||||
|
||||
## Min and Max Dates
|
||||
|
||||
Sets the minimum and maximum allowed date values using the `min` and `max` configuration settings (in ISO format or
|
||||
strtotime()).
|
||||
`strtotime()`).
|
||||
|
||||
:::php
|
||||
DateField::create('MyDate')
|
||||
->setMinDate('-7 days')
|
||||
->setMaxDate'2012-12-31')
|
||||
|
||||
## Separate Day / Month / Year Fields
|
||||
|
||||
To display separate input fields for day, month and year separately you can use the `SeparatedDateField` subclass`.
|
||||
HTML5 placeholders 'day', 'month' and 'year' are enabled by default.
|
||||
|
||||
:::php
|
||||
SeparatedDateField::create('MyDate');
|
||||
|
||||
<div class="alert" markdown="1">
|
||||
Any custom date format settings will be ignored.
|
||||
</div>
|
||||
|
||||
## Calendar Picker
|
||||
|
||||
The following setting will add a Calendar to a single DateField, using the jQuery UI DatePicker widget.
|
||||
|
||||
:::php
|
||||
DateField::create('MyDate')
|
||||
->setShowCalendar(true);
|
||||
|
||||
The jQuery date picker will support most custom locale formats (if left as default).
|
||||
If setting an explicit date format via setDateFormat() then the below table of supported
|
||||
characters should be used.
|
||||
|
||||
It is recommended to use numeric format, as `MMM` or `MMMM` month names may not always pass validation.
|
||||
|
||||
Constant | xxxxx
|
||||
-------- | -----
|
||||
d | numeric day of the month (without leading zero)
|
||||
dd | numeric day of the month (with leading zero)
|
||||
EEE | dayname, abbreviated
|
||||
EEEE | dayname
|
||||
M | numeric month of the year (without leading zero)
|
||||
MM | numeric month of the year (with leading zero)
|
||||
MMM | monthname, abbreviated
|
||||
MMMM | monthname
|
||||
y | year (4 digits)
|
||||
yy | year (2 digits)
|
||||
yyyy | year (4 digits)
|
||||
->setMaxDate('2012-12-31')
|
||||
|
||||
## Formatting Hints
|
||||
|
||||
|
@ -71,18 +71,24 @@ and default alignment of paragraphs and tables to browsers.
|
||||
|
||||
### Date and time formats
|
||||
|
||||
Formats can be set globally in the i18n class. These settings are currently only picked up by the CMS, you'll need
|
||||
to write your own logic for any frontend output.
|
||||
Formats can be set globally in the i18n class.
|
||||
You can use these settings for your own view logic.
|
||||
|
||||
:::php
|
||||
Config::inst()->update('i18n', 'date_format', 'dd.MM.YYYY');
|
||||
Config::inst()->update('i18n', 'time_format', 'HH:mm');
|
||||
|
||||
Most localization routines in SilverStripe use the [Zend_Date API](http://framework.zend.com/manual/1.12/en/zend.date.overview.html).
|
||||
This means all formats are defined in
|
||||
[ISO date format](http://framework.zend.com/manual/1.12/en/zend.date.constants.html),
|
||||
Localization in SilverStripe uses PHP's [intl extension](http://php.net/intl).
|
||||
Formats for it's [IntlDateFormatter](http://php.net/manual/en/class.intldateformatter.php)
|
||||
are defined in [ICU format](http://www.icu-project.org/apiref/icu4c/classSimpleDateFormat.html#details),
|
||||
not PHP's built-in [date()](http://nz.php.net/manual/en/function.date.php).
|
||||
|
||||
These settings are not used for CMS presentation.
|
||||
Users can choose their own locale, which determines the date format
|
||||
that gets presented to them. Currently this is a mix of PHP defaults (for readonly `DateField` and `TimeField`),
|
||||
browser defaults (for `DateField` on browsers supporting HTML5), and [Moment.JS](http://momentjs.com/)
|
||||
client-side logic (for `DateField` polyfills and other readonly dates and times).
|
||||
|
||||
### Language Names
|
||||
|
||||
SilverStripe comes with a built-in list of common languages, listed by locale and region.
|
||||
@ -126,32 +132,17 @@ Please refer to [W3C: Introduction to IDN and IRI](http://www.w3.org/Internation
|
||||
|
||||
### i18n in Form Fields
|
||||
|
||||
Date- and time related form fields support i18n ([api:DateField], [api:TimeField], [api:DatetimeField]).
|
||||
Date and time related form fields are automatically localised ([api:DateField], [api:TimeField], [api:DatetimeField]).
|
||||
Since they use HTML5 `type=date` and `type=time` fields by default, these fields will present dates
|
||||
in a localised format chosen by the browser and operating system.
|
||||
|
||||
:::php
|
||||
i18n::set_locale('ca_AD');
|
||||
$field = new DateField(); // will automatically set date format defaults for 'ca_AD'
|
||||
$field->setLocale('de_DE'); // will not update the date formats
|
||||
$field->setConfig('dateformat', 'dd. MMMM YYYY'); // sets typical 'de_DE' date format, shows as "23. Juni 1982"
|
||||
|
||||
Defaults can be applied globally for all field instances through the `DateField.default_config`
|
||||
and `TimeField.default_config` [configuration arrays](/developer_guides/configuration).
|
||||
If no 'locale' default is set on the field, [api:i18n::get_locale()] will be used.
|
||||
|
||||
**Important:** Form fields in the CMS are automatically configured according to the profile settings for the logged-in user (`Member->Locale`, `Member->DateFormat` and `Member->TimeFormat`). This means that in most cases,
|
||||
fields created through [api:DataObject::getCMSFields()] will get their i18n settings from a specific member
|
||||
|
||||
The [api:DateField] API can be enhanced by JavaScript, and comes with
|
||||
[jQuery UI datepicker](http://jqueryui.com/demos/datepicker/) capabilities built-in.
|
||||
The field tries to translate the date formats and locales into a format compatible with jQuery UI
|
||||
(see [api:DateField_View_JQuery::$locale_map_] and [api:DateField_View_JQuery::convert_iso_to_jquery_format()]).
|
||||
Fields can be forced to use a certain locale and date/time format by calling `setHTML5(false)`,
|
||||
followed by `setLocale()` or `setDateFormat()`/`setTimeFormat()`.
|
||||
|
||||
:::php
|
||||
$field = new DateField();
|
||||
$field->setLocale('de_AT'); // set Austrian/German locale
|
||||
$field->setConfig('showcalendar', true);
|
||||
$field->setConfig('jslocale', 'de'); // jQuery UI only has a generic German localization
|
||||
$field->setConfig('dateformat', 'dd. MMMM YYYY'); // will be transformed to 'dd. MM yy' for jQuery
|
||||
$field->setLocale('de_AT'); // set Austrian/German locale, defaulting format to dd.MM.y
|
||||
$field->setDateFormat('d.M.y'); // set a more specific date format (single digit day/month)
|
||||
|
||||
## Translating text
|
||||
|
||||
|
@ -398,6 +398,19 @@ In templates this can also be invoked as below:
|
||||
<%t MyObject.PLURALS 'An item|{count} items' count=$Count %>
|
||||
|
||||
|
||||
#### Removed Member.DateFormat and Member.TimeFormat database settings
|
||||
|
||||
We're using [native HTML5 date and time pickers](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date)
|
||||
in `DateField` and `TimeField` now ([discussion](https://github.com/silverstripe/silverstripe-framework/issues/6626)),
|
||||
where the browser localises the output based on the browser/system preferences.
|
||||
In this context it no longer makes sense to give users control over their own
|
||||
date and time formats in their CMS profile.
|
||||
Consequently, we've also removed `MemberDatetimeOptionsetField`.
|
||||
|
||||
`Member->getDateFormat()` and `Member->getTimeFormat()` still exist, and default to
|
||||
the [IntlDateFormatter defaults](http://php.net/manual/en/class.intldateformatter.php) for the selected locale.
|
||||
|
||||
|
||||
#### New asset storage mechanism
|
||||
|
||||
File system has been abstracted into an abstract interface. By default, the out of the box filesystem
|
||||
@ -1495,19 +1508,25 @@ New `DatetimeField` methods replace `getConfig()` / `setConfig()`:
|
||||
|
||||
New `DateField` methods replace `getConfig()` / `setConfig()`:
|
||||
|
||||
* `getShowCalendar()` / `setShowCalendar()`
|
||||
* `getDateFormat()` / `setShowCalendar()`
|
||||
* `getDateFormat()` / `setDateFormat()`
|
||||
* `getMinDate()` / `setMinDate()`
|
||||
* `getMaxDate()` / `setMaxDate()`
|
||||
* `getPlaceholders()` / `setPlaceholders()`
|
||||
* `getClientLocale` / `setClientLocale`
|
||||
* `getLocale()` / `setLocale()`
|
||||
* option `dmyfields` is now superceded with an `SeparatedDateField` class
|
||||
|
||||
The `DateField` has changed behavior:
|
||||
|
||||
* `DateField` no longer provides a jQuery UI date picker (`showcalendar` option),
|
||||
and uses [HTML5 date pickers](https://www.wufoo.com/html5/types/4-date.html) by default instead.
|
||||
* `DateField` provides an optional polyfill for
|
||||
[browsers without HTML5 date picker support](http://caniuse.com/#feat=input-datetime)
|
||||
* The `dmyfields` option has been replced with native HTML5 behaviour (as one single `<input type=date>`).
|
||||
* `getClientLocale` / `setClientLocale` have been removed (handled by `DateField->locale` and browser settings)
|
||||
|
||||
New `TimeField` methods replace `getConfig()` / `setConfig()`
|
||||
|
||||
* `getTimeFormat()` / `setTimeFormat()`
|
||||
* `getLocale()` / `setLocale()`
|
||||
* `getClientConfig()` has been removed (in favour of `setHTML5()`)
|
||||
|
||||
#### <a name="overview-template-removed"></a>Template and Form Removed API
|
||||
|
||||
@ -1550,6 +1569,8 @@ New `TimeField` methods replace `getConfig()` / `setConfig()`
|
||||
* `get_source_file_comments()`
|
||||
* `getOption`
|
||||
* `setOption`
|
||||
* Removed `MemberDatetimeOptionsetField` (no replacement)
|
||||
* Removed `DateField_View_JQuery` (replaced with native HTML5 support in `DateField`)
|
||||
|
||||
### <a name="overview-i18n"></a>i18n API
|
||||
|
||||
|
@ -5,6 +5,7 @@ namespace SilverStripe\Forms;
|
||||
use IntlDateFormatter;
|
||||
use SilverStripe\i18n\i18n;
|
||||
use InvalidArgumentException;
|
||||
use SilverStripe\ORM\FieldType\DBDate;
|
||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||
|
||||
/**
|
||||
@ -85,13 +86,6 @@ class DateField extends TextField
|
||||
*/
|
||||
protected $dateLength = null;
|
||||
|
||||
/**
|
||||
* Set whether to show placeholders
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $placeholders = true;
|
||||
|
||||
/**
|
||||
* Override locale for client side.
|
||||
*
|
||||
@ -122,25 +116,27 @@ class DateField extends TextField
|
||||
protected $rawValue = null;
|
||||
|
||||
/**
|
||||
* Check if calendar should be shown on the frontend
|
||||
* Use HTML5-based input fields (and force ISO 8601 date formats).
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $html5 = true;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getShowCalendar()
|
||||
public function getHTML5()
|
||||
{
|
||||
return $this->showCalendar;
|
||||
return $this->html5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if calendar should be shown on the frontend.
|
||||
* @internal WARNING: Experimental and volatile API.
|
||||
*
|
||||
* @param bool $show
|
||||
* @param boolean $bool
|
||||
* @return $this
|
||||
*/
|
||||
public function setShowCalendar($show)
|
||||
public function setHTML5($bool)
|
||||
{
|
||||
$this->showCalendar = $show;
|
||||
$this->html5 = $bool;
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -164,12 +160,8 @@ class DateField extends TextField
|
||||
}
|
||||
|
||||
/**
|
||||
* Get length of the date format to use. One of:
|
||||
*
|
||||
* - IntlDateFormatter::SHORT
|
||||
* - IntlDateFormatter::MEDIUM
|
||||
* - IntlDateFormatter::LONG
|
||||
* - IntlDateFormatter::FULL
|
||||
* Get length of the date format to use.
|
||||
* Only applicable with {@link setHTML5(false)}.
|
||||
*
|
||||
* @see http://php.net/manual/en/class.intldateformatter.php#intl.intldateformatter-constants
|
||||
*
|
||||
@ -192,6 +184,11 @@ class DateField extends TextField
|
||||
*/
|
||||
public function getDateFormat()
|
||||
{
|
||||
if ($this->getHTML5()) {
|
||||
// Browsers expect ISO 8601 dates, localisation is handled on the client
|
||||
$this->setDateFormat(DBDate::ISO_DATE);
|
||||
}
|
||||
|
||||
if ($this->dateFormat) {
|
||||
return $this->dateFormat;
|
||||
}
|
||||
@ -202,6 +199,7 @@ class DateField extends TextField
|
||||
|
||||
/**
|
||||
* Set date format in CLDR standard format.
|
||||
* Only applicable with {@link setHTML5(false)}.
|
||||
*
|
||||
* @see http://userguide.icu-project.org/formatparse/datetime#TOC-Date-Field-Symbol-Table
|
||||
* @param string $format
|
||||
@ -216,18 +214,40 @@ class DateField extends TextField
|
||||
/**
|
||||
* Get date formatter with the standard locale / date format
|
||||
*
|
||||
* @throws \LogicException
|
||||
* @return IntlDateFormatter
|
||||
*/
|
||||
protected function getFormatter()
|
||||
{
|
||||
if ($this->getHTML5() && $this->dateFormat && $this->dateFormat !== DBDate::ISO_DATE) {
|
||||
throw new \LogicException(
|
||||
'Please opt-out of HTML5 processing of ISO 8601 dates via setHTML5(false) if using setDateFormat()'
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->getHTML5() && $this->dateLength) {
|
||||
throw new \LogicException(
|
||||
'Please opt-out of HTML5 processing of ISO 8601 dates via setHTML5(false) if using setDateLength()'
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->getHTML5() && $this->locale) {
|
||||
throw new \LogicException(
|
||||
'Please opt-out of HTML5 processing of ISO 8601 dates via setHTML5(false) if using setLocale()'
|
||||
);
|
||||
}
|
||||
|
||||
$formatter = IntlDateFormatter::create(
|
||||
$this->getLocale(),
|
||||
$this->getDateLength(),
|
||||
IntlDateFormatter::NONE
|
||||
);
|
||||
|
||||
if ($this->getHTML5()) {
|
||||
// Browsers expect ISO 8601 dates, localisation is handled on the client
|
||||
$formatter->setPattern(DBDate::ISO_DATE);
|
||||
} elseif ($this->dateFormat) {
|
||||
// Don't invoke getDateFormat() directly to avoid infinite loop
|
||||
if ($this->dateFormat) {
|
||||
$ok = $formatter->setPattern($this->dateFormat);
|
||||
if (!$ok) {
|
||||
throw new InvalidArgumentException("Invalid date format {$this->dateFormat}");
|
||||
@ -243,59 +263,28 @@ class DateField extends TextField
|
||||
*/
|
||||
protected function getISO8601Formatter()
|
||||
{
|
||||
$locale = i18n::config()->uninherited('default_locale');
|
||||
$formatter = IntlDateFormatter::create(
|
||||
i18n::config()->uninherited('default_locale'),
|
||||
IntlDateFormatter::MEDIUM,
|
||||
IntlDateFormatter::NONE
|
||||
);
|
||||
$formatter->setLenient(false);
|
||||
// CLDR iso8601 date.
|
||||
$formatter->setPattern('y-MM-dd');
|
||||
// CLDR ISO 8601 date.
|
||||
$formatter->setPattern(DBDate::ISO_DATE);
|
||||
return $formatter;
|
||||
}
|
||||
|
||||
public function FieldHolder($properties = array())
|
||||
{
|
||||
return $this->renderWithClientView(function () use ($properties) {
|
||||
return parent::FieldHolder($properties);
|
||||
});
|
||||
}
|
||||
|
||||
public function SmallFieldHolder($properties = array())
|
||||
{
|
||||
return $this->renderWithClientView(function () use ($properties) {
|
||||
return parent::SmallFieldHolder($properties);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate field with client view enabled
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return string
|
||||
*/
|
||||
protected function renderWithClientView($callback)
|
||||
{
|
||||
$clientView = null;
|
||||
if ($this->getShowCalendar()) {
|
||||
$clientView = $this->getClientView();
|
||||
$clientView->onBeforeRender();
|
||||
}
|
||||
$html = $callback();
|
||||
if ($clientView) {
|
||||
$html = $clientView->onAfterRender($html);
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
|
||||
public function getAttributes()
|
||||
{
|
||||
$attributes = parent::getAttributes();
|
||||
|
||||
// Merge with client config
|
||||
$config = $this->getClientConfig();
|
||||
foreach ($config as $key => $value) {
|
||||
$attributes["data-{$key}"] = $value;
|
||||
$attributes['lang'] = i18n::convert_rfc1766($this->getLocale());
|
||||
|
||||
if ($this->getHTML5()) {
|
||||
$attributes['type'] = 'date';
|
||||
$attributes['min'] = $this->getMinDate();
|
||||
$attributes['max'] = $this->getMaxDate();
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
@ -434,7 +423,9 @@ class DateField extends TextField
|
||||
}
|
||||
|
||||
/**
|
||||
* Caution: Will not update the 'dateformat' config value.
|
||||
* Determines the presented/processed format based on locale defaults,
|
||||
* instead of explicitly setting {@link setDateFormat()}.
|
||||
* Only applicable with {@link setHTML5(false)}.
|
||||
*
|
||||
* @param string $locale
|
||||
* @return $this
|
||||
@ -445,29 +436,6 @@ class DateField extends TextField
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get locale code for client-side. Will default to getLocale() if omitted.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getClientLocale()
|
||||
{
|
||||
if ($this->clientLocale) {
|
||||
return $this->clientLocale;
|
||||
}
|
||||
return $this->getLocale();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $clientLocale
|
||||
* @return DateField
|
||||
*/
|
||||
public function setClientLocale($clientLocale)
|
||||
{
|
||||
$this->clientLocale = $clientLocale;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSchemaValidation()
|
||||
{
|
||||
$rules = parent::getSchemaValidation();
|
||||
@ -475,28 +443,6 @@ class DateField extends TextField
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* If placeholders are shown
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getPlaceholders()
|
||||
{
|
||||
return $this->placeholders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if placeholders are shown
|
||||
*
|
||||
* @param bool $placeholders
|
||||
* @return $this
|
||||
*/
|
||||
public function setPlaceholders($placeholders)
|
||||
{
|
||||
$this->placeholders = $placeholders;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@ -533,35 +479,6 @@ class DateField extends TextField
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get client data properties for this field
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getClientConfig()
|
||||
{
|
||||
$view = $this->getClientView();
|
||||
$config = [
|
||||
'showcalendar' => $this->getShowCalendar() ? 'true' : null,
|
||||
'date-format' => $view->getDateFormat(), // https://api.jqueryui.com/datepicker/#option-dateFormat
|
||||
'locale' => $view->getLocale(),
|
||||
];
|
||||
|
||||
// Format min/maxDate in format expected by jquery datepicker
|
||||
$min = $this->getMinDate();
|
||||
if ($min) {
|
||||
// https://api.jqueryui.com/datepicker/#option-minDate
|
||||
$config['min-date'] = $this->iso8601ToLocalised($min);
|
||||
}
|
||||
$max = $this->getMaxDate();
|
||||
if ($max) {
|
||||
// https://api.jqueryui.com/datepicker/#option-maxDate
|
||||
$config['max-date'] = $this->iso8601ToLocalised($max);
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert date localised in the current locale to ISO 8601 date
|
||||
*
|
||||
@ -627,12 +544,4 @@ class DateField extends TextField
|
||||
}
|
||||
return $formatter->format($timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DateField_View_JQuery
|
||||
*/
|
||||
protected function getClientView()
|
||||
{
|
||||
return DateField_View_JQuery::create($this);
|
||||
}
|
||||
}
|
||||
|
@ -1,197 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Forms;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Core\Config\Configurable;
|
||||
use SilverStripe\Core\Injector\Injectable;
|
||||
use SilverStripe\i18n\i18n;
|
||||
use SilverStripe\View\Requirements;
|
||||
|
||||
/**
|
||||
* Preliminary API to separate optional view properties
|
||||
* like calendar popups from the actual datefield logic.
|
||||
*
|
||||
* Caution: This API is highly volatile, and might change without prior deprecation.
|
||||
*/
|
||||
class DateField_View_JQuery
|
||||
{
|
||||
use Injectable;
|
||||
use Configurable;
|
||||
|
||||
/**
|
||||
* @var DateField
|
||||
*/
|
||||
protected $field;
|
||||
|
||||
/**
|
||||
* @var array Maps values from {@link i18n::$all_locales} to
|
||||
* localizations existing in jQuery UI.
|
||||
*/
|
||||
private static $locale_map = array(
|
||||
'en_GB' => 'en-GB',
|
||||
'en_US' => 'en',
|
||||
'en_NZ' => 'en-GB',
|
||||
'fr_CH' => 'fr',
|
||||
'pt_BR' => 'pt-BR',
|
||||
'sr_SR' => 'sr-SR',
|
||||
'zh_CN' => 'zh-CN',
|
||||
'zh_HK' => 'zh-HK',
|
||||
'zh_TW' => 'zh-TW',
|
||||
);
|
||||
|
||||
/**
|
||||
* @param DateField $field
|
||||
*/
|
||||
public function __construct($field)
|
||||
{
|
||||
$this->field = $field;
|
||||
|
||||
// Health check
|
||||
if (!$this->localePath('en')) {
|
||||
throw new InvalidArgumentException("Missing jquery config");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DateField
|
||||
*/
|
||||
public function getField()
|
||||
{
|
||||
return $this->field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get path to localisation file for a given locale, if it exists
|
||||
*
|
||||
* @param string $lang
|
||||
* @return string Relative path to file, or null if it isn't available
|
||||
*/
|
||||
protected function localePath($lang)
|
||||
{
|
||||
$path = ADMIN_THIRDPARTY_DIR . "/jquery-ui/datepicker/i18n/jquery.ui.datepicker-{$lang}.js";
|
||||
if (file_exists(BASE_PATH . '/' . $path)) {
|
||||
return $path;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function onBeforeRender()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param String $html
|
||||
* @return string
|
||||
*/
|
||||
public function onAfterRender($html)
|
||||
{
|
||||
if ($this->getField()->getShowCalendar()) {
|
||||
// Load config for this locale if available
|
||||
$locale = $this->getLocale();
|
||||
$localeFile = $this->localePath($locale);
|
||||
if ($localeFile) {
|
||||
Requirements::javascript($localeFile);
|
||||
}
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines which language to use for jQuery UI, which
|
||||
* can be different from the value set in i18n.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocale()
|
||||
{
|
||||
$locale = $this->getField()->getClientLocale();
|
||||
|
||||
// Check standard mappings
|
||||
$map = Config::inst()->get(__CLASS__, 'locale_map');
|
||||
if (array_key_exists($locale, $map)) {
|
||||
return $map[$locale];
|
||||
}
|
||||
|
||||
// Fall back to default lang (meaning "en_US" turns into "en")
|
||||
return i18n::getData()->langFromLocale($locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert iso to jquery UI date format.
|
||||
* Needs to be consistent with Zend formatting, otherwise validation will fail.
|
||||
* Removes all time settings like hour/minute/second from the format.
|
||||
* See http://docs.jquery.com/UI/Datepicker/formatDate
|
||||
* From http://userguide.icu-project.org/formatparse/datetime
|
||||
*
|
||||
* @param string $format
|
||||
* @return string
|
||||
*/
|
||||
public static function convert_iso_to_jquery_format($format)
|
||||
{
|
||||
$convert = array(
|
||||
'/([^d])d([^d])/' => '$1d$2',
|
||||
'/^d([^d])/' => 'd$1',
|
||||
'/([^d])d$/' => '$1d',
|
||||
'/dd/' => 'dd',
|
||||
'/SS/' => '',
|
||||
'/eee/' => 'd',
|
||||
'/e/' => 'N',
|
||||
'/D/' => '',
|
||||
'/EEEE/' => 'DD',
|
||||
'/EEE/' => 'D',
|
||||
'/w/' => '',
|
||||
// make single "M" lowercase
|
||||
'/([^M])M([^M])/' => '$1m$2',
|
||||
// make single "M" at start of line lowercase
|
||||
'/^M([^M])/' => 'm$1',
|
||||
// make single "M" at end of line lowercase
|
||||
'/([^M])M$/' => '$1m',
|
||||
// match exactly three capital Ms not preceeded or followed by an M
|
||||
'/(?<!M)MMM(?!M)/' => 'M',
|
||||
// match exactly two capital Ms not preceeded or followed by an M
|
||||
'/(?<!M)MM(?!M)/' => 'mm',
|
||||
// match four capital Ms (maximum allowed)
|
||||
'/MMMM/' => 'MM',
|
||||
'/l/' => '',
|
||||
'/YYYY/' => 'yy',
|
||||
'/yyyy/' => 'yy',
|
||||
// See http://open.silverstripe.org/ticket/7669
|
||||
'/y{1,3}/' => 'yy',
|
||||
'/a/' => '',
|
||||
'/B/' => '',
|
||||
'/hh/' => '',
|
||||
'/h/' => '',
|
||||
'/([^H])H([^H])/' => '',
|
||||
'/^H([^H])/' => '',
|
||||
'/([^H])H$/' => '',
|
||||
'/HH/' => '',
|
||||
// '/mm/' => '',
|
||||
'/ss/' => '',
|
||||
'/zzzz/' => '',
|
||||
'/I/' => '',
|
||||
'/ZZZZ/' => '',
|
||||
'/Z/' => '',
|
||||
'/z/' => '',
|
||||
'/X/' => '',
|
||||
'/r/' => '',
|
||||
'/U/' => '',
|
||||
);
|
||||
$patterns = array_keys($convert);
|
||||
$replacements = array_values($convert);
|
||||
|
||||
return preg_replace($patterns, $replacements, $format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get client date format
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDateFormat()
|
||||
{
|
||||
return static::convert_iso_to_jquery_format($this->getField()->getDateFormat());
|
||||
}
|
||||
}
|
@ -13,6 +13,10 @@ use SilverStripe\i18n\i18n;
|
||||
* If you want to save into {@link Date} or {@link Time} columns,
|
||||
* please instanciate the fields separately.
|
||||
*
|
||||
* This field does not implement the <input type="datetime-local"> HTML5 field,
|
||||
* but can use date and time HTML5 inputs separately (through {@link DateField->setHTML5()}
|
||||
* and {@link TimeField->setHTML5()}.
|
||||
*
|
||||
* # Configuration
|
||||
*
|
||||
* Individual options are configured either on the DatetimeField, or on individual
|
||||
|
@ -1,177 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Forms;
|
||||
|
||||
use SilverStripe\i18n\i18n;
|
||||
|
||||
/**
|
||||
* Date field with separate inputs for d/m/y
|
||||
*/
|
||||
class SeparatedDateField extends DateField
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $separator = '/';
|
||||
|
||||
public function Field($properties = array())
|
||||
{
|
||||
// Three separate fields for day, month and year
|
||||
$valArr = $this->iso8601ToArray($this->dataValue());
|
||||
$fieldDay = NumericField::create($this->name . '[day]', false, $valArr ? $valArr['day'] : null)
|
||||
->addExtraClass('day')
|
||||
->setHTML5(true)
|
||||
->setMaxLength(2);
|
||||
$fieldMonth = NumericField::create($this->name . '[month]', false, $valArr ? $valArr['month'] : null)
|
||||
->addExtraClass('month')
|
||||
->setHTML5(true)
|
||||
->setMaxLength(2);
|
||||
$fieldYear = NumericField::create($this->name . '[year]', false, $valArr ? $valArr['year'] : null)
|
||||
->addExtraClass('year')
|
||||
->setHTML5(true)
|
||||
->setMaxLength(4);
|
||||
|
||||
// Set placeholders
|
||||
if ($this->getPlaceholders()) {
|
||||
$fieldDay->setAttribute('placeholder', _t(__CLASS__ . '.DAY', 'Day'));
|
||||
$fieldMonth->setAttribute('placeholder', _t(__CLASS__ . '.MONTH', 'Month'));
|
||||
$fieldYear->setAttribute('placeholder', _t(__CLASS__ . '.YEAR', 'Year'));
|
||||
}
|
||||
|
||||
$format = $this->getDateFormat();
|
||||
$validFormat = (
|
||||
stripos($format, 'd') !== false
|
||||
&& stripos($format, 'm') !== false
|
||||
&& stripos($format, 'y') !== false
|
||||
);
|
||||
if (!$validFormat) {
|
||||
throw new \InvalidArgumentException(
|
||||
'Invalid date format for field ordering: ' . $format
|
||||
. '. Requires "d", "m", and "y" values to determine order'
|
||||
);
|
||||
}
|
||||
|
||||
$fields = array();
|
||||
$fields[stripos($format, 'd')] = $fieldDay->Field();
|
||||
$fields[stripos($format, 'm')] = $fieldMonth->Field();
|
||||
$fields[stripos($format, 'y')] = $fieldYear->Field();
|
||||
ksort($fields);
|
||||
|
||||
|
||||
// Join all fields
|
||||
$sep = ' <span class="separator">' . $this->getSeparator() . '</span> ';
|
||||
return implode($sep, $fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $string
|
||||
* @return self
|
||||
*/
|
||||
public function setSeparator($separator)
|
||||
{
|
||||
$this->separator = $separator;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSeparator()
|
||||
{
|
||||
return $this->separator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert array to timestamp
|
||||
*
|
||||
* @param array $value
|
||||
* @return string
|
||||
*/
|
||||
public function arrayToISO8601($value)
|
||||
{
|
||||
if ($this->isEmptyArray($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// ensure all keys are specified
|
||||
if (!isset($value['month']) || !isset($value['day']) || !isset($value['year'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Ensure valid range
|
||||
if (!checkdate($value['month'], $value['day'], $value['year'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Note: Set formatter to strict for array input
|
||||
$formatter = $this->getISO8601Formatter();
|
||||
$timestamp = mktime(0, 0, 0, $value['month'], $value['day'], $value['year']);
|
||||
if ($timestamp === false) {
|
||||
return null;
|
||||
}
|
||||
return $formatter->format($timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert iso 8601 date to array (day / month / year)
|
||||
*
|
||||
* @param string $date
|
||||
* @return array|null Array form, or null if not valid
|
||||
*/
|
||||
public function iso8601ToArray($date)
|
||||
{
|
||||
if (!$date) {
|
||||
return null;
|
||||
}
|
||||
$formatter = $this->getISO8601Formatter();
|
||||
$timestamp = $formatter->parse($date);
|
||||
if ($timestamp === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Format time manually into an array
|
||||
return [
|
||||
'day' => date('j', $timestamp),
|
||||
'month' => date('n', $timestamp),
|
||||
'year' => date('Y', $timestamp),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign value posted from form submission
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param mixed $data
|
||||
* @return $this
|
||||
*/
|
||||
public function setSubmittedValue($value, $data = null)
|
||||
{
|
||||
// Filter out empty arrays
|
||||
if ($this->isEmptyArray($value)) {
|
||||
$value = null;
|
||||
}
|
||||
$this->rawValue = $value;
|
||||
|
||||
// Null case
|
||||
if (!$value || !is_array($value)) {
|
||||
$this->value = null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Parse
|
||||
$this->value = $this->arrayToISO8601($value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this array is empty
|
||||
*
|
||||
* @param $value
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmptyArray($value)
|
||||
{
|
||||
return is_array($value) && !array_filter($value);
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ use IntlDateFormatter;
|
||||
use InvalidArgumentException;
|
||||
use SilverStripe\i18n\i18n;
|
||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||
use SilverStripe\ORM\FieldType\DBTime;
|
||||
|
||||
/**
|
||||
* Form field to display editable time values in an <input type="text"> field.
|
||||
@ -57,6 +58,31 @@ class TimeField extends TextField
|
||||
*/
|
||||
protected $timezone = null;
|
||||
|
||||
/**
|
||||
* Use HTML5-based input fields (and force ISO 8601 time formats).
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $html5 = true;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getHTML5()
|
||||
{
|
||||
return $this->html5;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boolean $bool
|
||||
* @return $this
|
||||
*/
|
||||
public function setHTML5($bool)
|
||||
{
|
||||
$this->html5 = $bool;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get time format in CLDR standard format
|
||||
*
|
||||
@ -67,6 +93,11 @@ class TimeField extends TextField
|
||||
*/
|
||||
public function getTimeFormat()
|
||||
{
|
||||
if ($this->getHTML5()) {
|
||||
// Browsers expect ISO 8601 times, localisation is handled on the client
|
||||
$this->setTimeFormat(DBTime::ISO_TIME);
|
||||
}
|
||||
|
||||
if ($this->timeFormat) {
|
||||
return $this->timeFormat;
|
||||
}
|
||||
@ -77,6 +108,7 @@ class TimeField extends TextField
|
||||
|
||||
/**
|
||||
* Set time format in CLDR standard format.
|
||||
* Only applicable with {@link setHTML5(false)}.
|
||||
*
|
||||
* @see http://userguide.icu-project.org/formatparse/datetime#TOC-Date-Field-Symbol-Table
|
||||
* @param string $format
|
||||
@ -108,12 +140,8 @@ class TimeField extends TextField
|
||||
}
|
||||
|
||||
/**
|
||||
* Get length of the time format to use. One of:
|
||||
*
|
||||
* - IntlDateFormatter::SHORT E.g. '6:31 PM'
|
||||
* - IntlDateFormatter::MEDIUM E.g. '6:30:48 PM'
|
||||
* - IntlDateFormatter::LONG E.g. '6:32:09 PM NZDT'
|
||||
* - IntlDateFormatter::FULL E.g. '6:32:24 PM New Zealand Daylight Time'
|
||||
* Get length of the time format to use.
|
||||
* Only applicable with {@link setHTML5(false)}.
|
||||
*
|
||||
* @see http://php.net/manual/en/class.intldateformatter.php#intl.intldateformatter-constants
|
||||
*
|
||||
@ -133,6 +161,24 @@ class TimeField extends TextField
|
||||
*/
|
||||
protected function getFormatter()
|
||||
{
|
||||
if ($this->getHTML5() && $this->timeFormat && $this->timeFormat !== DBTime::ISO_TIME) {
|
||||
throw new \LogicException(
|
||||
'Please opt-out of HTML5 processing of ISO 8601 times via setHTML5(false) if using setTimeFormat()'
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->getHTML5() && $this->timeLength) {
|
||||
throw new \LogicException(
|
||||
'Please opt-out of HTML5 processing of ISO 8601 times via setHTML5(false) if using setTimeLength()'
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->getHTML5() && $this->locale) {
|
||||
throw new \LogicException(
|
||||
'Please opt-out of HTML5 processing of ISO 8601 times via setHTML5(false) if using setLocale()'
|
||||
);
|
||||
}
|
||||
|
||||
$formatter = IntlDateFormatter::create(
|
||||
$this->getLocale(),
|
||||
IntlDateFormatter::NONE,
|
||||
@ -140,8 +186,11 @@ class TimeField extends TextField
|
||||
$this->getTimezone()
|
||||
);
|
||||
|
||||
// Don't invoke getDateFormat() directly to avoid infinite loop
|
||||
if ($this->timeFormat) {
|
||||
if ($this->getHTML5()) {
|
||||
// Browsers expect ISO 8601 times, localisation is handled on the client
|
||||
$formatter->setPattern(DBTime::ISO_TIME);
|
||||
// Don't invoke getTimeFormat() directly to avoid infinite loop
|
||||
} elseif ($this->timeFormat) {
|
||||
$ok = $formatter->setPattern($this->timeFormat);
|
||||
if (!$ok) {
|
||||
throw new InvalidArgumentException("Invalid time format {$this->timeFormat}");
|
||||
@ -164,35 +213,22 @@ class TimeField extends TextField
|
||||
date_default_timezone_get() // Default to server timezone
|
||||
);
|
||||
$formatter->setLenient(false);
|
||||
// CLDR iso8601 time
|
||||
|
||||
// Note we omit timezone from this format, and we assume server TZ always.
|
||||
$formatter->setPattern('HH:mm:ss');
|
||||
$formatter->setPattern(DBTime::ISO_TIME);
|
||||
|
||||
return $formatter;
|
||||
}
|
||||
|
||||
public function getAttribute($name)
|
||||
public function getAttributes()
|
||||
{
|
||||
$attributes = parent::getAttributes();
|
||||
|
||||
// Merge with client config
|
||||
$config = $this->getClientConfig();
|
||||
foreach ($config as $key => $value) {
|
||||
$attributes["data-{$key}"] = $value;
|
||||
}
|
||||
return $attributes;
|
||||
if ($this->getHTML5()) {
|
||||
$attributes['type'] = 'time';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get client config options for this field
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getClientConfig()
|
||||
{
|
||||
return [
|
||||
// @todo - Support javascript time picker
|
||||
'timeformat' => $this->getTimeFormat(),
|
||||
];
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
public function Type()
|
||||
@ -302,6 +338,10 @@ class TimeField extends TextField
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the presented/processed format based on locale defaults,
|
||||
* instead of explicitly setting {@link setTimeFormat()}.
|
||||
* Only applicable with {@link setHTML5(false)}.
|
||||
*
|
||||
* @param string $locale
|
||||
* @return $this
|
||||
*/
|
||||
@ -337,9 +377,19 @@ class TimeField extends TextField
|
||||
$fromFormatter = $this->getFormatter();
|
||||
$toFormatter = $this->getISO8601Formatter();
|
||||
$timestamp = $fromFormatter->parse($time);
|
||||
|
||||
// Try to parse time without seconds, since that's a valid HTML5 submission format
|
||||
// See https://html.spec.whatwg.org/multipage/infrastructure.html#times
|
||||
if ($timestamp === false && $this->getHTML5()) {
|
||||
$fromFormatter->setPattern('HH:mm');
|
||||
$timestamp = $fromFormatter->parse($time);
|
||||
}
|
||||
|
||||
// If timestamp still can't be detected, we've got an invalid time
|
||||
if ($timestamp === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $toFormatter->format($timestamp);
|
||||
}
|
||||
|
||||
|
@ -512,17 +512,7 @@ class DBDate extends DBField
|
||||
public function scaffoldFormField($title = null, $params = null)
|
||||
{
|
||||
$field = DateField::create($this->name, $title);
|
||||
$format = $field->getDateFormat();
|
||||
|
||||
// Show formatting hints for better usability
|
||||
$now = DBDatetime::now()->Format($format);
|
||||
$field->setDescription(_t(
|
||||
'FormField.EXAMPLE',
|
||||
'e.g. {format}',
|
||||
'Example format',
|
||||
[ 'format' => $now ]
|
||||
));
|
||||
$field->setAttribute('placeholder', $format);
|
||||
$field->setHTML5(true);
|
||||
|
||||
return $field;
|
||||
}
|
||||
|
@ -141,19 +141,7 @@ class DBTime extends DBField
|
||||
|
||||
public function scaffoldFormField($title = null, $params = null)
|
||||
{
|
||||
$field = TimeField::create($this->name, $title);
|
||||
$format = $field->getTimeFormat();
|
||||
|
||||
// Show formatting hints for better usability
|
||||
$now = DBDatetime::now()->Format($format);
|
||||
$field->setDescription(_t(
|
||||
'FormField.Example',
|
||||
'e.g. {format}',
|
||||
'Example format',
|
||||
[ 'format' => $now ]
|
||||
));
|
||||
$field->setAttribute('placeholder', $format);
|
||||
return $field;
|
||||
return TimeField::create($this->name, $title);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -19,7 +19,6 @@ use SilverStripe\Forms\DropdownField;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\HTMLEditor\HTMLEditorConfig;
|
||||
use SilverStripe\Forms\ListboxField;
|
||||
use SilverStripe\Forms\MemberDatetimeOptionsetField;
|
||||
use SilverStripe\i18n\i18n;
|
||||
use SilverStripe\MSSQL\MSSQLDatabase;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
@ -81,9 +80,6 @@ class Member extends DataObject implements TemplateGlobalProvider
|
||||
'Locale' => 'Varchar(6)',
|
||||
// handled in registerFailedLogin(), only used if $lock_out_after_incorrect_logins is set
|
||||
'FailedLoginCount' => 'Int',
|
||||
// In ISO format
|
||||
'DateFormat' => 'Varchar(30)',
|
||||
'TimeFormat' => 'Varchar(30)',
|
||||
);
|
||||
|
||||
private static $belongs_many_many = array(
|
||||
@ -1315,20 +1311,24 @@ class Member extends DataObject implements TemplateGlobalProvider
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the default getter for DateFormat so the
|
||||
* default format for the user's locale is used
|
||||
* if the user has not defined their own.
|
||||
* Return the date format based on the user's chosen locale,
|
||||
* falling back to the default format defined by the {@link i18n.get_locale()} setting.
|
||||
*
|
||||
* @return string ISO date format
|
||||
*/
|
||||
public function getDateFormat()
|
||||
{
|
||||
$format = $this->getField('DateFormat');
|
||||
if ($format) {
|
||||
$formatter = new IntlDateFormatter(
|
||||
$this->getLocale(),
|
||||
IntlDateFormatter::MEDIUM,
|
||||
IntlDateFormatter::NONE
|
||||
);
|
||||
$format = $formatter->getPattern();
|
||||
|
||||
$this->extend('updateDateFormat', $format);
|
||||
|
||||
return $format;
|
||||
}
|
||||
return $this->getDefaultDateFormat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user locale
|
||||
@ -1343,19 +1343,23 @@ class Member extends DataObject implements TemplateGlobalProvider
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the default getter for TimeFormat so the
|
||||
* default format for the user's locale is used
|
||||
* if the user has not defined their own.
|
||||
* Return the time format based on the user's chosen locale,
|
||||
* falling back to the default format defined by the {@link i18n.get_locale()} setting.
|
||||
*
|
||||
* @return string ISO date format
|
||||
*/
|
||||
public function getTimeFormat()
|
||||
{
|
||||
$timeFormat = $this->getField('TimeFormat');
|
||||
if ($timeFormat) {
|
||||
return $timeFormat;
|
||||
}
|
||||
return $this->getDefaultTimeFormat();
|
||||
$formatter = new IntlDateFormatter(
|
||||
$this->getLocale(),
|
||||
IntlDateFormatter::NONE,
|
||||
IntlDateFormatter::MEDIUM
|
||||
);
|
||||
$format = $formatter->getPattern();
|
||||
|
||||
$this->extend('updateTimeFormat', $format);
|
||||
|
||||
return $format;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------//
|
||||
@ -1592,112 +1596,11 @@ class Member extends DataObject implements TemplateGlobalProvider
|
||||
if ($permissionsTab) {
|
||||
$permissionsTab->addExtraClass('readonly');
|
||||
}
|
||||
|
||||
// Date format selecter
|
||||
$mainFields->push(
|
||||
$dateFormatField = new MemberDatetimeOptionsetField(
|
||||
'DateFormat',
|
||||
$this->fieldLabel('DateFormat'),
|
||||
$this->getDateFormats()
|
||||
)
|
||||
);
|
||||
$formatClass = get_class($dateFormatField);
|
||||
$dateFormatField->setValue($this->DateFormat);
|
||||
$dateTemplate = SSViewer::get_templates_by_class($formatClass, '_description_date', $formatClass);
|
||||
$dateFormatField->setDescriptionTemplate($dateTemplate);
|
||||
|
||||
// Time format selector
|
||||
$mainFields->push(
|
||||
$timeFormatField = new MemberDatetimeOptionsetField(
|
||||
'TimeFormat',
|
||||
$this->fieldLabel('TimeFormat'),
|
||||
$this->getTimeFormats()
|
||||
)
|
||||
);
|
||||
$timeFormatField->setValue($this->TimeFormat);
|
||||
$timeTemplate = SSViewer::get_templates_by_class($formatClass, '_description_time', $formatClass);
|
||||
$timeFormatField->setDescriptionTemplate($timeTemplate);
|
||||
});
|
||||
|
||||
return parent::getCMSFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of date formats with example values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getDateFormats()
|
||||
{
|
||||
$defaultDateFormat = $this->getDefaultDateFormat();
|
||||
$formats = [
|
||||
'MMM d, y' => null,
|
||||
'yyyy/MM/dd' => null,
|
||||
'MM/dd/y' => null,
|
||||
'dd/MM/y' => null,
|
||||
];
|
||||
unset($formats[$defaultDateFormat]);
|
||||
$formats[$defaultDateFormat] = null;
|
||||
// Fill in each format with example
|
||||
foreach (array_keys($formats) as $format) {
|
||||
$formats[$format] = DBDatetime::now()->Format($format);
|
||||
}
|
||||
// Mark default format
|
||||
$formats[$defaultDateFormat] .= sprintf(' (%s)', _t('Member.DefaultDateTime', 'default'));
|
||||
return $formats;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDefaultDateFormat()
|
||||
{
|
||||
$formatter = new IntlDateFormatter(
|
||||
$this->getLocale(),
|
||||
IntlDateFormatter::MEDIUM,
|
||||
IntlDateFormatter::NONE
|
||||
);
|
||||
return $formatter->getPattern();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDefaultTimeFormat()
|
||||
{
|
||||
$formatter = new IntlDateFormatter(
|
||||
$this->getLocale(),
|
||||
IntlDateFormatter::NONE,
|
||||
IntlDateFormatter::MEDIUM
|
||||
);
|
||||
$defaultTimeFormat = $formatter->getPattern();
|
||||
return $defaultTimeFormat;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get list of date formats with example values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTimeFormats()
|
||||
{
|
||||
$defaultTimeFormat = $this->getDefaultTimeFormat();
|
||||
$formats = [
|
||||
'h:mm a' => null,
|
||||
'H:mm' => null,
|
||||
];
|
||||
unset($formats[$defaultTimeFormat]);
|
||||
$formats[$defaultTimeFormat] = null;
|
||||
// Fill in each format with example
|
||||
foreach (array_keys($formats) as $format) {
|
||||
$formats[$format] = DBDatetime::now()->Format($format);
|
||||
}
|
||||
// Mark default format
|
||||
$formats[$defaultTimeFormat] .= sprintf(' (%s)', _t('Member.DefaultDateTime', 'default'));
|
||||
return $formats;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $includerelations Indicate if the labels returned include relation fields
|
||||
* @return array
|
||||
@ -1714,8 +1617,6 @@ class Member extends DataObject implements TemplateGlobalProvider
|
||||
$labels['PasswordExpiry'] = _t('Member.db_PasswordExpiry', 'Password Expiry Date', 'Password expiry date');
|
||||
$labels['LockedOutUntil'] = _t('Member.db_LockedOutUntil', 'Locked out until', 'Security related date');
|
||||
$labels['Locale'] = _t('Member.db_Locale', 'Interface Locale');
|
||||
$labels['DateFormat'] = _t('Member.DATEFORMAT', 'Date format');
|
||||
$labels['TimeFormat'] = _t('Member.TIMEFORMAT', 'Time format');
|
||||
if ($includerelations) {
|
||||
$labels['Groups'] = _t(
|
||||
'Member.belongs_many_many_Groups',
|
||||
|
@ -85,12 +85,20 @@ class i18n implements TemplateGlobalProvider
|
||||
private static $default_locale = 'en_US';
|
||||
|
||||
/**
|
||||
* System-wide date format. Will be overruled for CMS UI display
|
||||
* by the format defaults inferred from the browser as well as
|
||||
* any user-specific locale preferences.
|
||||
*
|
||||
* @config
|
||||
* @var string
|
||||
*/
|
||||
private static $date_format = 'yyyy-MM-dd';
|
||||
|
||||
/**
|
||||
* System-wide time format. Will be overruled for CMS UI display
|
||||
* by the format defaults inferred from the browser as well as
|
||||
* any user-specific locale preferences.
|
||||
*
|
||||
* @config
|
||||
* @var string
|
||||
*/
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
namespace SilverStripe\Forms\Tests;
|
||||
|
||||
use IntlDateFormatter;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\Forms\DateField;
|
||||
use SilverStripe\Forms\SeparatedDateField;
|
||||
use SilverStripe\Forms\RequiredFields;
|
||||
use SilverStripe\i18n\i18n;
|
||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||
@ -105,21 +105,11 @@ class DateFieldTest extends SapphireTest
|
||||
public function testSetValueWithDateString()
|
||||
{
|
||||
$f = new DateField('Date', 'Date');
|
||||
$f->setHTML5(false);
|
||||
$f->setSubmittedValue('29/03/2003');
|
||||
$this->assertEquals($f->dataValue(), '2003-03-29');
|
||||
}
|
||||
|
||||
public function testSetValueWithDateArray()
|
||||
{
|
||||
$f = new SeparatedDateField('Date', 'Date');
|
||||
$f->setSubmittedValue([
|
||||
'day' => 29,
|
||||
'month' => 03,
|
||||
'year' => 2003
|
||||
]);
|
||||
$this->assertEquals($f->dataValue(), '2003-03-29');
|
||||
}
|
||||
|
||||
public function testConstructorWithIsoDate()
|
||||
{
|
||||
// used by Form->loadDataFrom()
|
||||
@ -145,84 +135,11 @@ class DateFieldTest extends SapphireTest
|
||||
$this->assertFalse($f->validate(new RequiredFields()));
|
||||
}
|
||||
|
||||
public function testEmptyValueValidation()
|
||||
{
|
||||
$validator = new RequiredFields();
|
||||
$field = new SeparatedDateField('Date');
|
||||
$this->assertTrue($field->validate($validator));
|
||||
$field->setSubmittedValue([
|
||||
'day' => '',
|
||||
'month' => '',
|
||||
'year' => '',
|
||||
]);
|
||||
$this->assertTrue($field->validate($validator));
|
||||
}
|
||||
|
||||
public function testValidateArray()
|
||||
{
|
||||
$f = new SeparatedDateField('Date', 'Date');
|
||||
$f->setSubmittedValue([
|
||||
'day' => 29,
|
||||
'month' => 03,
|
||||
'year' => 2003
|
||||
]);
|
||||
$this->assertTrue($f->validate(new RequiredFields()));
|
||||
|
||||
$f->setValue(null);
|
||||
$this->assertTrue($f->validate(new RequiredFields()), 'NULL values are validating TRUE');
|
||||
|
||||
$f->setSubmittedValue(array());
|
||||
$this->assertTrue($f->validate(new RequiredFields()), 'Empty array values are validating TRUE');
|
||||
|
||||
$f->setSubmittedValue([
|
||||
'day' => null,
|
||||
'month' => null,
|
||||
'year' => null
|
||||
]);
|
||||
$this->assertTrue($f->validate(new RequiredFields()), 'Empty array values with keys are validating TRUE');
|
||||
$f->setSubmittedValue([
|
||||
'day' => 9999,
|
||||
'month' => 9999,
|
||||
'year' => 9999
|
||||
]);
|
||||
$this->assertFalse($f->validate(new RequiredFields()));
|
||||
}
|
||||
|
||||
public function testValidateEmptyArrayValuesSetsNullForValueObject()
|
||||
{
|
||||
$f = new SeparatedDateField('Date', 'Date');
|
||||
$f->setSubmittedValue([
|
||||
'day' => '',
|
||||
'month' => '',
|
||||
'year' => ''
|
||||
]);
|
||||
$this->assertNull($f->dataValue());
|
||||
|
||||
$f->setSubmittedValue([
|
||||
'day' => null,
|
||||
'month' => null,
|
||||
'year' => null
|
||||
]);
|
||||
$this->assertNull($f->dataValue());
|
||||
}
|
||||
|
||||
public function testValidateArrayValue()
|
||||
{
|
||||
$f = new SeparatedDateField('Date', 'Date');
|
||||
$f->setSubmittedValue(['day' => 29, 'month' => 03, 'year' => 2003]);
|
||||
$this->assertTrue($f->validate(new RequiredFields()));
|
||||
|
||||
$f->setSubmittedValue(['month' => 03, 'year' => 2003]);
|
||||
$this->assertFalse($f->validate(new RequiredFields()));
|
||||
|
||||
$f->setSubmittedValue(array('day' => 99, 'month' => 99, 'year' => 2003));
|
||||
$this->assertFalse($f->validate(new RequiredFields()));
|
||||
}
|
||||
|
||||
public function testFormatEnNz()
|
||||
{
|
||||
/* We get YYYY-MM-DD format as the data value for DD/MM/YYYY input value */
|
||||
$f = new DateField('Date', 'Date');
|
||||
$f->setHTML5(false);
|
||||
$f->setSubmittedValue('29/03/2003');
|
||||
$this->assertEquals($f->dataValue(), '2003-03-29');
|
||||
}
|
||||
@ -232,6 +149,7 @@ class DateFieldTest extends SapphireTest
|
||||
// should get en_NZ by default through setUp()
|
||||
i18n::set_locale('de_DE');
|
||||
$f = new DateField('Date', 'Date', '29/03/2003');
|
||||
$f->setHTML5(false);
|
||||
$f->setValue('29.06.2006');
|
||||
$this->assertEquals($f->dataValue(), '2006-06-29');
|
||||
}
|
||||
@ -242,6 +160,7 @@ class DateFieldTest extends SapphireTest
|
||||
public function testMDYFormat()
|
||||
{
|
||||
$dateField = new DateField('Date', 'Date');
|
||||
$dateField->setHTML5(false);
|
||||
$dateField->setDateFormat('d/M/y');
|
||||
$dateField->setSubmittedValue('31/03/2003');
|
||||
$this->assertEquals(
|
||||
@ -251,6 +170,7 @@ class DateFieldTest extends SapphireTest
|
||||
);
|
||||
|
||||
$dateField2 = new DateField('Date', 'Date');
|
||||
$dateField2->setHTML5(false);
|
||||
$dateField2->setDateFormat('d/M/y');
|
||||
$dateField2->setSubmittedValue('04/3/03');
|
||||
$this->assertEquals(
|
||||
@ -259,4 +179,40 @@ class DateFieldTest extends SapphireTest
|
||||
"Even if input value hasn't got leading 0's in it we still get the correct data value"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessageRegExp /Please opt-out .* if using setDateFormat/
|
||||
*/
|
||||
public function testHtml5WithCustomFormatThrowsException()
|
||||
{
|
||||
$dateField = new DateField('Date', 'Date');
|
||||
$dateField->setValue('2010-03-31');
|
||||
$dateField->setDateFormat('d/M/y');
|
||||
$dateField->Value();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessageRegExp /Please opt-out .* if using setDateLength/
|
||||
*/
|
||||
public function testHtml5WithCustomDateLengthThrowsException()
|
||||
{
|
||||
$dateField = new DateField('Date', 'Date');
|
||||
$dateField->setValue('2010-03-31');
|
||||
$dateField->setDateLength(IntlDateFormatter::MEDIUM);
|
||||
$dateField->Value();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessageRegExp /Please opt-out .* if using setLocale/
|
||||
*/
|
||||
public function testHtml5WithCustomLocaleThrowsException()
|
||||
{
|
||||
$dateField = new DateField('Date', 'Date');
|
||||
$dateField->setValue('2010-03-31');
|
||||
$dateField->setLocale('de_DE');
|
||||
$dateField->Value();
|
||||
}
|
||||
}
|
||||
|
@ -1,47 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Forms\Tests;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\Forms\DateField_View_JQuery;
|
||||
|
||||
class DateFieldViewJQueryTest extends SapphireTest
|
||||
{
|
||||
|
||||
public function testConvert()
|
||||
{
|
||||
$this->assertEquals(
|
||||
'M d, yy',
|
||||
DateField_View_JQuery::convert_iso_to_jquery_format('MMM d, yyyy')
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'd/mm/yy',
|
||||
DateField_View_JQuery::convert_iso_to_jquery_format('d/MM/yyyy')
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'dd.m.yy',
|
||||
DateField_View_JQuery::convert_iso_to_jquery_format('dd.M.yyyy'),
|
||||
'Month, no leading zero'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'dd.mm.yy',
|
||||
DateField_View_JQuery::convert_iso_to_jquery_format('dd.MM.yyyy'),
|
||||
'Month, two digit'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'dd.M.yy',
|
||||
DateField_View_JQuery::convert_iso_to_jquery_format('dd.MMM.yyyy'),
|
||||
'Abbreviated month name'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'dd.MM.yy',
|
||||
DateField_View_JQuery::convert_iso_to_jquery_format('dd.MMMM.yyyy'),
|
||||
'Full month name'
|
||||
);
|
||||
}
|
||||
}
|
@ -7,7 +7,6 @@ use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Forms\DatetimeField;
|
||||
use SilverStripe\Forms\RequiredFields;
|
||||
use SilverStripe\Forms\DateField;
|
||||
use SilverStripe\Forms\SeparatedDateField;
|
||||
use SilverStripe\Forms\Tests\DatetimeFieldTest\Model;
|
||||
use SilverStripe\Forms\TimeField;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
@ -38,6 +37,32 @@ class DatetimeFieldTest extends SapphireTest
|
||||
$form = $this->getMockForm();
|
||||
$form->Fields()->push($dateTimeField);
|
||||
|
||||
$dateTimeField->setSubmittedValue([
|
||||
'date' => '2003-03-29',
|
||||
'time' => '23:59:38'
|
||||
]);
|
||||
$validator = new RequiredFields();
|
||||
$this->assertTrue($dateTimeField->validate($validator));
|
||||
$m = new Model();
|
||||
$form->saveInto($m);
|
||||
$this->assertEquals('2003-03-29 23:59:38', $m->MyDatetime);
|
||||
}
|
||||
|
||||
public function testFormSaveIntoLocalised()
|
||||
{
|
||||
$dateTimeField = new DatetimeField('MyDatetime');
|
||||
|
||||
$dateTimeField->getDateField()
|
||||
->setHTML5(false)
|
||||
->setLocale('en_NZ');
|
||||
|
||||
$dateTimeField->getTimeField()
|
||||
->setHTML5(false)
|
||||
->setLocale('en_NZ');
|
||||
|
||||
$form = $this->getMockForm();
|
||||
$form->Fields()->push($dateTimeField);
|
||||
|
||||
// en_NZ standard format
|
||||
$dateTimeField->setSubmittedValue([
|
||||
'date' => '29/03/2003',
|
||||
@ -97,6 +122,25 @@ class DatetimeFieldTest extends SapphireTest
|
||||
public function testSetValueWithArray()
|
||||
{
|
||||
$datetimeField = new DatetimeField('Datetime', 'Datetime');
|
||||
$datetimeField->setSubmittedValue([
|
||||
'date' => '2003-03-29',
|
||||
'time' => '23:00:00'
|
||||
]);
|
||||
$this->assertEquals($datetimeField->dataValue(), '2003-03-29 23:00:00');
|
||||
}
|
||||
|
||||
public function testSetValueWithArrayLocalised()
|
||||
{
|
||||
$datetimeField = new DatetimeField('Datetime', 'Datetime');
|
||||
|
||||
$datetimeField->getDateField()
|
||||
->setHTML5(false)
|
||||
->setLocale('en_NZ');
|
||||
|
||||
$datetimeField->getTimeField()
|
||||
->setHTML5(false)
|
||||
->setLocale('en_NZ');
|
||||
|
||||
// Values can only be localized (= non-ISO) in array notation
|
||||
$datetimeField->setSubmittedValue([
|
||||
'date' => '29/03/2003',
|
||||
@ -105,17 +149,6 @@ class DatetimeFieldTest extends SapphireTest
|
||||
$this->assertEquals($datetimeField->dataValue(), '2003-03-29 23:00:00');
|
||||
}
|
||||
|
||||
public function testSetValueWithDmyArray()
|
||||
{
|
||||
$f = new DatetimeField('Datetime', 'Datetime');
|
||||
$f->setDateField(new SeparatedDateField('Datetime[date]'));
|
||||
$f->setSubmittedValue([
|
||||
'date' => ['day' => 29, 'month' => 03, 'year' => 2003],
|
||||
'time' => '11:00:00 pm'
|
||||
]);
|
||||
$this->assertEquals($f->dataValue(), '2003-03-29 23:00:00');
|
||||
}
|
||||
|
||||
public function testValidate()
|
||||
{
|
||||
$f = new DatetimeField('Datetime', 'Datetime', '2003-03-29 23:59:38');
|
||||
@ -128,11 +161,20 @@ class DatetimeFieldTest extends SapphireTest
|
||||
$this->assertFalse($f->validate(new RequiredFields()));
|
||||
}
|
||||
|
||||
public function testTimezoneSet()
|
||||
public function testTimezoneSetLocalised()
|
||||
{
|
||||
date_default_timezone_set('Europe/Berlin');
|
||||
// Berlin and Auckland have 12h time difference in northern hemisphere winter
|
||||
$datetimeField = new DatetimeField('Datetime', 'Datetime');
|
||||
|
||||
$datetimeField->getDateField()
|
||||
->setHTML5(false)
|
||||
->setLocale('en_NZ');
|
||||
|
||||
$datetimeField->getTimeField()
|
||||
->setHTML5(false)
|
||||
->setLocale('en_NZ');
|
||||
|
||||
$datetimeField->setTimezone('Pacific/Auckland');
|
||||
$datetimeField->setValue('2003-12-24 23:59:59');
|
||||
$this->assertEquals(
|
||||
@ -149,11 +191,20 @@ class DatetimeFieldTest extends SapphireTest
|
||||
);
|
||||
}
|
||||
|
||||
public function testTimezoneFromConfig()
|
||||
public function testTimezoneFromConfigLocalised()
|
||||
{
|
||||
date_default_timezone_set('Europe/Berlin');
|
||||
// Berlin and Auckland have 12h time difference in northern hemisphere summer, but Berlin and Moscow only 2h.
|
||||
$datetimeField = new DatetimeField('Datetime', 'Datetime');
|
||||
|
||||
$datetimeField->getDateField()
|
||||
->setHTML5(false)
|
||||
->setLocale('en_NZ');
|
||||
|
||||
$datetimeField->getTimeField()
|
||||
->setHTML5(false)
|
||||
->setLocale('en_NZ');
|
||||
|
||||
$datetimeField->setTimezone('Europe/Moscow');
|
||||
$datetimeField->setSubmittedValue([
|
||||
// pass in default format, at user time (Moscow)
|
||||
@ -170,7 +221,7 @@ class DatetimeFieldTest extends SapphireTest
|
||||
$field = new DatetimeField('Datetime', 'Datetime');
|
||||
$field->setForm($form);
|
||||
$field->setSubmittedValue([
|
||||
'date' => '24/06/2003',
|
||||
'date' => '2003-06-24',
|
||||
'time' => '23:59:59',
|
||||
]);
|
||||
$dateField = new DateField('Datetime[date]');
|
||||
@ -195,8 +246,8 @@ class DatetimeFieldTest extends SapphireTest
|
||||
$field = new DatetimeField('Datetime', 'Datetime');
|
||||
$field->setForm($form);
|
||||
$field->setSubmittedValue([
|
||||
'date' => '24/06/2003',
|
||||
'time' => '11:59:59 pm',
|
||||
'date' => '2003-06-24',
|
||||
'time' => '23:59:59',
|
||||
]);
|
||||
$timeField = new TimeField('Datetime[time]');
|
||||
$field->setTimeField($timeField);
|
||||
|
@ -1,177 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Forms\Tests;
|
||||
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Dev\CSSContentParser;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Forms\MemberDatetimeOptionsetField;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\Form;
|
||||
use SilverStripe\Forms\RequiredFields;
|
||||
use SilverStripe\i18n\i18n;
|
||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||
use SilverStripe\Security\Member;
|
||||
|
||||
class MemberDatetimeOptionsetFieldTest extends SapphireTest
|
||||
{
|
||||
protected static $fixture_file = 'MemberDatetimeOptionsetFieldTest.yml';
|
||||
|
||||
/**
|
||||
* @param Member $member
|
||||
* @return MemberDatetimeOptionsetField
|
||||
*/
|
||||
protected function createDateFormatFieldForMember($member)
|
||||
{
|
||||
$defaultDateFormat = $member->getDefaultDateFormat();
|
||||
$dateFormatMap = array(
|
||||
'yyyy-MM-dd' => DBDatetime::now()->Format('yyyy-MM-dd'),
|
||||
'yyyy/MM/dd' => DBDatetime::now()->Format('yyyy/MM/dd'),
|
||||
'MM/dd/yyyy' => DBDatetime::now()->Format('MM/dd/yyyy'),
|
||||
'dd/MM/yyyy' => DBDatetime::now()->Format('dd/MM/yyyy'),
|
||||
);
|
||||
$dateFormatMap[$defaultDateFormat] = DBDatetime::now()->Format($defaultDateFormat) . ' (default)';
|
||||
$field = new MemberDatetimeOptionsetField(
|
||||
'DateFormat',
|
||||
'Date format',
|
||||
$dateFormatMap
|
||||
);
|
||||
$field->setValue($member->getDateFormat());
|
||||
return $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Member $member
|
||||
* @return MemberDatetimeOptionsetField
|
||||
*/
|
||||
protected function createTimeFormatFieldForMember($member)
|
||||
{
|
||||
$defaultTimeFormat = $member->getDefaultTimeFormat();
|
||||
$timeFormatMap = array(
|
||||
'h:mm a' => DBDatetime::now()->Format('h:mm a'),
|
||||
'H:mm' => DBDatetime::now()->Format('H:mm'),
|
||||
);
|
||||
$timeFormatMap[$defaultTimeFormat] = DBDatetime::now()->Format($defaultTimeFormat) . ' (default)';
|
||||
$field = new MemberDatetimeOptionsetField(
|
||||
'TimeFormat',
|
||||
'Time format',
|
||||
$timeFormatMap
|
||||
);
|
||||
$field->setValue($member->getTimeFormat());
|
||||
return $field;
|
||||
}
|
||||
|
||||
public function testDateFormatDefaultCheckedInFormField()
|
||||
{
|
||||
/** @var Member $member */
|
||||
$member = $this->objFromFixture(Member::class, 'noformatmember');
|
||||
$field = $this->createDateFormatFieldForMember($member);
|
||||
/** @skipUpgrade */
|
||||
$field->setForm(
|
||||
new Form(
|
||||
new Controller(),
|
||||
'Form',
|
||||
new FieldList(),
|
||||
new FieldList()
|
||||
)
|
||||
); // fake form
|
||||
// `MMM d, y` is default format for default locale (en_US)
|
||||
$parser = new CSSContentParser($field->Field());
|
||||
$xmlArr = $parser->getBySelector('#Form_Form_DateFormat_MMM_d_y');
|
||||
$this->assertEquals('checked', (string) $xmlArr[0]['checked']);
|
||||
}
|
||||
|
||||
public function testTimeFormatDefaultCheckedInFormField()
|
||||
{
|
||||
/** @var Member $member */
|
||||
$member = $this->objFromFixture(Member::class, 'noformatmember');
|
||||
$field = $this->createTimeFormatFieldForMember($member);
|
||||
/** @skipUpgrade */
|
||||
$field->setForm(
|
||||
new Form(
|
||||
new Controller(),
|
||||
'Form',
|
||||
new FieldList(),
|
||||
new FieldList()
|
||||
)
|
||||
); // fake form
|
||||
// `h:mm:ss a` is the default for en_US locale
|
||||
$parser = new CSSContentParser($field->Field());
|
||||
$xmlArr = $parser->getBySelector('#Form_Form_TimeFormat_h:mm:ss_a');
|
||||
$this->assertEquals('checked', (string) $xmlArr[0]['checked']);
|
||||
}
|
||||
|
||||
public function testDateFormatChosenIsCheckedInFormField()
|
||||
{
|
||||
/** @var Member $member */
|
||||
$member = $this->objFromFixture(Member::class, 'noformatmember');
|
||||
$member->setField('DateFormat', 'MM/dd/yyyy');
|
||||
$field = $this->createDateFormatFieldForMember($member);
|
||||
/** @skipUpgrade */
|
||||
$field->setForm(
|
||||
new Form(
|
||||
new Controller(),
|
||||
'Form',
|
||||
new FieldList(),
|
||||
new FieldList()
|
||||
)
|
||||
); // fake form
|
||||
$parser = new CSSContentParser($field->Field());
|
||||
$xmlArr = $parser->getBySelector('#Form_Form_DateFormat_MM_dd_yyyy');
|
||||
$this->assertEquals('checked', (string) $xmlArr[0]['checked']);
|
||||
}
|
||||
|
||||
public function testDateFormatCustomFormatAppearsInCustomInputInField()
|
||||
{
|
||||
/** @var Member $member */
|
||||
$member = $this->objFromFixture(Member::class, 'noformatmember');
|
||||
$member->setField('DateFormat', 'dd MM yy');
|
||||
$field = $this->createDateFormatFieldForMember($member);
|
||||
/** @skipUpgrade */
|
||||
$field->setForm(
|
||||
new Form(
|
||||
new Controller(),
|
||||
'Form',
|
||||
new FieldList(),
|
||||
new FieldList()
|
||||
)
|
||||
); // fake form
|
||||
$parser = new CSSContentParser($field->Field());
|
||||
$xmlInputArr = $parser->getBySelector('.valcustom input');
|
||||
$this->assertEquals('checked', (string) $xmlInputArr[0]['checked']);
|
||||
$this->assertEquals('dd MM yy', (string) $xmlInputArr[1]['value']);
|
||||
}
|
||||
|
||||
public function testDateFormValid()
|
||||
{
|
||||
$field = new MemberDatetimeOptionsetField('DateFormat', 'DateFormat');
|
||||
$validator = new RequiredFields();
|
||||
$this->assertTrue($field->validate($validator));
|
||||
$field->setSubmittedValue([
|
||||
'Options' => '__custom__',
|
||||
'Custom' => 'dd MM yyyy'
|
||||
]);
|
||||
$this->assertTrue($field->validate($validator));
|
||||
$field->setSubmittedValue([
|
||||
'Options' => '__custom__',
|
||||
'Custom' => 'sdfdsfdfd1244'
|
||||
]);
|
||||
// @todo - Be less forgiving of invalid CLDR date format strings
|
||||
$this->assertTrue($field->validate($validator));
|
||||
}
|
||||
|
||||
public function testDescriptionTemplate()
|
||||
{
|
||||
$field = new MemberDatetimeOptionsetField('DateFormat', 'DateFormat');
|
||||
|
||||
$this->assertEmpty($field->getDescription());
|
||||
|
||||
$field->setDescription('Test description');
|
||||
$this->assertEquals('Test description', $field->getDescription());
|
||||
|
||||
$field->setDescriptionTemplate(get_class($field).'_description_time');
|
||||
$this->assertNotEmpty($field->getDescription());
|
||||
$this->assertNotEquals('Test description', $field->getDescription());
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
SilverStripe\Security\Member:
|
||||
noformatmember:
|
||||
Email: noformat@test.com
|
||||
delocalemember:
|
||||
Email: delocalemember@test.com
|
||||
Locale: de_DE
|
@ -1,53 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Forms\Tests;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\Forms\DateField;
|
||||
use SilverStripe\Forms\SeparatedDateField;
|
||||
use SilverStripe\Forms\RequiredFields;
|
||||
use SilverStripe\i18n\i18n;
|
||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||
|
||||
class SeparatedDateFieldTest extends SapphireTest
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
i18n::set_locale('en_NZ');
|
||||
DBDatetime::set_mock_now('2011-02-01 8:34:00');
|
||||
}
|
||||
|
||||
public function testFieldOrderingBasedOnLocale()
|
||||
{
|
||||
$dateField = new SeparatedDateField('Date');
|
||||
$dateField->setLocale('en_NZ');
|
||||
$this->assertRegExp('/.*[day].*[month].*[year]/', $dateField->Field());
|
||||
}
|
||||
|
||||
public function testFieldOrderingBasedOnDateFormat()
|
||||
{
|
||||
$dateField = new SeparatedDateField('Date');
|
||||
$dateField->setDateFormat('y/MM/dd');
|
||||
$this->assertRegExp('/.*[year].*[month].*[day]/', $dateField->Field());
|
||||
}
|
||||
|
||||
public function testCustomSeparator()
|
||||
{
|
||||
$dateField = new SeparatedDateField('Date');
|
||||
$dateField->setDateFormat('dd/MM/y');
|
||||
$dateField->setSeparator('###');
|
||||
$this->assertRegExp('/.*[day].*###.*[month].*###.*[day]/', $dateField->Field());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage Invalid date format
|
||||
*/
|
||||
public function testInvalidDateFormat()
|
||||
{
|
||||
$dateField = new SeparatedDateField('Date');
|
||||
$dateField->setDateFormat('y/MM');
|
||||
$dateField->Field();
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace SilverStripe\Forms\Tests;
|
||||
|
||||
use IntlDateFormatter;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\Forms\TimeField;
|
||||
@ -40,11 +41,23 @@ class TimeFieldTest extends SapphireTest
|
||||
$this->assertFalse($f->validate(new RequiredFields()));
|
||||
}
|
||||
|
||||
public function testValidateLenientWithHtml5()
|
||||
{
|
||||
$f = new TimeField('Time', 'Time', '23:59:59');
|
||||
$f->setHTML5(true);
|
||||
$this->assertTrue($f->validate(new RequiredFields()));
|
||||
|
||||
$f = new TimeField('Time', 'Time', '23:59'); // leave out seconds
|
||||
$f->setHTML5(true);
|
||||
$this->assertTrue($f->validate(new RequiredFields()));
|
||||
}
|
||||
|
||||
public function testSetLocale()
|
||||
{
|
||||
// should get en_NZ by default through setUp()
|
||||
$f = new TimeField('Time', 'Time');
|
||||
$f->setLocale('de_DE');
|
||||
$f->setHTML5(false);
|
||||
$f->setLocale('fr_FR');
|
||||
// TODO Find a hour format thats actually different
|
||||
$f->setValue('23:59');
|
||||
$this->assertEquals($f->dataValue(), '23:59:00');
|
||||
@ -85,8 +98,7 @@ class TimeFieldTest extends SapphireTest
|
||||
public function testOverrideWithNull()
|
||||
{
|
||||
$field = new TimeField('Time', 'Time');
|
||||
|
||||
$field->setValue('11:00pm');
|
||||
$field->setValue('11:00:00');
|
||||
$field->setValue('');
|
||||
$this->assertEquals($field->dataValue(), '');
|
||||
}
|
||||
@ -94,33 +106,80 @@ class TimeFieldTest extends SapphireTest
|
||||
/**
|
||||
* Test that AM/PM is preserved correctly in various situations
|
||||
*/
|
||||
public function testPreserveAMPM()
|
||||
public function testSetTimeFormat()
|
||||
{
|
||||
|
||||
// Test with timeformat that includes hour
|
||||
|
||||
// Check pm
|
||||
$f = new TimeField('Time', 'Time');
|
||||
$f->setHTML5(false);
|
||||
$f->setTimeFormat('h:mm:ss a');
|
||||
$f->setValue('3:59 pm');
|
||||
$this->assertEquals($f->dataValue(), '15:59:00');
|
||||
|
||||
// Check am
|
||||
$f = new TimeField('Time', 'Time');
|
||||
$f->setHTML5(false);
|
||||
$f->setTimeFormat('h:mm:ss a');
|
||||
$f->setValue('3:59 am');
|
||||
$this->assertEquals($f->dataValue(), '03:59:00');
|
||||
|
||||
// Check with ISO date/time
|
||||
$f = new TimeField('Time', 'Time');
|
||||
$f->setHTML5(false);
|
||||
$f->setTimeFormat('h:mm:ss a');
|
||||
$f->setValue('15:59:00');
|
||||
$this->assertEquals($f->dataValue(), '15:59:00');
|
||||
|
||||
// ISO am
|
||||
$f = new TimeField('Time', 'Time');
|
||||
$f->setHTML5(false);
|
||||
$f->setTimeFormat('h:mm:ss a');
|
||||
$f->setValue('03:59:00');
|
||||
$this->assertEquals($f->dataValue(), '03:59:00');
|
||||
}
|
||||
|
||||
public function testLenientSubmissionParseWithoutSecondsOnHtml5()
|
||||
{
|
||||
$f = new TimeField('Time', 'Time');
|
||||
$f->setSubmittedValue('23:59');
|
||||
$this->assertEquals($f->Value(), '23:59:00');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessageRegExp /Please opt-out .* if using setTimeFormat/
|
||||
*/
|
||||
public function testHtml5WithCustomFormatThrowsException()
|
||||
{
|
||||
$f = new TimeField('Time', 'Time');
|
||||
$f->setValue('15:59:00');
|
||||
$f->setTimeFormat('mm:HH');
|
||||
$f->Value();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessageRegExp /Please opt-out .* if using setTimeLength/
|
||||
*/
|
||||
public function testHtml5WithCustomDateLengthThrowsException()
|
||||
{
|
||||
$f = new TimeField('Time', 'Time');
|
||||
$f->setValue('15:59:00');
|
||||
$f->setTimeLength(IntlDateFormatter::MEDIUM);
|
||||
$f->Value();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessageRegExp /Please opt-out .* if using setLocale/
|
||||
*/
|
||||
public function testHtml5WithCustomLocaleThrowsException()
|
||||
{
|
||||
$f = new TimeField('Time', 'Time');
|
||||
$f->setValue('15:59:00');
|
||||
$f->setLocale('de_DE');
|
||||
$f->Value();
|
||||
}
|
||||
}
|
||||
|
@ -324,41 +324,4 @@ class DBDateTest extends SapphireTest
|
||||
|
||||
DBDatetime::clear_mock_now();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see testFormatFromSettings
|
||||
* @return array
|
||||
*/
|
||||
public function dataTestFormatFromSettings()
|
||||
{
|
||||
return [
|
||||
['2000-12-31', '31/12/2000'],
|
||||
['31-12-2000', '31/12/2000'],
|
||||
['2014-04-01', '01/04/2014'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataTestFormatFromSettings
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
*/
|
||||
public function testFormatFromSettings($from, $to)
|
||||
{
|
||||
$this->suppressNotices();
|
||||
$member = new Member();
|
||||
$member->DateFormat = 'dd/MM/y';
|
||||
|
||||
$date = DBField::create_field('Date', $from);
|
||||
$this->assertEquals($to, $date->FormatFromSettings($member));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that FormatFromSettings without a member defaults to Nice()
|
||||
*/
|
||||
public function testFormatFromSettingsEmpty()
|
||||
{
|
||||
$date = DBfield::create_field('Date', '2000-12-31');
|
||||
$this->assertEquals('31/12/2000', $date->FormatFromSettings());
|
||||
}
|
||||
}
|
||||
|
@ -204,41 +204,4 @@ class DBDatetimeTest extends SapphireTest
|
||||
|
||||
DBDatetime::clear_mock_now();
|
||||
}
|
||||
|
||||
public function dataTestFormatFromSettings()
|
||||
{
|
||||
return [
|
||||
['2000-12-31 10:11:01', '31/12/2000 10:11:01'],
|
||||
['2000-12-31 1:11:01', '31/12/2000 01:11:01'],
|
||||
['2000-12-12 1:11:01', '12/12/2000 01:11:01'],
|
||||
['2000-12-31', '31/12/2000 00:00:00'],
|
||||
['2014-04-01 10:11:01', '01/04/2014 10:11:01']
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataTestFormatFromSettings
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
*/
|
||||
public function testFormatFromSettings($from, $to)
|
||||
{
|
||||
$member = new Member();
|
||||
$member->DateFormat = 'dd/MM/y';
|
||||
$member->TimeFormat = 'HH:mm:ss';
|
||||
|
||||
$date = DBDatetime::create_field('Datetime', $from);
|
||||
$this->assertEquals($to, $date->FormatFromSettings($member));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that FormatFromSettings without a member defaults to Nice()
|
||||
*/
|
||||
public function testFormatFromSettingsEmpty()
|
||||
{
|
||||
$date = DBDatetime::create_field('Datetime', '2000-12-31 10:11:01');
|
||||
|
||||
// note: Some localisation packages exclude the ',' in default medium format
|
||||
$this->assertRegExp('#31/12/2000(,)? 10:11:01 AM#', $date->FormatFromSettings());
|
||||
}
|
||||
}
|
||||
|
@ -56,35 +56,4 @@ class DBTimeTest extends SapphireTest
|
||||
$time = DBTime::create_field('Time', '17:15:55');
|
||||
$this->assertEquals('5:15 PM', $time->Short());
|
||||
}
|
||||
|
||||
public function dataTestFormatFromSettings()
|
||||
{
|
||||
return [
|
||||
['10:11:01', '10:11:01 (AM)'],
|
||||
['21:11:01', '9:11:01 (PM)'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataTestFormatFromSettings
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
*/
|
||||
public function testFormatFromSettings($from, $to)
|
||||
{
|
||||
$member = new Member();
|
||||
$member->TimeFormat = 'h:mm:ss (a)';
|
||||
|
||||
$date = DBTime::create_field('Time', $from);
|
||||
$this->assertEquals($to, $date->FormatFromSettings($member));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that FormatFromSettings without a member defaults to Nice()
|
||||
*/
|
||||
public function testFormatFromSettingsEmpty()
|
||||
{
|
||||
$date = DBTime::create_field('Time', '10:11:01');
|
||||
$this->assertEquals('10:11:01 AM', $date->FormatFromSettings());
|
||||
}
|
||||
}
|
||||
|
@ -395,15 +395,6 @@ class MemberTest extends FunctionalTest
|
||||
$member->PasswordExpiry = date('Y-m-d', time() + 86400);
|
||||
$this->assertFalse($member->isPasswordExpired());
|
||||
}
|
||||
|
||||
public function testMemberWithNoDateFormatFallsbackToGlobalLocaleDefaultFormat()
|
||||
{
|
||||
// Note: All default strings are based on locale defaults for en_US
|
||||
$member = $this->objFromFixture(Member::class, 'noformatmember');
|
||||
$this->assertEquals('MMM d, y', $member->DateFormat);
|
||||
$this->assertEquals('h:mm:ss a', $member->TimeFormat);
|
||||
}
|
||||
|
||||
public function testInGroups()
|
||||
{
|
||||
$staffmember = $this->objFromFixture(Member::class, 'staffmember');
|
||||
|
Loading…
Reference in New Issue
Block a user