| 1 | <?php |
|---|
| 2 | |
|---|
| 3 | // +---------------------------------------------------------------------------+ |
|---|
| 4 | // | This file is part of the Agavi package. | |
|---|
| 5 | // | Copyright (c) 2005-2008 the Agavi Project. | |
|---|
| 6 | // | | |
|---|
| 7 | // | For the full copyright and license information, please view the LICENSE | |
|---|
| 8 | // | file that was distributed with this source code. You can also view the | |
|---|
| 9 | // | LICENSE file online at http://www.agavi.org/LICENSE.txt | |
|---|
| 10 | // | vi: set noexpandtab: | |
|---|
| 11 | // | Local Variables: | |
|---|
| 12 | // | indent-tabs-mode: t | |
|---|
| 13 | // | End: | |
|---|
| 14 | // +---------------------------------------------------------------------------+ |
|---|
| 15 | |
|---|
| 16 | /** |
|---|
| 17 | * AgaviDateTimeValidator verifies that a parameter is of a date and or time |
|---|
| 18 | * format. |
|---|
| 19 | * |
|---|
| 20 | * Arguments: |
|---|
| 21 | * This can be: |
|---|
| 22 | * * a single argument which will then be parsed with the formats in the |
|---|
| 23 | * 'formats' parameter. |
|---|
| 24 | * * multiple arguments with the calendar constants |
|---|
| 25 | * (AgaviDateDefinitions::MONTH, etc) as key and the argument field as |
|---|
| 26 | * value. |
|---|
| 27 | * * multiple arguments and the 'arguments_format' parameter defined. This |
|---|
| 28 | * will use the string in 'arguments_format' as input string to sprintf and |
|---|
| 29 | * will use the arguments in the given order as argument to sprintf. |
|---|
| 30 | * |
|---|
| 31 | * Parameters: |
|---|
| 32 | * 'check' check date if the specified day really exists |
|---|
| 33 | * 'formats' an array of arrays with these keys: |
|---|
| 34 | * 'type' The type of the string in 'format'. |
|---|
| 35 | * 'format' The input string dependent on the type. These types are |
|---|
| 36 | * allowed: |
|---|
| 37 | * format: The value is a date format string. |
|---|
| 38 | * time: The value is a time specifier (full,...) or null |
|---|
| 39 | * date: The value is a date specifier or null |
|---|
| 40 | * datetime: The value is a date specifier or null |
|---|
| 41 | * translation_domain: The value will be translated in the |
|---|
| 42 | * domain given in the 'translation_domain' key. |
|---|
| 43 | * |
|---|
| 44 | * 'locale' The optional locale which will be used for this format. |
|---|
| 45 | * 'translation_domain' Only applicable when the type is translation_domain |
|---|
| 46 | * 'cast_to' Only useful in combination with the export parameter. |
|---|
| 47 | * This can either be a string or an array. If its an string it |
|---|
| 48 | * can be one of 'unix' (converts the date to a unix timestamp), |
|---|
| 49 | * 'string' (converts it to a string using the default format), |
|---|
| 50 | * 'calendar' (will return the AgaviCalendar object), |
|---|
| 51 | * 'datetime' (case sensitive, will return a PHP DateTime |
|---|
| 52 | * object, requires PHP 5.1.x with DateTime explicitly enabled |
|---|
| 53 | * or >= PHP 5.2). |
|---|
| 54 | * If it's an array it can have these keys: |
|---|
| 55 | * 'type' The type of the format (format, time, date, datetime) |
|---|
| 56 | * 'format' see in 'formats' above. |
|---|
| 57 | * 'arguments_format' A string which will be used as the format string for |
|---|
| 58 | * sprintf. |
|---|
| 59 | * 'min' Either an string or an array. When its a string the the |
|---|
| 60 | * its assumed to be in the format 'yyyy-MM-dd[ HH:mm:ss[.S]]'. |
|---|
| 61 | * When its an array it will take the minimum value from a |
|---|
| 62 | * request field. These indizes apply: |
|---|
| 63 | * 'format' A custom format string which should be used when the field |
|---|
| 64 | * is an string. |
|---|
| 65 | * 'field' The name of the field to use as minimum value (could be a |
|---|
| 66 | * previous exported calendar object). Do NOT use unvalidated |
|---|
| 67 | * fields here. Lax parsing will be used. |
|---|
| 68 | * This value is inclusive. |
|---|
| 69 | * 'max' The same as min except that the max is exclusive. |
|---|
| 70 | * |
|---|
| 71 | * @package agavi |
|---|
| 72 | * @subpackage validator |
|---|
| 73 | * |
|---|
| 74 | * @author Dominik del Bondio <ddb@bitxtender.com> |
|---|
| 75 | * @copyright Authors |
|---|
| 76 | * @copyright The Agavi Project |
|---|
| 77 | * |
|---|
| 78 | * @since 0.11.0 |
|---|
| 79 | * |
|---|
| 80 | * @version $Id$ |
|---|
| 81 | */ |
|---|
| 82 | class AgaviDateTimeValidator extends AgaviValidator |
|---|
| 83 | { |
|---|
| 84 | /** |
|---|
| 85 | * Validates the input. |
|---|
| 86 | * |
|---|
| 87 | * @return bool True if the input was a valid date. |
|---|
| 88 | * |
|---|
| 89 | * @author Dominik del Bondio <ddb@bitxtender.com> |
|---|
| 90 | * @since 0.11.0 |
|---|
| 91 | */ |
|---|
| 92 | protected function validate() |
|---|
| 93 | { |
|---|
| 94 | if(!AgaviConfig::get('core.use_translation')) { |
|---|
| 95 | throw new AgaviConfigurationException('The datetime validator can only be used with use_translation on'); |
|---|
| 96 | } |
|---|
| 97 | $tm = $this->getContext()->getTranslationManager(); |
|---|
| 98 | $cal = null; |
|---|
| 99 | |
|---|
| 100 | $check = $this->getParameter('check', true); |
|---|
| 101 | $locale = $this->hasParameter('locale') ? $tm->getLocale($this->getParameter('locale')) : $tm->getCurrentLocale(); |
|---|
| 102 | |
|---|
| 103 | if($this->hasMultipleArguments() && !$this->getParameter('arguments_format')) { |
|---|
| 104 | $cal = $tm->createCalendar(); |
|---|
| 105 | $cal->clear(); |
|---|
| 106 | $cal->setLenient(!$check); |
|---|
| 107 | foreach($this->getArguments() as $calField => $field) { |
|---|
| 108 | $param = $this->getData($field); |
|---|
| 109 | if(defined($calField)) { |
|---|
| 110 | $calField = constant($calField); |
|---|
| 111 | } elseif(!is_numeric($calField)) { |
|---|
| 112 | throw new AgaviValidatorException('Unknown argument name "' . $calField . '" for argument "' . $field . '" supplied. This needs to be one of the constants defined in AgaviDateDefinitions.'); |
|---|
| 113 | } |
|---|
| 114 | |
|---|
| 115 | if($calField == AgaviDateDefinitions::MONTH) { |
|---|
| 116 | $param -= 1; |
|---|
| 117 | } |
|---|
| 118 | |
|---|
| 119 | $cal->set($calField, (float) $param); |
|---|
| 120 | } |
|---|
| 121 | |
|---|
| 122 | try { |
|---|
| 123 | $cal->getTime(); |
|---|
| 124 | } catch(Exception $e) { |
|---|
| 125 | $this->throwError('check'); |
|---|
| 126 | return false; |
|---|
| 127 | } |
|---|
| 128 | } else { |
|---|
| 129 | if($argFormat = $this->getParameter('arguments_format')) { |
|---|
| 130 | $values = array(); |
|---|
| 131 | foreach($this->getArguments() as $field) { |
|---|
| 132 | $values[] = $this->getData($field); |
|---|
| 133 | } |
|---|
| 134 | $param = vsprintf($argFormat, $values); |
|---|
| 135 | } else { |
|---|
| 136 | $param = $this->getData($this->getArgument()); |
|---|
| 137 | } |
|---|
| 138 | |
|---|
| 139 | $matchedFormat = false; |
|---|
| 140 | foreach((array)$this->getParameter('formats', array()) as $key => $item) { |
|---|
| 141 | if(!is_array($item)) { |
|---|
| 142 | $item = array((is_int($key) ? 'format' : $key) => $item); |
|---|
| 143 | } |
|---|
| 144 | |
|---|
| 145 | $itemLocale = empty($item['locale']) ? $locale : $tm->getLocale($item['locale']); |
|---|
| 146 | $type = empty($item['type']) ? 'format' : $item['type']; |
|---|
| 147 | |
|---|
| 148 | if($type == 'format') { |
|---|
| 149 | $formatString = $item['format']; |
|---|
| 150 | } elseif($type == 'time' || $type == 'date' || $type == 'datetime') { |
|---|
| 151 | $format = isset($item['format']) ? $item['format'] : null; |
|---|
| 152 | $formatString = AgaviDateFormatter::resolveFormat($format, $itemLocale, $type); |
|---|
| 153 | } elseif($type == 'translation_domain') { |
|---|
| 154 | $td = $item['translation_domain']; |
|---|
| 155 | $formatString = $tm->_($item['format'], $td, $itemLocale); |
|---|
| 156 | } |
|---|
| 157 | |
|---|
| 158 | try { |
|---|
| 159 | $format = new AgaviDateFormat($formatString); |
|---|
| 160 | $cal = $format->parse($param, $itemLocale, $check); |
|---|
| 161 | |
|---|
| 162 | // no exception got thrown so the parsing was successful |
|---|
| 163 | $matchedFormat = true; |
|---|
| 164 | break; |
|---|
| 165 | } catch(Exception $e) { |
|---|
| 166 | // nop |
|---|
| 167 | } |
|---|
| 168 | } |
|---|
| 169 | |
|---|
| 170 | if(!$matchedFormat) { |
|---|
| 171 | $this->throwError('format'); |
|---|
| 172 | return false; |
|---|
| 173 | } |
|---|
| 174 | } |
|---|
| 175 | |
|---|
| 176 | $cal->setLenient(true); |
|---|
| 177 | $value = $cal; |
|---|
| 178 | |
|---|
| 179 | if($cast = $this->getParameter('cast_to')) { |
|---|
| 180 | // an array means the user wants it custom formatted |
|---|
| 181 | if(is_array($cast)) { |
|---|
| 182 | $type = empty($cast['type']) ? 'format' : $cast['type']; |
|---|
| 183 | if($type == 'format') { |
|---|
| 184 | $formatString = $cast['format']; |
|---|
| 185 | } elseif($type == 'time' || $type == 'date' || $type == 'datetime') { |
|---|
| 186 | $format = isset($cast['format']) ? $cast['format'] : null; |
|---|
| 187 | $formatString = AgaviDateFormatter::resolveFormat($format, $locale, $type); |
|---|
| 188 | } |
|---|
| 189 | |
|---|
| 190 | $format = new AgaviDateFormat($formatString); |
|---|
| 191 | $value = $format->format($cal, $cal->getType(), $locale); |
|---|
| 192 | } else { |
|---|
| 193 | $cast = strtolower($cast); |
|---|
| 194 | if($cast == 'unix') { |
|---|
| 195 | $value = $cal->getUnixTimestamp(); |
|---|
| 196 | } elseif($cast == 'string') { |
|---|
| 197 | $value = $tm->_d($cal); |
|---|
| 198 | } elseif($cast == 'datetime') { |
|---|
| 199 | $value = $cal->getNativeDateTime(); |
|---|
| 200 | } else { |
|---|
| 201 | $value = $cal; |
|---|
| 202 | } |
|---|
| 203 | } |
|---|
| 204 | } |
|---|
| 205 | |
|---|
| 206 | $defaultParseFormat = new AgaviDateFormat('yyyy-MM-dd HH:mm:ss.S'); |
|---|
| 207 | |
|---|
| 208 | if($this->hasParameter('min')) { |
|---|
| 209 | $min = $this->getMinOrMaxValue('min', $defaultParseFormat, $locale); |
|---|
| 210 | |
|---|
| 211 | $isAfterEqual = $cal->after($min) || $cal->equals($min); |
|---|
| 212 | if(!$isAfterEqual) { |
|---|
| 213 | $this->throwError('min'); |
|---|
| 214 | return false; |
|---|
| 215 | } |
|---|
| 216 | } |
|---|
| 217 | |
|---|
| 218 | if($this->hasParameter('max')) { |
|---|
| 219 | $max = $this->getMinOrMaxValue('max', $defaultParseFormat, $locale); |
|---|
| 220 | |
|---|
| 221 | $isBefore = $cal->before($max); |
|---|
| 222 | if(!$isBefore) { |
|---|
| 223 | $this->throwError('max'); |
|---|
| 224 | return false; |
|---|
| 225 | } |
|---|
| 226 | } |
|---|
| 227 | |
|---|
| 228 | if($this->hasParameter('export')) { |
|---|
| 229 | $export = $this->getParameter('export'); |
|---|
| 230 | if(is_string($export)) { |
|---|
| 231 | $this->export($value); |
|---|
| 232 | } elseif(is_array($export)) { |
|---|
| 233 | foreach($export as $calField => $field) { |
|---|
| 234 | if(defined($calField)) { |
|---|
| 235 | $this->export($cal->get(constant($calField)), $field); |
|---|
| 236 | } |
|---|
| 237 | } |
|---|
| 238 | } |
|---|
| 239 | } |
|---|
| 240 | |
|---|
| 241 | return true; |
|---|
| 242 | } |
|---|
| 243 | |
|---|
| 244 | /** |
|---|
| 245 | * Returns the calendar object for a max or min definition. |
|---|
| 246 | * |
|---|
| 247 | * @param string 'min' or 'max' |
|---|
| 248 | * @param AgaviDateFormat The default format when parsing strings. |
|---|
| 249 | * @param AgaviLocale The locale to use. |
|---|
| 250 | * |
|---|
| 251 | * @return AgaviCalendar The calendar object storing the date. |
|---|
| 252 | * |
|---|
| 253 | * @author Dominik del Bondio <ddb@bitxtender.com> |
|---|
| 254 | * @since 0.11.0 |
|---|
| 255 | */ |
|---|
| 256 | protected function getMinOrMaxValue($minMax, $defaultParseFormat, $locale) |
|---|
| 257 | { |
|---|
| 258 | $format = $defaultParseFormat; |
|---|
| 259 | |
|---|
| 260 | $minMax = $this->getParameter($minMax); |
|---|
| 261 | if(is_array($minMax)) { |
|---|
| 262 | $minMaxValue = $this->validationParameters->getParameter($minMax['field']); |
|---|
| 263 | if(!$minMaxValue instanceof AgaviCalendar) { |
|---|
| 264 | if(isset($minMax['format'])) { |
|---|
| 265 | $format = new AgaviDateFormat($minMax['format']); |
|---|
| 266 | } |
|---|
| 267 | $result = $format->parse($minMaxValue, $locale, false); |
|---|
| 268 | } else { |
|---|
| 269 | $result = $minMaxValue; |
|---|
| 270 | } |
|---|
| 271 | } else { |
|---|
| 272 | $result = $format->parse($minMax, $locale, false); |
|---|
| 273 | } |
|---|
| 274 | |
|---|
| 275 | return $result; |
|---|
| 276 | } |
|---|
| 277 | |
|---|
| 278 | } |
|---|
| 279 | |
|---|
| 280 | ?> |
|---|