diff --git a/core/model/fieldtypes/Datetime.php b/core/model/fieldtypes/Datetime.php index 13e070879..2f63bd8b1 100644 --- a/core/model/fieldtypes/Datetime.php +++ b/core/model/fieldtypes/Datetime.php @@ -58,7 +58,7 @@ class SS_Datetime extends Date { } public function scaffoldFormField($title = null, $params = null) { - return new PopupDateTimeField($this->name, $title); + return new DatetimeField($this->name, $title); } /** diff --git a/css/DateField.css b/css/DateField.css new file mode 100755 index 000000000..c44b2a25b --- /dev/null +++ b/css/DateField.css @@ -0,0 +1,20 @@ +.calendardate .calendar table { + width: 200px; +} +.calendardate img { + position: relative; + top: 2px; + cursor: pointer; +} + +.calendarpopup { + position: absolute; + left: 0em; + top: 2em; + display: none; + z-index: 2; +} + +.calendarpopup.focused { + display: block; +} diff --git a/css/PopupDateTimeField.css b/css/DatetimeField.css similarity index 100% rename from css/PopupDateTimeField.css rename to css/DatetimeField.css diff --git a/css/DropdownTimeField.css b/css/TimeField_dropdown.css similarity index 100% rename from css/DropdownTimeField.css rename to css/TimeField_dropdown.css diff --git a/forms/CalendarDateField.php b/forms/CalendarDateField.php deleted file mode 100755 index 0a769ce43..000000000 --- a/forms/CalendarDateField.php +++ /dev/null @@ -1,62 +0,0 @@ - $this->futureOnly ? SSDatetime::now()->format('m/d/Y') : null - )); - - $this->addExtraClass($jsConfig); - - return parent::Field(); - } - - /** - * Sets the field so that only future dates can be set on them. - * Only applies for JavaScript value, no server-side validation. - * - * @deprecated 2.4 - */ - function futureDateOnly() { - $this->futureOnly = true; - } - -} - -?> \ No newline at end of file diff --git a/forms/CompositeDateField.php b/forms/CompositeDateField.php deleted file mode 100755 index 294c8de7a..000000000 --- a/forms/CompositeDateField.php +++ /dev/null @@ -1,178 +0,0 @@ -dateDropdown = new DropdownField($name."[date]", "", - array('NotSet' => '('._t('CompositeDateField.DAY', 'Day').')', - '01'=>'01', '02'=>'02', '03'=>'03', '04'=>'04', '05'=>'05', - '06'=>'06', '07'=>'07', '08'=>'08', '09'=>'09', '10'=>'10', - '11'=>'11', '12'=>'12', '13'=>'13', '14'=>'14', '15'=>'15', - '16'=>'16', '17'=>'17', '18'=>'18', '19'=>'19', '20'=>'20', - '21'=>'21', '22'=>'22', '23'=>'23', '24'=>'24', '25'=>'25', - '26'=>'26', '27'=>'27', '28'=>'28', '29'=>'29', '30'=>'30', - '31'=>'31' - ), - $date - ); - - $this->monthDropdown = new DropdownField($name."[month]", "", - array( 'NotSet' => '('._t('CompositeDateField.MONTH', 'Month').')', - '01'=>'01', '02'=>'02', '03'=>'03', '04'=>'04', '05'=>'05', - '06'=>'06', '07'=>'07', '08'=>'08', '09'=>'09', '10'=>'10', - '11'=>'11', '12'=>'12' - ), - $month - ); - - if($yearRange == null){ - $this->customiseYearDropDown($name, "1995-2012", $year); - }else{ - $this->customiseYearDropDown($name, $yearRange, $year); - } - parent::__construct($name, $title); - } - - function Field() { - $this->dateDropdown->setTabIndex($this->getTabIndex()); - $this->monthDropdown->setTabIndex($this->getTabIndex()+1); - $this->yearDropdown->setTabIndex($this->getTabIndex()+2); - return $this->dateDropdown->Field() . $this->monthDropdown->Field() . $this->yearDropdown->Field(); - } - - - function performReadonlyTransformation() { - $field = new CompositeDateField_Disabled($this->name, $this->title, $this->value); - $field->setForm($this->form); - return $field; - } - - function customiseYearDropDown($name, $yearRange, $year){ - list($from,$to) = explode('-', $yearRange); - $source['NotSet'] = '(Year)'; - for($i = $to; $i >= $from; $i--){ - $source[$i]=$i; - } - $this->yearDropdown = new DropdownField($name."[year]", "", $source, $year); - $this->yearDropdown->setValue($year); - } - - function setForm($form) { - $this->dateDropdown->setForm($form); - $this->monthDropdown->setForm($form); - $this->yearDropdown->setForm($form); - parent::setForm($form); - } - - function setValue($val) { - if($val) { - if(is_array($val)){ - if($val['date'] == 'NotSet' || $val['month'] == 'NotSet' || $val['year'] == 'NotSet'){ - $this->value = null; return; - } - $val = $val['year'] . '-' . $val['month'] . '-' . $val['date']; - } - $this->value = date('d/m/Y', strtotime($val)); - list($year, $month, $date) = explode("-", $val); - $this->yearDropdown->setValue($year); - $this->monthDropdown->setValue($month); - $this->dateDropdown->setValue($date); - - } else { - $this->value = null; - } - } - - function jsValidation() { - $formID = $this->form->FormName(); - $error1 = _t('CompositeDateField.VALIDATIONJS1', 'Please ensure you have set the'); - $error2 = _t('CompositeDateField.VALIDATIONJS2', 'correctly.'); - $day = _t('CompositeDateField.DAYJS', 'day'); - $month = _t('CompositeDateField.MONTHJS', 'month'); - $year = _t('CompositeDateField.YEARJS', 'year'); - $jsFunc =<<name');"; - } - - function validate($validator) { - // TODO Implement server-side validation - if($this->value == null) { - $validator->validationError($this->name,_t('Form.VALIDATIONALLDATEVALUES',"Please ensure you have set all date values"),"validation"); - return false; - } else { - return true; - } - } -} - -/** - * Allows dates to be represented in a form, by - * showing in a user friendly format, eg, dd/mm/yyyy. - * @package forms - * @subpackage fields-datetime - */ -class CompositeDateField_Disabled extends DateField { - - protected $disabled = true; - - function setValue($val) { - if($val && $val != "0000-00-00") $this->value = date('d/m/Y', strtotime($val)); - else $this->value = _t('Form.DATENOTSET', "(No date set)"); - } - - function Field() { - if($this->value) { - $df = new Date($this->name); - $df->setValue($this->dataValue()); - $val = Convert::raw2xml($this->value); - } else { - $val = '' . _t('Form.NOTSET', '(not set)') . ''; - } - - return "id() . "\">$val"; - } - - function Type() { - return "date_disabled readonly"; - } -} -?> \ No newline at end of file diff --git a/forms/DMYCalendarDateField.php b/forms/DMYCalendarDateField.php deleted file mode 100755 index a509318b1..000000000 --- a/forms/DMYCalendarDateField.php +++ /dev/null @@ -1,52 +0,0 @@ -id(); - $val = $this->attrValue(); - - $day = $month = $year = null; - - if( preg_match( '/^\d{2}\/\d{2}\/\d{4}$/', $val ) ) { - $dateArray = explode( '/', $val ); - $val = $dateArray[2] . '-' . $dateArray[1] . '-' . $dateArray[0]; - } - - if($val) { - $dateArray = explode( '-', $val ); - $day = $dateArray[2]; - $month = $dateArray[1]; - $year = $dateArray[0]; - } - - if(preg_match('/(.*)[(.+)]$/', $this->name, $fieldNameParts)) { - $fieldNamePrefix = $fieldNameParts[1]; - $fieldName = $fieldNameParts[2]; - } else { - $fieldNamePrefix = $this->name; - $fieldName = $this->name; - } - - return << - - / - / - -
- -HTML; - } -} -?> \ No newline at end of file diff --git a/forms/DMYDateField.php b/forms/DMYDateField.php deleted file mode 100644 index 7627c2bf8..000000000 --- a/forms/DMYDateField.php +++ /dev/null @@ -1,110 +0,0 @@ -value = $value['Year'] . '-' . $value['Month'] . '-' . $value['Day']; - else if(is_array($value)&&(!$value['Day']||!$value['Month']||!$value['Year'])) - $this->value = null; - else if(is_string($value)) - $this->value = $value; - } - - function Field() { - Requirements::javascript(SAPPHIRE_DIR . "/javascript/CalendarDateField.js"); - - $field = DateField::Field(); - - $id = $this->id(); - $val = $this->attrValue(); - - if( preg_match( '/^\d{2}\/\d{2}\/\d{4}$/', $val ) ) { - $dateArray = explode( '/', $val ); - - $val = $dateArray[2] . '-' . $dateArray[1] . '-' . $dateArray[0]; - } - - $day = $month = $year = null; - if($val) { - $dateArray = explode( '-', $val ); - - $day = $dateArray[2]; - $month = $dateArray[1]; - $year = $dateArray[0]; - } - - $fieldName = $this->name; - - return << - / - / - -
- -HTML; - } - - function validate($validator) - { - if(!empty ($this->value) && !preg_match('/^[0-90-9]{2,4}\-[0-9]{1,2}\-[0-90-9]{1,2}$/', $this->value)) - { - $validator->validationError( - $this->name, - _t('DMYDateField.VALIDDATEFORMAT', "Please enter a valid date format (DD-MM-YYYY)."), - "validation", - false - ); - return false; - } - return true; - } - - function jsValidation() { - $formID = $this->form->FormName(); - $error = _t('DateField.VALIDATIONJS', 'Please enter a valid date format (DD/MM/YYYY).'); - $jsFunc =<< 0 && !value.match(/^[0-9]{1,2}\/[0-9]{1,2}\/[0-90-9]{2,4}\$/)) { - validationError(_CURRENT_FORM.elements[fieldName+'[Day]'],"$error","validation",false); - return false; - } - return true; - } - } -}); -JS; - Requirements :: customScript($jsFunc, 'func_validateDMYDate_'.$formID); - -// return "\$('$formID').validateDate('$this->name');"; - return <<name') - \$('$formID').validateDMYDate('$this->name'); - }else{ - \$('$formID').validateDMYDate('$this->name'); - } -} -JS; - } -} -?> \ No newline at end of file diff --git a/forms/DateField.php b/forms/DateField.php index c3fe9a85a..50077d9d6 100755 --- a/forms/DateField.php +++ b/forms/DateField.php @@ -1,9 +1,41 @@ ` field, + * or in three separate fields for day, month and year. * - * @todo Add localization support, see http://open.silverstripe.com/ticket/2931 + * # Configuration + * + * - 'showcalendar' (boolean): Determines if a calendar picker is shown. + * By default, "DHTML Calendar" is used,see http://www.dynarch.com/projects/calendar. + * CAUTION: Only works in NZ date format, see calendar-setup.js + * - 'dmyfields' (boolean): Show three input fields for day, month and year separately. + * CAUTION: Might not be useable in combination with 'showcalendar', depending on the used javascript library + * - 'dateformat' (string): Date format compatible with Zend_Date. + * Usually set to default format for {@link locale} + * through {@link Zend_Locale_Format::getDateFormat()}. + * - 'datavalueformat' (string): Internal ISO format string used by {@link dataValue()} to save the + * date to a database. + * - 'min' (string): Minimum allowed date value (in ISO format, or strtotime() compatible). + * Example: '2010-03-31', or '-7 days' + * - 'max' (string): Maximum allowed date value (in ISO format, or strtotime() compatible). + * Example: '2010-03-31', or '1 year' + * + * # Localization + * + * The field will get its default locale from {@link i18n::get_locale()}, and set the `dateformat` + * configuration accordingly. Changing the locale through {@link setLocale()} will not update the + * `dateformat` configuration automatically. + * + * # Usage + * + * ## Example: German dates with separate fields for day, month, year + * + * $f = new DateField('MyDate'); + * $f->setLocale('de_DE'); + * $f->setConfig('dmyfields'); * * @package forms * @subpackage fields-datetime @@ -11,73 +43,260 @@ class DateField extends TextField { /** - * Enable DD/MM/YYYY field format validation - * in {@link DateField->validate()}. Set to - * FALSE to disable this validation. - * - * @var boolean + * @var array */ - public static $validation_enabled = true; + protected $config = array( + 'showcalendar' => false, + 'dmyfields' => false, + 'dateformat' => null, + 'datavalueformat' => 'yyyy-MM-dd', + 'min' => null, + 'max' => null + ); + + /** + * @var String + */ + protected $locale = null; + /** + * @var Zend_Date Just set if the date is valid. + * {@link $value} will always be set to aid validation, + * and might contain invalid values. + */ + protected $valueObj = null; + + function __construct($name, $title = null, $value = null, $form = null, $rightTitle = null) { + if(!$this->locale) { + $this->locale = i18n::get_locale(); + } + + if(!$this->getConfig('dateformat')) { + $this->setConfig('dateformat', Zend_Locale_Format::getDateFormat($this->locale)); + } + + parent::__construct($name, $title, $value, $form, $rightTitle); + } + + function FieldHolder() { + + + return parent::FieldHolder(); + } + + function Field() { + // Three separate fields for day, month and year + if($this->getConfig('dmyfields')) { + // values + $valArr = ($this->valueObj) ? $this->valueObj->toArray() : null; + + // fields + $fieldDay = new NumericField($this->name . '[day]', false, ($valArr) ? $valArr['day'] : null); + $fieldDay->addExtraClass('day'); + $fieldDay->setMaxLength(2); + $fieldMonth = new NumericField($this->name . '[month]', false, ($valArr) ? $valArr['month'] : null); + $fieldMonth->addExtraClass('month'); + $fieldMonth->setMaxLength(2); + $fieldYear = new NumericField($this->name . '[year]', false, ($valArr) ? $valArr['year'] : null); + $fieldYear->addExtraClass('year'); + $fieldYear->setMaxLength(4); + + // TODO Locale specific ordering of fields + $sep = ' / '; + $html = $fieldDay->Field() . + $sep . + $fieldMonth->Field() . + $sep . + $fieldYear->Field(); + } + // Default text input field + else { + $html = parent::Field(); + } + + $html = $this->FieldDriver($html); + + // wrap in additional div for legacy reasons and to apply behaviour correctly + if($this->getConfig('showcalendar')) $html = sprintf('
%s
', $html); + + return $html; + } + + /** + * Caution: API might change. This will evolve into a pluggable + * API for 'form field drivers' which can add their own + * markup and requirements. + * + * @param String $html + * @return $html + */ + protected function FieldDriver($html) { + // Optionally add a "DHTML" calendar icon. Mainly legacy, a date picker + // should be unobtrusively added by javascript (e.g. jQuery UI). + // CAUTION: Only works in NZ date format, see calendar-setup.js + if($this->getConfig('showcalendar')) { + Requirements::javascript(THIRDPARTY_DIR . '/prototype/prototype.js'); + Requirements::javascript(THIRDPARTY_DIR . '/behaviour/behaviour.js'); + Requirements::javascript(THIRDPARTY_DIR . "/calendar/calendar.js"); + Requirements::javascript(THIRDPARTY_DIR . "/calendar/lang/calendar-en.js"); + Requirements::javascript(THIRDPARTY_DIR . "/calendar/calendar-setup.js"); + Requirements::css(SAPPHIRE_DIR . "/css/DateField.css"); + Requirements::css(THIRDPARTY_DIR . "/calendar/calendar-win2k-1.css"); + + $html .= sprintf('Calendar icon', $this->id()); + $html .= sprintf('
', $this->id()); + } + + return $html; + } + + /** + * Sets the internal value to ISO date format. + * + * @param String|Array $val + */ function setValue($val) { - if(is_string($val) && preg_match('/^([\d]{2,4})-([\d]{1,2})-([\d]{1,2})/', $val)) { - $this->value = preg_replace('/^([\d]{2,4})-([\d]{1,2})-([\d]{1,2})/','\\3/\\2/\\1', $val); + if(empty($val)) { + $this->value = null; + $this->valueObj = null; } else { - $this->value = $val; + if($this->getConfig('dmyfields')) { + // Setting in correct locale + if(is_array($val) && $this->validateArrayValue($val)) { + // set() gets confused with custom date formats when using array notation + $this->valueObj = new Zend_Date($val, null, $this->locale); + $this->value = $this->valueObj->toArray(); + } + // load ISO date from database (usually through Form->loadDataForm()) + else if(!empty($val) && Zend_Date::isDate($val, $this->getConfig('datavalueformat'))) { + $this->valueObj = new Zend_Date($val, $this->getConfig('datavalueformat')); + $this->value = $this->valueObj->toArray(); + } + else { + $this->value = $val; + $this->valueObj = null; + } + } else { + // Setting in corect locale. + // Caution: Its important to have this check *before* the ISO date fallback, + // as some dates are falsely detected as ISO by isDate(), e.g. '03/04/03' + // (en_NZ for 3rd of April, definetly not yyyy-MM-dd) + if(!empty($val) && Zend_Date::isDate($val, $this->getConfig('dateformat'), $this->locale)) { + $this->valueObj = new Zend_Date($val, $this->getConfig('dateformat'), $this->locale); + $this->value = $this->valueObj->get($this->getConfig('dateformat')); + } + // load ISO date from database (usually through Form->loadDataForm()) + else if(!empty($val) && Zend_Date::isDate($val, $this->getConfig('datavalueformat'))) { + $this->valueObj = new Zend_Date($val, $this->getConfig('datavalueformat')); + $this->value = $this->valueObj->get($this->getConfig('dateformat')); + } + else { + $this->value = $val; + $this->valueObj = null; + } + } } } + /** + * @return String ISO 8601 date, suitable for insertion into database + */ function dataValue() { - if(is_array($this->value)) { - if(isset($this->value['Year']) && isset($this->value['Month']) && isset($this->value['Day'])) { - return $this->value['Year'] . '-' . $this->value['Month'] . '-' . $this->value['Day']; - } else { - user_error("Bad DateField value " . var_export($this->value,true), E_USER_WARNING); - } - } elseif(preg_match('/^([\d]{1,2})\/([\d]{1,2})\/([\d]{2,4})/', $this->value, $parts)) { - return "$parts[3]-$parts[2]-$parts[1]"; - } elseif(!empty($this->value)) { - return date('Y-m-d', strtotime($this->value)); + if($this->valueObj) { + return $this->valueObj->toString($this->getConfig('datavalueformat')); } else { return null; } } function performReadonlyTransformation() { - $field = new DateField_Disabled($this->name, $this->title, $this->value); + $field = new DateField_Disabled($this->name, $this->title, $this->valueObj); $field->setForm($this->form); $field->readonly = true; + return $field; } function jsValidation() { $formID = $this->form->FormName(); - - if(Validator::get_javascript_validator_handler() == 'none') { - return true; - } - - $error = _t('DateField.VALIDATIONJS', 'Please enter a valid date format (DD/MM/YYYY).'); - $jsFunc =<<showSeparateFields) { + $error = _t('DateField.VALIDATIONJS', 'Please enter a valid date format (DD/MM/YYYY).'); + $error = 'Please enter a valid date format (DD/MM/YYYY) from dmy.'; + $jsFunc =<< 0 && !value.match(/^[0-9]{1,2}\/[0-9]{1,2}\/[0-90-9]{2,4}\$/)) { - validationError(el,"$error","validation",false); + validateDMYDate: function(fieldName) { + var day_value = \$F(_CURRENT_FORM.elements[fieldName+'[day]']); + var month_value = \$F(_CURRENT_FORM.elements[fieldName+'[month]']); + var year_value = \$F(_CURRENT_FORM.elements[fieldName+'[year]']); + + // TODO NZ specific + var value = day_value + '/' + month_value + '/' + year_value; + if(value && value.length > 0 && !value.match(/^[0-9]{1,2}\/[0-9]{1,2}\/([0-9][0-9]){1,2}\$/)) { + validationError(_CURRENT_FORM.elements[fieldName+'[day]'],"$error","validation",false); + return false; } + return true; } } }); JS; - Requirements :: customScript($jsFunc, 'func_validateDate_'.$formID); - -// return "\$('$formID').validateDate('$this->name');"; - return <<name') + \$('$formID').validateDMYDate('$this->name'); + }else{ + \$('$formID').validateDMYDate('$this->name'); + } + } +JS; + } else { + $error = _t('DateField.VALIDATIONJS', 'Please enter a valid date format (DD/MM/YYYY).'); + $jsFunc =<< 0 && !value.match(/^[0-9]{1,2}\/[0-9]{1,2}\/([0-9][0-9]){1,2}\$/)) { + validationError(_CURRENT_FORM.elements[fieldName+'[day]'],"$error","validation",false); + return false; + } + } else { + // single field validation + if(value && value.length > 0 && !value.match(/^[0-9]{1,2}\/[0-9]{1,2}\/[0-90-9]{2,4}\$/)) { + validationError(el,"$error","validation",false); + return false; + } + } + + return true; + } + } +}); +JS; + Requirements :: customScript($jsFunc, 'func_validateDate_'.$formID); + + return <<name') @@ -87,17 +306,45 @@ if(\$('$formID')){ } } JS; + } } + /** + * Validate an array with expected keys 'day', 'month' and 'year. + * Used because Zend_Date::isDate() doesn't provide this. + * + * @param Array $val + * @return boolean + */ + function validateArrayValue($val) { + if(!is_array($val)) return false; + + return ( + array_key_exists('year', $val) + && Zend_Date::isDate($val['year'], 'yyyy', $this->locale) + && array_key_exists('month', $val) + && Zend_Date::isDate($val['month'], 'MM', $this->locale) + && array_key_exists('day', $val) + && Zend_Date::isDate($val['day'], 'dd', $this->locale) + ); + } + + /** + * @return Boolean + */ function validate($validator) { - $validationHandler = Validator::get_javascript_validator_handler(); + $valid = true; - if(!self::$validation_enabled || $validationHandler == 'none') { - return true; + // Don't validate empty fields + if(empty($this->value)) return true; + + // date format + if($this->getConfig('dmyfields')) { + $valid = ($this->validateArrayValue($this->value)); + } else { + $valid = (Zend_Date::isDate($this->value, $this->getConfig('dateformat'), $this->locale)); } - - if(!empty ($this->value) && !preg_match('/^[0-9]{1,2}\/[0-9]{1,2}\/[0-90-9]{2,4}$/', $this->value)) - { + if(!$valid) { $validator->validationError( $this->name, _t('DateField.VALIDDATEFORMAT', "Please enter a valid date format (DD/MM/YYYY)."), @@ -106,8 +353,98 @@ JS; ); return false; } + + // min/max - Assumes that the date value was valid in the first place + if($min = $this->getConfig('min')) { + // ISO or strtotime() + if(Zend_Date::isDate($min, $this->getConfig('datavalueformat'))) { + $minDate = new Zend_Date($min, $this->getConfig('datavalueformat')); + } else { + $minDate = new Zend_Date(strftime('%F', strtotime($min)), $this->getConfig('datavalueformat')); + } + if(!$this->valueObj->isLater($minDate) && !$this->valueObj->equals($minDate)) { + $validator->validationError( + $this->name, + sprintf( + _t('DateField.VALIDDATEMINDATE', "Your date has to be newer or matching the minimum allowed date (%s)"), + $minDate->toString($this->getConfig('dateformat')) + ), + "validation", + false + ); + return false; + } + } + if($max = $this->getConfig('max')) { + // ISO or strtotime() + if(Zend_Date::isDate($min, $this->getConfig('datavalueformat'))) { + $maxDate = new Zend_Date($max, $this->getConfig('datavalueformat')); + } else { + $maxDate = new Zend_Date(strftime('%F', strtotime($max)), $this->getConfig('datavalueformat')); + } + if(!$this->valueObj->isEarlier($maxDate) && !$this->valueObj->equals($maxDate)) { + $validator->validationError( + $this->name, + sprintf( + _t('DateField.VALIDDATEMAXDATE', "Your date has to be older or matching the maximum allowed date (%s)"), + $maxDate->toString($this->getConfig('dateformat')) + ), + "validation", + false + ); + return false; + } + } + return true; } + + /** + * @return string + */ + function getLocale() { + return $this->locale; + } + + /** + * Caution: Will not update the 'dateformat' config value. + * + * @param String $locale + */ + function setLocale($locale) { + $this->locale = $locale; + } + + /** + * @param string $name + * @param mixed $val + */ + function setConfig($name, $val) { + switch($name) { + case 'min': + $format = $this->getConfig('datavalueformat'); + if($val && !Zend_Date::isDate($val, $format) && !strtotime($val)) { + throw new InvalidArgumentException('Date "%s" is not a valid minimum date format (%s) or strtotime() argument', $val, $format); + } + break; + case 'max': + $format = $this->getConfig('datavalueformat'); + if($val && !Zend_Date::isDate($val, $format) && !strtotime($val)) { + throw new InvalidArgumentException('Date "%s" is not a valid maximum date format (%s) or strtotime() argument', $val, $format); + } + break; + } + + $this->config[$name] = $val; + } + + /** + * @param String $name + * @return mixed + */ + function getConfig($name) { + return $this->config[$name]; + } } /** @@ -119,31 +456,21 @@ JS; class DateField_Disabled extends DateField { protected $disabled = true; - - function setValue($val) { - if(is_string($val) && preg_match('/^([\d]{2,4})-([\d]{1,2})-([\d]{1,2})/', $val)) { - $this->value = preg_replace('/^([\d]{2,4})-([\d]{1,2})-([\d]{1,2})/','\\3/\\2/\\1', $val); - } else { - $this->value = $val; - } - } - + function Field() { - if($this->value) { - $df = new Date($this->name); - $df->setValue($this->dataValue()); - - if(date('Y-m-d', time()) == $this->dataValue()) { - $val = Convert::raw2xml($this->value . ' ('._t('DateField.TODAY','today').')'); + if($this->valueObj) { + if($this->valueObj->isToday()) { + $val = Convert::raw2xml($this->valueObj->toString($this->getConfig('dateformat')) . ' ('._t('DateField.TODAY','today').')'); } else { - $val = Convert::raw2xml($this->value . ', ' . $df->Ago()); + $df = new Date($this->name); + $df->setValue($this->dataValue()); + $val = Convert::raw2xml($this->valueObj->toString($this->getConfig('dateformat')) . ', ' . $df->Ago()); } } else { $val = '('._t('DateField.NOTSET', 'not set').')'; } - return "id() . "\">$val - value}\" name=\"$this->name\" />"; + return "id() . "\">$val"; } function Type() { @@ -153,13 +480,10 @@ class DateField_Disabled extends DateField { function jsValidation() { return null; } - - function php() { - return true; - } function validate($validator) { return true; } } + ?> \ No newline at end of file diff --git a/forms/DatetimeField.php b/forms/DatetimeField.php new file mode 100644 index 000000000..e1dad8438 --- /dev/null +++ b/forms/DatetimeField.php @@ -0,0 +1,202 @@ + 'YYYY-MM-dd HH:mm:ss' + ); + + function __construct($name, $title = null, $value = ""){ + $this->dateField = new DateField($name . '[date]', false); + $this->timeField = new TimeField($name . '[time]', false); + + parent::__construct($name, $title, $value); + } + + function setForm($form) { + parent::setForm($form); + + $this->dateField->setForm($form); + $this->timeField->setForm($form); + } + + function Field() { + return $this->dateField->Field() . $this->timeField->Field(); + } + + /** + * Sets the internal value to ISO date format. + * + * @param string|array $val String expects an ISO date format. Array notation with 'date' and 'time' + * keys can contain localized strings. If the 'dmyfields' option is used for {@link DateField}, + * the 'date' value may contain array notation was well (see {@link DateField->setValue()}). + */ + function setValue($val) { + if(empty($val)) { + $this->dateField->setValue(null); + $this->timeField->setValue(null); + } else { + // String setting is only possible from the database, so we don't allow anything but ISO format + if(is_string($val) && Zend_Date::isDate($val, $this->getConfig('datavalueformat'), $this->locale)) { + // split up in date and time string values. + $valueObj = new Zend_Date($val, $this->getConfig('datavalueformat'), $this->locale); + // set date either as array, or as string + if($this->dateField->getConfig('dmyfields')) { + $this->dateField->setValue($valueObj->toArray()); + } else { + $this->dateField->setValue($valueObj->get($this->dateField->getConfig('dateformat'))); + } + // set time + $this->timeField->setValue($valueObj->get($this->timeField->getConfig('timeformat'))); + } + // Setting from form submission + elseif(is_array($val) && array_key_exists('date', $val) && array_key_exists('time', $val)) { + $this->dateField->setValue($val['date']); + $this->timeField->setValue($val['time']); + } else { + $this->dateField->setValue($val); + $this->timeField->setValue($val); + } + } + } + + function dataValue() { + $valDate = $this->dateField->dataValue(); + $valTime = $this->timeField->dataValue(); + + if($valDate && $valTime) { + return $valDate . ' ' . $valTime; + } else { + // TODO + return null; + } + } + + /** + * @return DateField + */ + function getDateField() { + return $this->dateField; + } + + /** + * @return TimeField + */ + function getTimeField() { + return $this->timeField; + } + + function setLocale($locale) { + $this->dateField->setLocale($locale); + $this->timeField->setLocale($locale); + } + + function getLocale() { + return $this->dateField->getLocale(); + } + + /** + * Note: Use {@link getDateField()} and {@link getTimeField()} + * to set field-specific config options. + * + * @param string $name + * @param mixed $val + */ + function setConfig($name, $val) { + $this->config[$name] = $val; + } + + /** + * Note: Use {@link getDateField()} and {@link getTimeField()} + * to get field-specific config options. + * + * @param String $name + * @return mixed + */ + function getConfig($name) { + return $this->config[$name]; + } + + function validate($validator) { + $dateValid = $this->dateField->validate($validator); + $timeValid = $this->timeField->validate($validator); + + return ($dateValid && $timeValid); + } + + function jsValidation() { + return $this->dateField->jsValidation() . $this->timeField->jsValidation(); + } + + function performReadonlyTransformation() { + $field = new DatetimeField_Readonly($this->name, $this->title, $this->dataValue()); + $field->setForm($this->form); + + return $field; + } +} + +/** + * The readonly class for our {@link DatetimeField}. + * + * @package forms + * @subpackage fields-datetime + */ +class DatetimeField_Readonly extends DatetimeField { + + protected $readonly = true; + + function Field() { + $valDate = $this->dateField->dataValue(); + $valTime = $this->timeField->dataValue(); + if($valDate && $valTime) { + $format = $this->dateField->getConfig('dateformat') . ' ' . $this->timeField->getConfig('timeformat'); + $valueObj = new Zend_Date( + $valDate . ' ' . $valTime, + $this->getConfig('datavalueformat'), + $this->dateField->getLocale() + ); + $val = $valueObj->toString($format); + } else { + // TODO Localization + $val = '(not set)'; + } + + return "id() . "\">$val"; + } + + function jsValidation() { + return null; + } + + function validate($validator) { + return true; + } +} +?> \ No newline at end of file diff --git a/forms/DropdownTimeField.php b/forms/DropdownTimeField.php deleted file mode 100644 index cf88e6234..000000000 --- a/forms/DropdownTimeField.php +++ /dev/null @@ -1,39 +0,0 @@ - - - -HTML; - } - - function Field() { - - self::Requirements(); - - $field = parent::Field(); - - $id = $this->id(); - $val = $this->attrValue(); - - $innerHTML = self::HTMLField( $id, $this->name, $val ); - - return << - $innerHTML - -HTML; - } -} \ No newline at end of file diff --git a/forms/FormField.php b/forms/FormField.php index 7647b2c32..08ce37b82 100755 --- a/forms/FormField.php +++ b/forms/FormField.php @@ -75,7 +75,7 @@ class FormField extends RequestHandler { $this->name = $name; $this->title = ($title === null) ? $name : $title; - if(isset($value)) $this->value = $value; + if($value) $this->setValue($value); if($form) $this->setForm($form); parent::__construct(); diff --git a/forms/PopupDateTimeField.php b/forms/PopupDateTimeField.php deleted file mode 100644 index f01477fe3..000000000 --- a/forms/PopupDateTimeField.php +++ /dev/null @@ -1,105 +0,0 @@ -id(); - - $val = $this->attrValue(); - - $date = $this->attrValueDate(); - $time = $this->attrValueTime(); - - $futureClass = $this->futureOnly ? ' futureonly' : ''; - - $innerHTMLDate = sprintf( - '', - $id . '_Date', - $this->name . '[Date]', - $date - ); - $innerHTMLTime = DropdownTimeField::HTMLField( $id . '_Time', $this->name . '[Time]', $time ); - - return << -
    -
  • $innerHTMLDate
  • - -
- -HTML; - } - - function attrValueDate() { - if( $this->value ) - return date( 'd/m/Y', strtotime( $this->value ) ); - else - return ''; - } - - function attrValueTime() { - if( $this->value ) - return date( 'h:i a', strtotime( $this->value ) ); - else - return ''; - } - - function setValue( $val ) { - if( is_array( $val ) ) { - - // 1) Date - - if( $val[ 'Date' ] && preg_match( '/^([\d]{1,2})\/([\d]{1,2})\/([\d]{2,4})/', $val[ 'Date' ], $parts ) ) - $date = "$parts[3]-$parts[2]-$parts[1]"; - else - $date = null; - - // 2) Time - - $time = $val[ 'Time' ] ? date( 'H:i:s', strtotime( $val[ 'Time' ] ) ) : null; - - if( $date == null ) - $this->value = $time; - else if( $time == null ) - $this->value = $date; - else - $this->value = $date . ' ' . $time; - } - else - $this->value = $val; - } - - function dataValue() { - return $this->value; - } -} - -?> \ No newline at end of file diff --git a/forms/TextField.php b/forms/TextField.php index 7850d65a0..62eb48067 100755 --- a/forms/TextField.php +++ b/forms/TextField.php @@ -5,6 +5,10 @@ * @subpackage fields-basic */ class TextField extends FormField { + + /** + * @var Int + */ protected $maxLength; /** @@ -12,9 +16,24 @@ class TextField extends FormField { */ function __construct($name, $title = null, $value = "", $maxLength = null, $form = null){ $this->maxLength = $maxLength; + parent::__construct($name, $title, $value, $form); } + /** + * @param Int $length + */ + function setMaxLength($length) { + $this->maxLength = $length; + } + + /** + * @return Int + */ + function getMaxLength() { + return $this->maxLength; + } + function Field() { $attributes = array( 'type' => 'text', diff --git a/forms/TimeField.php b/forms/TimeField.php index 3ac0eefc0..9a0a3985b 100755 --- a/forms/TimeField.php +++ b/forms/TimeField.php @@ -1,17 +1,52 @@ + * field. Can optionally display a dropdown with predefined time ranges + * through `setConfig('showdropdown', true)`. + * + * # Configuration + * + * - 'timeformat' (string): Time format compatible with Zend_Date. + * Usually set to default format for {@link locale} + * through {@link Zend_Locale_Format::getTimeFormat()}. + * - 'use_strtotime' (boolean): Accept values in PHP's built-in strtotime() notation, in addition + * to the format specified in `timeformat`. Example inputs: 'now', '11pm', '23:59:59'. + * - 'showdropdown': Show a dropdown with suggested date values. + * CAUTION: The dropdown does not support localization. + * + * # Localization + * + * See {@link DateField} + * + * @todo Timezone support + * @todo 'showdropdown' localization * * @package forms * @subpackage fields-datetime */ class TimeField extends TextField { + protected $config = array( + 'showdropdown' => false, + 'timeformat' => null, + 'use_strtotime' => true, + 'datavalueformat' => 'HH:mm:ss' + ); + /** - * @var string $timeformat Time description compatible with date() syntax. + * @var String */ - protected $timeformat = "g:ia"; + protected $locale = null; + /** + * @var Zend_Date Just set if the date is valid. + * {@link $value} will always be set to aid validation, + * and might contain invalid values. + */ + protected $valueObj = null; + /** * Constructor saves the format difference. Timefields shouldn't * have a problem with length as times can only be represented in on way. @@ -19,42 +54,156 @@ class TimeField extends TextField { * @param $name string The name of the field * @param $title string The Title of the field * @param $value string the value for the field - * @param $timeformat string The Time format in date php format e.g. G:ia + * @param $timeformat string The Time format in ISO format (see Zend_Date) */ - function __construct($name, $title = null, $value = "",$timeformat = null){ + function __construct($name, $title = null, $value = "",$timeformat = 'HH:mm:ss'){ + if($timeformat) { + $this->setConfig('timeformat', $timeformat); + } else { + $this->setConfig('timeformat', Zend_Locale_Format::getTimeFormat($this->locale)); + } + parent::__construct($name,$title,$value); + } + + function Field() { + $html = parent::Field(); - if($timeformat) $this->timeformat = $timeformat; + $html = $this->FieldDriver($html); + + return $html; } - function dataValue() { - if($this->value) { - return date($this->timeformat,strtotime($this->value)); - } else { - return $this->value; + /** + * Internal volatile API. + * + * @see DateField->FieldDriver() + */ + protected function FieldDriver($html) { + if($this->getConfig('showdropdown')) { + Requirements::javascript(SAPPHIRE_DIR . '/javascript/TimeField_dropdown.js'); + Requirements::css(SAPPHIRE_DIR . '/css/TimeField_dropdown.css'); + + $html .= sprintf('', $this->id()); + $html .= sprintf('', $this->id()); + $html = ''; } + + return $html; } + /** + * Sets the internal value to ISO date format. + * + * @param String|Array $val + */ function setValue($val) { - if($val) { - $this->value = date($this->timeformat,strtotime($val)); - } else { - $this->value = $val; + // Fuzzy matching through strtotime() to support a wider range of times, + // e.g. 11am. This means that validate() might not fire. + // Note: Time formats are assumed to be less ambiguous than dates across locales. + if($this->getConfig('use_strtotime') && !empty($val)) { + if($parsedTimestamp = strtotime($val)) { + $parsedObj = new Zend_Date($parsedTimestamp, Zend_Date::TIMESTAMP); + $val = $parsedObj->get($this->getConfig('timeformat')); + unset($parsedObj); + } } - + + if(empty($val)) { + $this->value = null; + $this->valueObj = null; + } + // load ISO time from database (usually through Form->loadDataForm()) + else if(Zend_Date::isDate($val, $this->getConfig('datavalueformat'))) { + $this->valueObj = new Zend_Date($val, $this->getConfig('datavalueformat')); + $this->value = $this->valueObj->get($this->getConfig('timeformat')); + } + // Set in current locale (as string) + else if(Zend_Date::isDate($val, $this->getConfig('timeformat'), $this->locale)) { + $this->valueObj = new Zend_Date($val, $this->getConfig('timeformat'), $this->locale); + $this->value = $this->valueObj->get($this->getConfig('timeformat')); + } + // Fallback: Set incorrect value so validate() can pick it up + else { + $this->value = $val; + $this->valueObj = null; + } } + /** + * @return String ISO 8601 date, suitable for insertion into database + */ + function dataValue() { + if($this->valueObj) { + return $this->valueObj->toString($this->getConfig('datavalueformat')); + } else { + return null; + } + } + + /** + * @return Boolean + */ + function validate($validator) { + $valid = true; + + // Don't validate empty fields + if(empty($this->value)) return true; + + if(!Zend_Date::isDate($this->value, $this->getConfig('timeformat'), $this->locale)) { + $validator->validationError( + $this->name, + _t('DateField.VALIDDATEFORMAT', "Please enter a valid time format."), + "validation", + false + ); + return false; + } + return true; + } + + /** + * @return string + */ + function getLocale() { + return $this->locale; + } + + /** + * @param String $locale + */ + function setLocale($locale) { + $this->locale = $locale; + } + + /** + * @param string $name + * @param mixed $val + */ + function setConfig($name, $val) { + $this->config[$name] = $val; + } + + /** + * @param String $name + * @return mixed + */ + function getConfig($name) { + return $this->config[$name]; + } + /** * Creates a new readonly field specified below */ function performReadonlyTransformation() { - return new TimeField_Readonly( $this->name, $this->title, $this->dataValue(),$this->timeformat); + return new TimeField_Readonly($this->name, $this->title, $this->dataValue(), $this->getConfig('timeformat')); } } /** * The readonly class for our {@link TimeField}. + * * @package forms * @subpackage fields-datetime */ @@ -63,11 +212,22 @@ class TimeField_Readonly extends TimeField { protected $readonly = true; function Field() { - if( $this->value ) - $val = $this->attrValue(); - else + if($this->valueObj) { + $val = Convert::raw2xml($this->valueObj->toString($this->getConfig('timeformat'))); + } else { + // TODO Localization $val = '(not set)'; + } return "id() . "\">$val"; } -} \ No newline at end of file + + function jsValidation() { + return null; + } + + function validate($validator) { + return true; + } +} +?> \ No newline at end of file diff --git a/javascript/DropdownTimeField.js b/javascript/TimeField_dropdown.js similarity index 100% rename from javascript/DropdownTimeField.js rename to javascript/TimeField_dropdown.js diff --git a/search/AdvancedSearchForm.php b/search/AdvancedSearchForm.php index 6be5cf12b..074f8b34f 100755 --- a/search/AdvancedSearchForm.php +++ b/search/AdvancedSearchForm.php @@ -32,8 +32,8 @@ class AdvancedSearchForm extends SearchForm { ), $chooseDate = new CompositeField( new HeaderField('LastUpdatedHeader',_t('AdvancedSearchForm.LASTUPDATEDHEADER', 'LAST UPDATED')), - new CompositeDateField("From", _t('AdvancedSearchForm.FROM', 'From')), - new CompositeDateField("To", _t('AdvancedSearchForm.TO', 'To')) + new DateField("From", _t('AdvancedSearchForm.FROM', 'From')), + new DateField("To", _t('AdvancedSearchForm.TO', 'To')) ) ); diff --git a/tests/forms/DateFieldTest.php b/tests/forms/DateFieldTest.php index 161809102..cf0857dbe 100644 --- a/tests/forms/DateFieldTest.php +++ b/tests/forms/DateFieldTest.php @@ -5,34 +5,167 @@ */ class DateFieldTest extends SapphireTest { - function testDMYFormat() { + function setUp() { + parent::setUp(); + + $this->originalLocale = i18n::get_locale(); + i18n::set_locale('en_NZ'); + } + + function tearDown() { + parent::tearDown(); + + i18n::set_locale($this->originalLocale); + } + + function testValidateMinDate() { + $f = new DateField('Date'); + $f->setConfig('min', '2009-03-31'); + $f->setValue('2010-03-31'); + $this->assertTrue($f->validate(new RequiredFields()), 'Date above min date'); + + $f = new DateField('Date'); + $f->setConfig('min', '2009-03-31'); + $f->setValue('1999-03-31'); + $this->assertFalse($f->validate(new RequiredFields()), 'Date below min date'); + + $f = new DateField('Date'); + $f->setConfig('min', '2009-03-31'); + $f->setValue('2009-03-31'); + $this->assertTrue($f->validate(new RequiredFields()), 'Date matching min date'); + } + + function testValidateMinDateStrtotime() { + $f = new DateField('Date'); + $f->setConfig('min', '-7 days'); + $f->setValue(strftime('%F', strtotime('-8 days'))); + $this->assertFalse($f->validate(new RequiredFields()), 'Date below min date, with strtotime'); + + $f = new DateField('Date'); + $f->setConfig('min', '-7 days'); + $f->setValue(strftime('%F', strtotime('-7 days'))); + $this->assertTrue($f->validate(new RequiredFields()), 'Date matching min date, with strtotime'); + } + + function testValidateMaxDateStrtotime() { + $f = new DateField('Date'); + $f->setConfig('max', '7 days'); + $f->setValue(strftime('%F', strtotime('8 days'))); + $this->assertFalse($f->validate(new RequiredFields()), 'Date above max date, with strtotime'); + + $f = new DateField('Date'); + $f->setConfig('max', '7 days'); + $f->setValue(strftime('%F', strtotime('7 days'))); + $this->assertTrue($f->validate(new RequiredFields()), 'Date matching max date, with strtotime'); + } + + function testValidateMaxDate() { + $f = new DateField('Date'); + $f->setConfig('max', '2009-03-31'); + $f->setValue('1999-03-31'); + $this->assertTrue($f->validate(new RequiredFields()), 'Date above min date'); + + $f = new DateField('Date'); + $f->setConfig('max', '2009-03-31'); + $f->setValue('2010-03-31'); + $this->assertFalse($f->validate(new RequiredFields()), 'Date above max date'); + + $f = new DateField('Date'); + $f->setConfig('max', '2009-03-31'); + $f->setValue('2009-03-31'); + $this->assertTrue($f->validate(new RequiredFields()), 'Date matching max date'); + } + + function testConstructorWithoutArgs() { + $f = new DateField('Date'); + $this->assertEquals($f->dataValue(), null); + } + + function testConstructorWithDateString() { + $f = new DateField('Date', 'Date', '29/03/2003'); + $this->assertEquals($f->dataValue(), '2003-03-29'); + } + + function testSetValueWithDateString() { + $f = new DateField('Date', 'Date'); + $f->setValue('29/03/2003'); + $this->assertEquals($f->dataValue(), '2003-03-29'); + } + + function testSetValueWithDateArray() { + $f = new DateField('Date', 'Date'); + $f->setConfig('dmyfields', true); + $f->setValue(array('day' => 29, 'month' => 03, 'year' => 2003)); + $this->assertEquals($f->dataValue(), '2003-03-29'); + } + + function testConstructorWithIsoDate() { + // used by Form->loadDataFrom() + $f = new DateField('Date', 'Date', '2003-03-29'); + $this->assertEquals($f->dataValue(), '2003-03-29'); + } + + function testValidateDMY() { + $f = new DateField('Date', 'Date', '29/03/2003'); + $this->assertTrue($f->validate(new RequiredFields())); + + $f = new DateField('Date', 'Date', 'wrong'); + $this->assertFalse($f->validate(new RequiredFields())); + } + + function testValidateArray() { + $f = new DateField('Date', 'Date'); + $f->setConfig('dmyfields', true); + $f->setValue(array('day' => 29, 'month' => 03, 'year' => 2003)); + $this->assertTrue($f->validate(new RequiredFields())); + + // TODO Fix array validation + // $f = new DateField('Date', 'Date', array('day' => 9999, 'month' => 9999, 'year' => 9999)); + // $this->assertFalse($f->validate(new RequiredFields())); + } + + function testValidateArrayValue() { + $f = new DateField('Date', 'Date'); + $this->assertTrue($f->validateArrayValue(array('day' => 29, 'month' => 03, 'year' => 2003))); + $this->assertFalse($f->validateArrayValue(array('month' => 03, 'year' => 2003))); + $this->assertFalse($f->validateArrayValue(array('day' => 99, 'month' => 99, 'year' => 2003))); + } + + function testFormatEnNz() { /* We get YYYY-MM-DD format as the data value for DD/MM/YYYY input value */ - $dateField = new DateField('Date', 'Date', '04/03/2003'); - $this->assertEquals($dateField->dataValue(), '2003-03-04'); - - /* Even if value hasn't got leading 0's in it we still get the correct data value */ - $dateField2 = new DateField('Date', 'Date', '4/3/03'); - $this->assertEquals($dateField2->dataValue(), '03-3-4'); + $f = new DateField('Date', 'Date', '29/03/2003'); + $this->assertEquals($f->dataValue(), '2003-03-29'); } - function testYMDFormat() { - /* We get YYYY-MM-DD format as the data value for YYYY-MM-DD input value */ - $dateField = new DateField('Date', 'Date', '2003/03/04'); - $this->assertEquals($dateField->dataValue(), '2003-03-04'); - - /* Even if input value hasn't got leading 0's in it we still get the correct data value */ - $dateField2 = new DateField('Date', 'Date', '2003/3/4'); - $this->assertEquals($dateField2->dataValue(), '2003-03-04'); + function testSetLocale() { + // should get en_NZ by default through setUp() + $f = new DateField('Date', 'Date', '29/03/2003'); + $f->setLocale('de_DE'); + $f->setValue('29.06.2006'); + $this->assertEquals($f->dataValue(), '2006-06-29'); } - + + /** + * Note: This is mostly tested for legacy reasons + */ function testMDYFormat() { - /* We get MM-DD-YYYY format as the data value for YYYY-MM-DD input value */ - $dateField = new DateField('Date', 'Date', '03/04/2003'); - $this->assertEquals($dateField->dataValue(), '2003-04-03'); - - /* Even if input value hasn't got leading 0's in it we still get the correct data value */ - $dateField2 = new DateField('Date', 'Date', '3/4/03'); - $this->assertEquals($dateField2->dataValue(), '03-4-3'); + $dateField = new DateField('Date', 'Date'); + $dateField->setConfig('dateformat', 'd/M/Y'); + $dateField->setValue('31/03/2003'); + $this->assertEquals( + $dateField->dataValue(), + '2003-03-31', + "We get MM-DD-YYYY format as the data value for YYYY-MM-DD input value" + ); + + $dateField2 = new DateField('Date', 'Date'); + $dateField2->setConfig('dateformat', 'd/M/Y'); + $dateField2->setValue('04/3/03'); + $this->assertEquals( + $dateField2->dataValue(), + '2003-03-04', + "Even if input value hasn't got leading 0's in it we still get the correct data value" + ); } } diff --git a/tests/forms/DatetimeFieldTest.php b/tests/forms/DatetimeFieldTest.php new file mode 100644 index 000000000..31adbf411 --- /dev/null +++ b/tests/forms/DatetimeFieldTest.php @@ -0,0 +1,80 @@ +originalLocale = i18n::get_locale(); + i18n::set_locale('en_NZ'); + } + + function tearDown() { + parent::tearDown(); + + i18n::set_locale($this->originalLocale); + } + + function testConstructorWithoutArgs() { + $f = new DatetimeField('Datetime'); + $this->assertEquals($f->dataValue(), null); + } + + // /** + // * @expectedException InvalidArgumentException + // */ + // function testConstructorWithLocalizedDateString() { + // $f = new DatetimeField('Datetime', 'Datetime', '29/03/2003 23:59:38'); + // } + + function testConstructorWithIsoDate() { + // used by Form->loadDataFrom() + $f = new DatetimeField('Datetime', 'Datetime', '2003-03-29 23:59:38'); + $this->assertEquals($f->dataValue(), '2003-03-29 23:59:38'); + } + + // /** + // * @expectedException InvalidArgumentException + // */ + // function testSetValueWithDateString() { + // $f = new DatetimeField('Datetime', 'Datetime'); + // $f->setValue('29/03/2003'); + // } + + function testSetValueWithDateTimeString() { + $f = new DatetimeField('Datetime', 'Datetime'); + $f->setValue('2003-03-29 23:59:38'); + $this->assertEquals($f->dataValue(), '2003-03-29 23:59:38'); + } + + function testSetValueWithArray() { + $f = new DatetimeField('Datetime', 'Datetime'); + // Values can only be localized (= non-ISO) in array notation + $f->setValue(array( + 'date' => '29/03/2003', + 'time' => '11pm' + )); + $this->assertEquals($f->dataValue(), '2003-03-29 23:00:00'); + } + + function testSetValueWithDmyArray() { + $f = new DatetimeField('Datetime', 'Datetime'); + $f->getDateField()->setConfig('dmyfields', true); + $f->setValue(array( + 'date' => array('day' => 29, 'month' => 03, 'year' => 2003), + 'time' => '11pm' + )); + $this->assertEquals($f->dataValue(), '2003-03-29 23:00:00'); + } + + function testValidate() { + $f = new DatetimeField('Datetime', 'Datetime', '2003-03-29 23:59:38'); + $this->assertTrue($f->validate(new RequiredFields())); + + $f = new DatetimeField('Datetime', 'Datetime', 'wrong'); + $this->assertFalse($f->validate(new RequiredFields())); + } +} \ No newline at end of file diff --git a/tests/forms/TimeFieldTest.php b/tests/forms/TimeFieldTest.php index fe9c69b02..37bfaeafe 100644 --- a/tests/forms/TimeFieldTest.php +++ b/tests/forms/TimeFieldTest.php @@ -4,38 +4,91 @@ * @subpackage tests */ class TimeFieldTest extends SapphireTest { - function testDataValue12h() { - $field12h = new TimeField('Time', 'Time'); + + function setUp() { + parent::setUp(); - $field12h->setValue('11pm'); - $this->assertEquals($field12h->dataValue(), '11:00pm'); - - $field12h->setValue('23:59'); - $this->assertEquals($field12h->dataValue(), '11:59pm'); - - $field12h->setValue('11:59pm'); - $this->assertEquals($field12h->dataValue(), '11:59pm'); - - $field12h->setValue('11:59 pm'); - $this->assertEquals($field12h->dataValue(), '11:59pm'); + $this->originalLocale = i18n::get_locale(); + i18n::set_locale('en_NZ'); } - function testDataValue24h() { - $field24h = new TimeField('Time', 'Time', null, 'H:i'); + function tearDown() { + parent::tearDown(); - $field24h->setValue('11pm'); - $this->assertEquals($field24h->dataValue(), '23:00'); - - $field24h->setValue('23:59'); - $this->assertEquals($field24h->dataValue(), '23:59'); - - $field24h->setValue('11:59pm'); - $this->assertEquals($field24h->dataValue(), '23:59'); - - $field24h->setValue('11:59 pm'); - $this->assertEquals($field24h->dataValue(), '23:59'); + i18n::set_locale($this->originalLocale); } + function testConstructorWithoutArgs() { + $f = new TimeField('Time'); + $this->assertEquals($f->dataValue(), null); + } + + function testConstructorWithString() { + $f = new TimeField('Time', 'Time', '23:00:00'); + $this->assertEquals($f->dataValue(), '23:00:00'); + } + + function testValidate() { + $f = new TimeField('Time', 'Time', '11pm'); + $this->assertTrue($f->validate(new RequiredFields())); + + $f = new TimeField('Time', 'Time', '23:59'); + $this->assertTrue($f->validate(new RequiredFields())); + + $f = new TimeField('Time', 'Time', 'wrong'); + $this->assertFalse($f->validate(new RequiredFields())); + } + + function testSetLocale() { + // should get en_NZ by default through setUp() + $f = new TimeField('Time', 'Time'); + $f->setLocale('de_DE'); + // TODO Find a hour format thats actually different + $f->setValue('23:59'); + $this->assertEquals($f->dataValue(), '23:59:00'); + } + + function testSetValueWithUseStrToTime() { + $f = new TimeField('Time', 'Time'); + $f->setValue('11pm'); + $this->assertEquals($f->dataValue(), '23:00:00', + 'Setting value to "11pm" parses with use_strtotime enabled' + ); + $this->assertTrue($f->validate(new RequiredFields())); + + $f = new TimeField('Time', 'Time'); + $f->setConfig('use_strtotime', false); + $f->setValue('11pm'); + $this->assertEquals($f->dataValue(), null, + 'Setting value to "11pm" parses with use_strtotime enabled' + ); + $this->assertFalse($f->validate(new RequiredFields())); + + $f = new TimeField('Time', 'Time'); + $f->setValue('11pm'); + $this->assertEquals($f->dataValue(), '23:00:00'); + + $f = new TimeField('Time', 'Time'); + $f->setValue('11:59pm'); + $this->assertEquals($f->dataValue(), '23:59:00'); + + $f = new TimeField('Time', 'Time'); + $f->setValue('11:59 pm'); + $this->assertEquals($f->dataValue(), '23:59:00'); + + $f = new TimeField('Time', 'Time'); + $f->setValue('11:59:38 pm'); + $this->assertEquals($f->dataValue(), '23:59:38'); + + $f = new TimeField('Time', 'Time'); + $f->setValue('23:59'); + $this->assertEquals($f->dataValue(), '23:59:00'); + + $f = new TimeField('Time', 'Time'); + $f->setValue('23:59:38'); + $this->assertEquals($f->dataValue(), '23:59:38'); + } + function testOverrideWithNull() { $field = new TimeField('Time', 'Time');