Version: current

      1 <?php
      2 require_once 'Zend/Date.php';
      3
      4 /**
      5  * Form field to display an editable date string,
      6  * either in a single `<input type="text">` field,
      7  * or in three separate fields for day, month and year.
      8  *
      9  * # Configuration
     10  *
     11  * - 'showcalendar' (boolean): Determines if a calendar picker is shown.
     12  *    By default, "DHTML Calendar" is used,see http://www.dynarch.com/projects/calendar.
     13  *    CAUTION: Only works in NZ date format, see calendar-setup.js
     14
     15  *    CAUTION: Might not be useable in combination with 'showcalendar', depending on the used javascript library
     16  * - 'dateformat' (string): Date format compatible with Zend_Date.
     17  *    Usually set to default format for {@link locale}
     18  *    through {@link Zend_Locale_Format::getDateFormat()}.
     19  * - 'datavalueformat' (string): Internal ISO format string used by {@link dataValue()} to save the
     20  *    date to a database.
     21  * - 'min' (string): Minimum allowed date value (in ISO format, or strtotime() compatible).
     22  *    Example: '2010-03-31', or '-7 days'
     23  * - 'max' (string): Maximum allowed date value (in ISO format, or strtotime() compatible).
     24  *    Example: '2010-03-31', or '1 year'
     25  *
     26  * # Localization
     27  *
     28  * The field will get its default locale from {@link i18n::get_locale()}, and set the `dateformat`
     29  * configuration accordingly. Changing the locale through {@link setLocale()} will not update the
     30  * `dateformat` configuration automatically.
     31  *
     32  * # Usage
     33  *
     34  * ## Example: German dates with separate fields for day, month, year
     35  *
     36  *  $f = new DateField('MyDate');
     37  *  $f->setLocale('de_DE');
     38
     39  *
     40  * # Validation
     41  *
     42  * Caution: JavaScript validation is only supported for the 'en_NZ' locale at the moment,
     43  * it will be disabled automatically for all other locales.
     44  *
     45  * @package forms
     46  * @subpackage fields-datetime
     47  */
     48 class DateField extends TextField {
     49
     50     /**
     51      * @var array
     52      */
     53     protected $config = array(
     54         'showcalendar' => false,
     55         'dateformat' => null,
     56         'datavalueformat' => 'yyyy-MM-dd',
     57         'min' => null,
     58         'max' => null
     59     );
     60
     61     /**
     62      * @var String
     63      */
     64     protected $locale = null;
     65
     66     /**
     67      * @var Zend_Date Just set if the date is valid.
     68      * {@link $value} will always be set to aid validation,
     69      * and might contain invalid values.
     70      */
     71     protected $valueObj = null;
     72
     73     function __construct($name, $title = null, $value = null, $form = null, $rightTitle = null) {
     74         if(!$this->locale) {
     75             $this->locale = i18n::get_locale();
     76         }
     77
     78         if(!$this->getConfig('dateformat')) {
     79             $this->setConfig('dateformat', Zend_Locale_Format::getDateFormat($this->locale));
     80         }
     81         if ($this->useHTML5()) {
     82             $this->setConfig('dateformat', 'yyyy-MM-dd');
     83         }
     84
     85         parent::__construct($name, $title, $value, $form, $rightTitle);
     86     }
     87
     88     function Field() {
     89         if ($this->useHTML5()) {
     90             $this->setHTML5Attribute('type', 'date');
     91             $this->setConfig('dateformat', 'yyyy-MM-dd');
     92             if ($min = $this->getConfig('min'))
     93                 $this->setHTML5Attribute('min', date('Y-m-d', strtotime($min)));
     94             if ($max = $this->getConfig('max'))
     95                 $this->setHTML5Attribute('max', date('Y-m-d', strtotime($max)));
     96         }
     97         $attributes = array(
     98             'type' => 'text',
     99             'class' => 'text ' . $this->class . ($this->extraClass() ? $this->extraClass() : ''),
    100             'id' => $this->id(),
    101             'name' => $this->Name(),
    102             'value' => $this->Value(),
    103             'tabindex' => $this->getTabIndex(),
    104             'maxlength' => ($this->maxLength) ? $this->maxLength : null,
    105             'size' => ($this->maxLength) ? min( $this->maxLength, 30 ) : null
    106         );
    107         /* может, в перспективе задавать форматы дат
    108         if($this->getConfig('showcalendar')) {
    109             $lang = i18n::get_lang_from_locale($this->getLocale());
    110             $attributes['lang'] = $lang;
    111         }
    112         */
    113         return $this->createTag('input', $attributes);
    114         // Видимо подключать здесь все скрипты календаря.
    115         // Но это должно регулироваться шаблоном (чтоб можно было не использовать на внешней части)
    116         return $html;
    117     }
    118
    119     function ShowCalendar() {
    120         return !$this->useHTML5() && $this->getConfig('showcalendar');
    121     }
    122
    123     function getCalendarLang() {
    124         if($this->ShowCalendar()) {
    125             $lang = i18n::get_lang_from_locale($this->getLocale());
    126             return $lang;
    127         }
    128         return false;
    129     }
    130
    131     /**
    132      * Sets the internal value to ISO date format.
    133      *
    134      * @param String|Array $val
    135      */
    136     function setValue($val) {
    137         if(empty($val)) {
    138             $this->value = null;
    139             $this->valueObj = null;
    140         } else {
    141             // Setting in corect locale.
    142             // Caution: Its important to have this check *before* the ISO date fallback,
    143             // as some dates are falsely detected as ISO by isDate(), e.g. '03/04/03'
    144             // (en_NZ for 3rd of April, definetly not yyyy-MM-dd)
    145             if(!empty($val) && Zend_Date::isDate($val, $this->getConfig('dateformat'), $this->locale)) {
    146                 $this->valueObj = new Zend_Date($val, $this->getConfig('dateformat'), $this->locale);
    147                 $this->value = $this->valueObj->get($this->getConfig('dateformat'));
    148             }
    149             // load ISO date from database (usually through Form->loadDataForm())
    150             else if(!empty($val) && Zend_Date::isDate($val, $this->getConfig('datavalueformat'))) {
    151                 $this->valueObj = new Zend_Date($val, $this->getConfig('datavalueformat'));
    152                 $this->value = $this->valueObj->get($this->getConfig('dateformat'));
    153             }
    154             else {
    155                 $this->value = $val;
    156                 $this->valueObj = null;
    157             }
    158         }
    159     }
    160
    161     /**
    162      * @return String ISO 8601 date, suitable for insertion into database
    163      */
    164     function dataValue() {
    165         if($this->valueObj) {
    166             return $this->valueObj->toString($this->getConfig('datavalueformat'));
    167         } else {
    168             return null;
    169         }
    170     }
    171
    172     function performReadonlyTransformation() {
    173         $field = new DateField_Disabled($this->name, $this->title, $this->value);
    174         $field->setForm($this->form);
    175         $field->readonly = true;
    176
    177         return $field;
    178     }
    179
    180     function jsValidation() {
    181         // !!! TODO возможно реализовать для админки
    182     }
    183
    184     /**
    185      * @return Boolean
    186      */
    187     function validate($validator) {
    188         $valid = true;
    189
    190         // Don't validate empty fields
    191         if(empty($this->value)) return true;
    192
    193         // date format
    194         $valid = (Zend_Date::isDate($this->value, $this->getConfig('dateformat'), $this->locale));
    195         if(!$valid) {
    196             $validator->validationError(
    197                 $this->name,
    198                 _t('DateField.VALIDDATEFORMAT', "Please enter a valid date format (DD/MM/YYYY)."),
    199                 "validation",
    200                 false
    201             );
    202             return false;
    203         }
    204
    205         // min/max - Assumes that the date value was valid in the first place
    206         if($min = $this->getConfig('min')) {
    207             // ISO or strtotime()
    208             if(Zend_Date::isDate($min, $this->getConfig('datavalueformat'))) {
    209                 $minDate = new Zend_Date($min, $this->getConfig('datavalueformat'));
    210             } else {
    211                 $minDate = new Zend_Date(strftime('%Y-%m-%d', strtotime($min)), $this->getConfig('datavalueformat'));
    212             }
    213             if(!$this->valueObj->isLater($minDate) && !$this->valueObj->equals($minDate)) {
    214                 $validator->validationError(
    215                     $this->name,
    216                     sprintf(
    217                         _t('DateField.VALIDDATEMINDATE', "Your date has to be newer or matching the minimum allowed date (%s)"),
    218                         $minDate->toString($this->getConfig('dateformat'))
    219                     ),
    220                     "validation",
    221                     false
    222                 );
    223                 return false;
    224             }
    225         }
    226         if($max = $this->getConfig('max')) {
    227             // ISO or strtotime()
    228             if(Zend_Date::isDate($min, $this->getConfig('datavalueformat'))) {
    229                 $maxDate = new Zend_Date($max, $this->getConfig('datavalueformat'));
    230             } else {
    231                 $maxDate = new Zend_Date(strftime('%Y-%m-%d', strtotime($max)), $this->getConfig('datavalueformat'));
    232             }
    233             if(!$this->valueObj->isEarlier($maxDate) && !$this->valueObj->equals($maxDate)) {
    234                 $validator->validationError(
    235                     $this->name,
    236                     sprintf(
    237                         _t('DateField.VALIDDATEMAXDATE', "Your date has to be older or matching the maximum allowed date (%s)"),
    238                         $maxDate->toString($this->getConfig('dateformat'))
    239                     ),
    240                     "validation",
    241                     false
    242                 );
    243                 return false;
    244             }
    245         }
    246
    247         return true;
    248     }
    249
    250     /**
    251      * @return string
    252      */
    253     function getLocale() {
    254         return $this->locale;
    255     }
    256
    257     /**
    258      * Caution: Will not update the 'dateformat' config value.
    259      *
    260      * @param String $locale
    261      */
    262     function setLocale($locale) {
    263         $this->locale = $locale;
    264     }
    265
    266     /**
    267      * @param string $name
    268      * @param mixed $val
    269      */
    270     function setConfig($name, $val) {
    271         switch($name) {
    272             case 'min':
    273                 $format = $this->getConfig('datavalueformat');
    274                 if($val && !Zend_Date::isDate($val, $format) && !strtotime($val)) {
    275                     throw new InvalidArgumentException('Date "%s" is not a valid minimum date format (%s) or strtotime() argument', $val, $format);
    276                 }
    277                 break;
    278             case 'max':
    279                 $format = $this->getConfig('datavalueformat');
    280                 if($val && !Zend_Date::isDate($val, $format) && !strtotime($val)) {
    281                     throw new InvalidArgumentException('Date "%s" is not a valid maximum date format (%s) or strtotime() argument', $val, $format);
    282                 }
    283                 break;
    284         }
    285
    286         $this->config[$name] = $val;
    287     }
    288
    289     /**
    290      * @param String $name
    291      * @return mixed
    292      */
    293     function getConfig($name) {
    294         return $this->config[$name];
    295     }
    296 }
    297
    298 /**
    299  * Disabled version of {@link DateField}.
    300  * Allows dates to be represented in a form, by showing in a user friendly format, eg, dd/mm/yyyy.
    301  * @package forms
    302  * @subpackage fields-datetime
    303  */
    304 class DateField_Disabled extends DateField {
    305
    306     protected $disabled = true;
    307
    308     function Field() {
    309         if($this->valueObj) {
    310             if($this->valueObj->isToday()) {
    311                 $val = Convert::raw2xml($this->valueObj->toString($this->getConfig('dateformat')) . ' ('._t('DateField.TODAY','today').')');
    312             } else {
    313                 $df = new Date($this->name);
    314                 $df->setValue($this->dataValue());
    315                 $val = Convert::raw2xml($this->valueObj->toString($this->getConfig('dateformat')) . ', ' . $df->Ago());
    316             }
    317         } else {
    318             $val = '<i>('._t('DateField.NOTSET', 'not set').')</i>';
    319         }
    320
    321         return "<span class=\"readonly\" id=\"" . $this->id() . "\">$val</span>";
    322     }
    323
    324     function Type() {
    325         return "date_disabled readonly";
    326     }
    327
    328     function jsValidation() {
    329         return null;
    330     }
    331
    332     function validate($validator) {
    333         return true;
    334     }
    335 }
    336

[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -