Completed
Push — master ( a45657...918fb9 )
by Daniel
13:32
created

DateField::Field()   C

Complexity

Conditions 13
Paths 36

Size

Total Lines 62
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 41
nc 36
nop 1
dl 0
loc 62
rs 6.1884
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 63 and the first side effect is on line 7.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
3
4
use SilverStripe\ORM\FieldType\DBDate;
5
6
7
require_once 'Zend/Date.php';
8
9
/**
10
 * Form field to display an editable date string,
11
 * either in a single `<input type="text">` field,
12
 * or in three separate fields for day, month and year.
13
 *
14
 * # Configuration
15
 *
16
 * - 'showcalendar' (boolean): Determines if a calendar picker is shown.
17
 *    By default, jQuery UI datepicker is used (see {@link DateField_View_JQuery}).
18
 * - 'jslocale' (string): Overwrites the "Locale" value set in this class.
19
 *    Only useful in combination with {@link DateField_View_JQuery}.
20
 * - 'dmyfields' (boolean): Show three input fields for day, month and year separately.
21
 *    CAUTION: Might not be useable in combination with 'showcalendar', depending on the used javascript library
22
 * - 'dmyseparator' (string): HTML markup to separate day, month and year fields.
23
 *    Only applicable with 'dmyfields'=TRUE. Use 'dateformat' to influence date representation with 'dmyfields'=FALSE.
24
 * - 'dmyplaceholders': Show HTML5 placehoder text to allow identification of the three separate input fields
25
 * - 'dateformat' (string): Date format compatible with Zend_Date.
26
 *    Usually set to default format for {@link locale} through {@link Zend_Locale_Format::getDateFormat()}.
27
 * - 'datavalueformat' (string): Internal ISO format string used by {@link dataValue()} to save the
28
 *    date to a database.
29
 * - 'min' (string): Minimum allowed date value (in ISO format, or strtotime() compatible).
30
 *    Example: '2010-03-31', or '-7 days'
31
 * - 'max' (string): Maximum allowed date value (in ISO format, or strtotime() compatible).
32
 *    Example: '2010-03-31', or '1 year'
33
 *
34
 * Depending which UI helper is used, further namespaced configuration options are available.
35
 * For the default jQuery UI, all options prefixed/namespaced with "jQueryUI." will be respected as well.
36
 * Example: <code>$myDateField->setConfig('jQueryUI.showWeek', true);</code>
37
 * See http://docs.jquery.com/UI/Datepicker for details.
38
 *
39
 * # Localization
40
 *
41
 * The field will get its default locale from {@link i18n::get_locale()}, and set the `dateformat`
42
 * configuration accordingly. Changing the locale through {@link setLocale()} will not update the
43
 * `dateformat` configuration automatically.
44
 *
45
 * See http://doc.silverstripe.org/framework/en/topics/i18n for more information about localizing form fields.
46
 *
47
 * # Usage
48
 *
49
 * ## Example: German dates with separate fields for day, month, year
50
 *
51
 *   $f = new DateField('MyDate');
52
 *   $f->setLocale('de_DE');
53
 *   $f->setConfig('dmyfields', true);
54
 *
55
 * # Validation
56
 *
57
 * Caution: JavaScript validation is only supported for the 'en_NZ' locale at the moment,
58
 * it will be disabled automatically for all other locales.
59
 *
60
 * @package forms
61
 * @subpackage fields-datetime
62
 */
63
class DateField extends TextField {
64
65
	protected $schemaDataType = FormField::SCHEMA_DATA_TYPE_DATE;
66
67
	/**
68
	 * @config
69
	 * @var array
70
	 */
71
	private static $default_config = array(
72
		'showcalendar' => false,
73
		'jslocale' => null,
74
		'dmyfields' => false,
75
		'dmyseparator' => '&nbsp;<span class="separator">/</span>&nbsp;',
76
		'dmyplaceholders' => true,
77
		'dateformat' => null,
78
		'datavalueformat' => 'yyyy-MM-dd',
79
		'min' => null,
80
		'max' => null,
81
	);
82
83
	/**
84
	 * @var array
85
	 */
86
	protected $config;
87
88
	/**
89
	 * @var String
90
	 */
91
	protected $locale = null;
92
93
	/**
94
	 * @var Zend_Date Just set if the date is valid.
95
	 * {@link $value} will always be set to aid validation,
96
	 * and might contain invalid values.
97
	 */
98
	protected $valueObj = null;
99
100
	public function __construct($name, $title = null, $value = null) {
101
		if(!$this->locale) {
102
			$this->locale = i18n::get_locale();
103
		}
104
105
		$this->config = $this->config()->default_config;
0 ignored issues
show
Documentation introduced by
The property default_config does not exist on object<Config_ForClass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
106
		if(!$this->getConfig('dateformat')) {
107
			$this->setConfig('dateformat', Config::inst()->get('i18n', 'date_format'));
108
		}
109
110
		foreach ($this->config()->default_config AS $defaultK => $defaultV) {
111
			if ($defaultV) {
112
				if ($defaultK=='locale')
113
					$this->locale = $defaultV;
114
				else
115
					$this->setConfig($defaultK, $defaultV);
116
			}
117
		}
118
119
		parent::__construct($name, $title, $value);
120
	}
121
122
	public function FieldHolder($properties = array()) {
123
		if ($this->getConfig('showcalendar')) {
124
			// TODO Replace with properly extensible view helper system
125
			$d = DateField_View_JQuery::create($this);
126
			if(!$d->regionalSettingsExist()) {
127
				$dateformat = $this->getConfig('dateformat');
128
129
				// if no localefile is present, the jQuery DatePicker
130
				// month- and daynames will default to English, so the date
131
				// will not pass Zend validatiobn. We provide a fallback
132
				if (preg_match('/(MMM+)|(EEE+)/', $dateformat)) {
133
					$this->setConfig('dateformat', $this->getConfig('datavalueformat'));
134
				}
135
			}
136
			$d->onBeforeRender();
137
		}
138
		$html = parent::FieldHolder();
139
140
		if(!empty($d)) {
141
			$html = $d->onAfterRender($html);
142
		}
143
		return $html;
144
	}
145
146
	function SmallFieldHolder($properties = array()){
147
		$d = DateField_View_JQuery::create($this);
148
		$d->onBeforeRender();
149
		$html = parent::SmallFieldHolder($properties);
150
		$html = $d->onAfterRender($html);
151
		return $html;
152
	}
153
154
	public function Field($properties = array()) {
155
		$config = array(
156
			'showcalendar' => $this->getConfig('showcalendar'),
157
			'isoDateformat' => $this->getConfig('dateformat'),
158
			'jquerydateformat' => DateField_View_JQuery::convert_iso_to_jquery_format($this->getConfig('dateformat')),
159
			'min' => $this->getConfig('min'),
160
			'max' => $this->getConfig('max')
161
		);
162
163
		// Add other jQuery UI specific, namespaced options (only serializable, no callbacks etc.)
164
		// TODO Move to DateField_View_jQuery once we have a properly extensible HTML5 attribute system for FormField
165
		$jqueryUIConfig = array();
166
		foreach($this->getConfig() as $k => $v) {
167
			if(preg_match('/^jQueryUI\.(.*)/', $k, $matches)) $jqueryUIConfig[$matches[1]] = $v;
168
		}
169
		if ($jqueryUIConfig)
0 ignored issues
show
Bug Best Practice introduced by
The expression $jqueryUIConfig of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
170
			$config['jqueryuiconfig'] =  Convert::array2json(array_filter($jqueryUIConfig));
171
		$config = array_filter($config);
172
		foreach($config as $k => $v) $this->setAttribute('data-' . $k, $v);
173
174
		// Three separate fields for day, month and year
175
		if($this->getConfig('dmyfields')) {
176
			// values
177
			$valArr = ($this->valueObj) ? $this->valueObj->toArray() : null;
178
179
			// fields
180
			$fieldNames = Zend_Locale::getTranslationList('Field', $this->locale);
181
			$fieldDay = NumericField::create($this->name . '[day]', false, ($valArr) ? $valArr['day'] : null)
182
				->addExtraClass('day')
183
				->setAttribute('placeholder', $this->getConfig('dmyplaceholders') ? $fieldNames['day'] : null)
184
				->setMaxLength(2);
185
186
			$fieldMonth = NumericField::create($this->name . '[month]', false, ($valArr) ? $valArr['month'] : null)
187
				->addExtraClass('month')
188
				->setAttribute('placeholder', $this->getConfig('dmyplaceholders') ? $fieldNames['month'] : null)
189
				->setMaxLength(2);
190
191
			$fieldYear = NumericField::create($this->name . '[year]', false, ($valArr) ? $valArr['year'] : null)
192
				->addExtraClass('year')
193
				->setAttribute('placeholder', $this->getConfig('dmyplaceholders') ? $fieldNames['year'] : null)
194
				->setMaxLength(4);
195
196
			// order fields depending on format
197
			$sep = $this->getConfig('dmyseparator');
198
			$format = $this->getConfig('dateformat');
199
			$fields = array();
200
			$fields[stripos($format, 'd')] = $fieldDay->Field();
201
			$fields[stripos($format, 'm')] = $fieldMonth->Field();
202
			$fields[stripos($format, 'y')] = $fieldYear->Field();
203
			ksort($fields);
204
			$html = implode($sep, $fields);
205
206
			// dmyfields doesn't work with showcalendar
207
			$this->setConfig('showcalendar',false);
208
		}
209
		// Default text input field
210
		else {
211
			$html = parent::Field();
212
		}
213
214
		return $html;
215
	}
216
217
	public function Type() {
218
		return 'date text';
219
	}
220
221
	/**
222
	 * Sets the internal value to ISO date format.
223
	 *
224
	 * @param String|Array $val
225
	 */
226
	public function setValue($val) {
227
		$locale = new Zend_Locale($this->locale);
228
229
		if(empty($val)) {
230
			$this->value = null;
231
			$this->valueObj = null;
232
		} else {
233
			if($this->getConfig('dmyfields')) {
234
				// Setting in correct locale
235
				if(is_array($val) && $this->validateArrayValue($val)) {
236
					// set() gets confused with custom date formats when using array notation
237
					if(!(empty($val['day']) || empty($val['month']) || empty($val['year']))) {
238
						$this->valueObj = new Zend_Date($val, null, $locale);
239
						$this->value = $this->valueObj->toArray();
240
					} else {
241
						$this->value = $val;
242
						$this->valueObj = null;
243
					}
244
				}
245
				// load ISO date from database (usually through Form->loadDataForm())
246
				else if(!empty($val) && Zend_Date::isDate($val, $this->getConfig('datavalueformat'), $locale)) {
247
					$this->valueObj = new Zend_Date($val, $this->getConfig('datavalueformat'), $locale);
248
					$this->value = $this->valueObj->toArray();
249
				}
250
				else {
251
					$this->value = $val;
252
					$this->valueObj = null;
253
				}
254
			} else {
255
				// Setting in correct locale.
256
				// Caution: Its important to have this check *before* the ISO date fallback,
257
				// as some dates are falsely detected as ISO by isDate(), e.g. '03/04/03'
258
				// (en_NZ for 3rd of April, definetly not yyyy-MM-dd)
259
				if(!empty($val) && Zend_Date::isDate($val, $this->getConfig('dateformat'), $locale)) {
260
					$this->valueObj = new Zend_Date($val, $this->getConfig('dateformat'), $locale);
261
					$this->value = $this->valueObj->get($this->getConfig('dateformat'), $locale);
262
263
				}
264
				// load ISO date from database (usually through Form->loadDataForm())
265
				else if(!empty($val) && Zend_Date::isDate($val, $this->getConfig('datavalueformat'))) {
266
					$this->valueObj = new Zend_Date($val, $this->getConfig('datavalueformat'));
267
					$this->value = $this->valueObj->get($this->getConfig('dateformat'), $locale);
268
				}
269
				else {
270
					$this->value = $val;
271
					$this->valueObj = null;
272
				}
273
			}
274
		}
275
276
		return $this;
277
	}
278
279
	/**
280
	 * @return String ISO 8601 date, suitable for insertion into database
281
	 */
282
	public function dataValue() {
283
		if($this->valueObj) {
284
			return $this->valueObj->toString($this->getConfig('datavalueformat'));
285
		} else {
286
			return null;
287
		}
288
	}
289
290
	public function performReadonlyTransformation() {
291
		$field = $this->castedCopy('DateField_Disabled');
292
		$field->setValue($this->dataValue());
293
		$field->readonly = true;
294
295
		return $field;
296
	}
297
298
	public function castedCopy($class) {
299
		$copy = new $class($this->name);
300
		if($copy->hasMethod('setConfig')) {
301
			$config = $this->getConfig();
302
			foreach($config as $k => $v) {
303
				$copy->setConfig($k, $v);
304
			}
305
		}
306
307
		return parent::castedCopy($copy);
308
	}
309
310
	/**
311
	 * Validate an array with expected keys 'day', 'month' and 'year.
312
	 * Used because Zend_Date::isDate() doesn't provide this.
313
	 *
314
	 * @param Array $val
315
	 * @return boolean
316
	 */
317
	public function validateArrayValue($val) {
318
		if(!is_array($val)) return false;
319
320
		// Validate against Zend_Date,
321
		// but check for empty array keys (they're included in standard form submissions)
322
		return (
323
			array_key_exists('year', $val)
324
			&& (!$val['year'] || Zend_Date::isDate($val['year'], 'yyyy', $this->locale))
325
			&& array_key_exists('month', $val)
326
			&& (!$val['month'] || Zend_Date::isDate($val['month'], 'MM', $this->locale))
327
			&& array_key_exists('day', $val)
328
			&& (!$val['day'] || Zend_Date::isDate($val['day'], 'dd', $this->locale))
329
		);
330
	}
331
332
	/**
333
	 * @deprecated 4.0 Use the "DateField.default_config" config setting instead
334
	 * @param String $k
335
	 * @param mixed $v
336
	 * @return boolean
337
	 */
338
	public static function set_default_config($k, $v) {
339
		Deprecation::notice('4.0', 'Use the "DateField.default_config" config setting instead');
340
		return Config::inst()->update('DateField', 'default_config', array($k => $v));
341
	}
342
343
	/**
344
	 * @return Boolean
345
	 */
346
	public function validate($validator) {
347
		$valid = true;
348
349
		// Don't validate empty fields
350
		if ($this->getConfig('dmyfields')) {
351
			if (empty($this->value['day']) && empty($this->value['month']) && empty($this->value['year'])) {
352
				return $valid;
353
			}
354
		}
355
		elseif (empty($this->value)) {
356
			return $valid;
357
		}
358
359
		// date format
360
		if($this->getConfig('dmyfields')) {
361
			$valid = (!$this->value || $this->validateArrayValue($this->value));
362
		} else {
363
			$valid = (Zend_Date::isDate($this->value, $this->getConfig('dateformat'), $this->locale));
364
		}
365
		if(!$valid) {
366
			$validator->validationError(
367
				$this->name,
368
				_t(
369
					'DateField.VALIDDATEFORMAT2', "Please enter a valid date format ({format})",
370
					array('format' => $this->getConfig('dateformat'))
371
				),
372
				"validation",
373
				false
374
			);
375
			return false;
376
		}
377
378
		// min/max - Assumes that the date value was valid in the first place
379
		if($min = $this->getConfig('min')) {
380
			// ISO or strtotime()
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
381
			if(Zend_Date::isDate($min, $this->getConfig('datavalueformat'))) {
382
				$minDate = new Zend_Date($min, $this->getConfig('datavalueformat'));
383
			} else {
384
				$minDate = new Zend_Date(strftime('%Y-%m-%d', strtotime($min)), $this->getConfig('datavalueformat'));
385
			}
386
			if(!$this->valueObj || (!$this->valueObj->isLater($minDate) && !$this->valueObj->equals($minDate))) {
387
				$validator->validationError(
388
					$this->name,
389
					_t(
390
						'DateField.VALIDDATEMINDATE',
391
						"Your date has to be newer or matching the minimum allowed date ({date})",
392
						array('date' => $minDate->toString($this->getConfig('dateformat')))
393
					),
394
					"validation",
395
					false
396
				);
397
				return false;
398
			}
399
		}
400
		if($max = $this->getConfig('max')) {
401
			// ISO or strtotime()
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
402
			if(Zend_Date::isDate($min, $this->getConfig('datavalueformat'))) {
403
				$maxDate = new Zend_Date($max, $this->getConfig('datavalueformat'));
404
			} else {
405
				$maxDate = new Zend_Date(strftime('%Y-%m-%d', strtotime($max)), $this->getConfig('datavalueformat'));
406
			}
407
			if(!$this->valueObj || (!$this->valueObj->isEarlier($maxDate) && !$this->valueObj->equals($maxDate))) {
408
				$validator->validationError(
409
					$this->name,
410
					_t('DateField.VALIDDATEMAXDATE',
411
						"Your date has to be older or matching the maximum allowed date ({date})",
412
						array('date' => $maxDate->toString($this->getConfig('dateformat')))
413
					),
414
					"validation",
415
					false
416
				);
417
				return false;
418
			}
419
		}
420
421
		return true;
422
	}
423
424
	/**
425
	 * @return string
426
	 */
427
	public function getLocale() {
428
		return $this->locale;
429
	}
430
431
	/**
432
	 * Caution: Will not update the 'dateformat' config value.
433
	 *
434
	 * @param String $locale
435
	 */
436
	public function setLocale($locale) {
437
		$this->locale = $locale;
438
		return $this;
439
	}
440
441
	/**
442
	 * @param string $name
443
	 * @param mixed $val
444
	 */
445
	public function setConfig($name, $val) {
446
		switch($name) {
447
			case 'min':
448
				$format = $this->getConfig('datavalueformat');
449
				if($val && !Zend_Date::isDate($val, $format) && !strtotime($val)) {
450
					throw new InvalidArgumentException(
451
						sprintf('Date "%s" is not a valid minimum date format (%s) or strtotime() argument',
452
						$val, $format));
453
				}
454
				break;
455
			case 'max':
456
				$format = $this->getConfig('datavalueformat');
457
				if($val && !Zend_Date::isDate($val, $format) && !strtotime($val)) {
458
					throw new InvalidArgumentException(
459
						sprintf('Date "%s" is not a valid maximum date format (%s) or strtotime() argument',
460
						$val, $format));
461
				}
462
				break;
463
		}
464
465
		$this->config[$name] = $val;
466
		return $this;
467
	}
468
469
	/**
470
	 * @param String $name Optional, returns the whole configuration array if empty
471
	 * @return mixed|array
472
	 */
473
	public function getConfig($name = null) {
474
		if($name) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $name of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
475
			return isset($this->config[$name]) ? $this->config[$name] : null;
476
		} else {
477
			return $this->config;
478
		}
479
	}
480
}
481
482
/**
483
 * Disabled version of {@link DateField}.
484
 * Allows dates to be represented in a form, by showing in a user friendly format, eg, dd/mm/yyyy.
485
 * @package forms
486
 * @subpackage fields-datetime
487
 */
488
class DateField_Disabled extends DateField {
489
490
	protected $disabled = true;
491
492
	public function Field($properties = array()) {
493
		if($this->valueObj) {
494
			if($this->valueObj->isToday()) {
495
				$val = Convert::raw2xml($this->valueObj->toString($this->getConfig('dateformat'))
496
					. ' ('._t('DateField.TODAY','today').')');
497
			} else {
498
				$df = new DBDate($this->name);
499
				$df->setValue($this->dataValue());
500
				$val = Convert::raw2xml($this->valueObj->toString($this->getConfig('dateformat'))
501
					. ', ' . $df->Ago());
502
			}
503
		} else {
504
			$val = '<i>('._t('DateField.NOTSET', 'not set').')</i>';
505
		}
506
507
		return "<span class=\"readonly\" id=\"" . $this->id() . "\">$val</span>";
508
	}
509
510
	public function Type() {
511
		return "date_disabled readonly";
512
	}
513
}
514
515
/**
516
 * Preliminary API to separate optional view properties
517
 * like calendar popups from the actual datefield logic.
518
 *
519
 * Caution: This API is highly volatile, and might change without prior deprecation.
520
 *
521
 * @package framework
522
 * @subpackage forms
523
 */
524
class DateField_View_JQuery extends Object {
525
526
	protected $field;
527
528
	/*
529
	 * the current jQuery UI DatePicker locale file
530
	 */
531
	protected $jqueryLocaleFile = '';
532
533
	/**
534
	 * @var array Maps values from {@link i18n::$all_locales} to
535
	 * localizations existing in jQuery UI.
536
	 */
537
	private static $locale_map = array(
538
		'en_GB' => 'en-GB',
539
		'en_US' => 'en',
540
		'en_NZ' => 'en-GB',
541
		'fr_CH' => 'fr',
542
		'pt_BR' => 'pt-BR',
543
		'sr_SR' => 'sr-SR',
544
		'zh_CN' => 'zh-CN',
545
		'zh_HK' => 'zh-HK',
546
		'zh_TW' => 'zh-TW',
547
	);
548
549
	/**
550
	 * @param DateField $field
551
	 */
552
	public function __construct($field) {
553
		$this->field = $field;
554
	}
555
556
	/**
557
	 * @return DateField
558
	 */
559
	public function getField() {
560
		return $this->field;
561
	}
562
563
	/**
564
	 * Check if jQuery UI locale settings exists for the current locale
565
	 * @return boolean
566
	 */
567
	function regionalSettingsExist() {
568
		$lang = $this->getLang();
569
		$localeFile = THIRDPARTY_DIR . "/jquery-ui/datepicker/i18n/jquery.ui.datepicker-{$lang}.js";
570
		if (file_exists(Director::baseFolder() . '/' .$localeFile)){
571
			$this->jqueryLocaleFile = $localeFile;
572
			return true;
573
		} else {
574
			// file goes before internal en_US settings,
575
			// but both will validate
576
			return ($lang == 'en');
577
		}
578
	}
579
580
	public function onBeforeRender() {
581
	}
582
583
	/**
584
	 * @param String $html
585
	 * @return
586
	 */
587
	public function onAfterRender($html) {
588
		if($this->getField()->getConfig('showcalendar')) {
589
			Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js');
590
			Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css');
591
			Requirements::javascript(FRAMEWORK_DIR . '/thirdparty/jquery-ui/jquery-ui.js');
592
593
			// Include language files (if required)
594
			if ($this->jqueryLocaleFile){
595
				Requirements::javascript($this->jqueryLocaleFile);
596
			}
597
598
			Requirements::javascript(FRAMEWORK_DIR . "/client/dist/js/DateField.js");
599
		}
600
601
		return $html;
602
	}
603
604
	/**
605
	 * Determines which language to use for jQuery UI, which
606
	 * can be different from the value set in i18n.
607
	 *
608
	 * @return String
609
	 */
610
	protected function getLang() {
611
		$locale = $this->getField()->getLocale();
612
		$map = $this->config()->locale_map;
0 ignored issues
show
Documentation introduced by
The property locale_map does not exist on object<Config_ForClass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
613
		if($this->getField()->getConfig('jslocale')) {
614
			// Undocumented config property for now, might move to the jQuery view helper
615
			$lang = $this->getField()->getConfig('jslocale');
616
		} else if(array_key_exists($locale, $map)) {
617
			// Specialized mapping for combined lang properties
618
			$lang = $map[$locale];
619
		} else {
620
			// Fall back to default lang (meaning "en_US" turns into "en")
621
			$lang = i18n::get_lang_from_locale($locale);
622
		}
623
624
		return $lang;
625
	}
626
627
	/**
628
	 * Convert iso to jquery UI date format.
629
	 * Needs to be consistent with Zend formatting, otherwise validation will fail.
630
	 * Removes all time settings like hour/minute/second from the format.
631
	 * See http://docs.jquery.com/UI/Datepicker/formatDate
632
	 *
633
	 * @param String $format
634
	 * @return String
635
	 */
636
	public static function convert_iso_to_jquery_format($format) {
637
		$convert = array(
638
			'/([^d])d([^d])/' => '$1d$2',
639
			'/^d([^d])/' => 'd$1',
640
			'/([^d])d$/' => '$1d',
641
			'/dd/' => 'dd',
642
			'/SS/' => '',
643
			'/eee/' => 'd',
644
			'/e/' => 'N',
645
			'/D/' => '',
646
			'/EEEE/' => 'DD',
647
			'/EEE/' => 'D',
648
			'/w/' => '',
649
			// make single "M" lowercase
650
			'/([^M])M([^M])/' => '$1m$2',
651
			// make single "M" at start of line lowercase
652
			'/^M([^M])/' => 'm$1',
653
				// make single "M" at end of line lowercase
654
			'/([^M])M$/' => '$1m',
655
			// match exactly three capital Ms not preceeded or followed by an M
656
			'/(?<!M)MMM(?!M)/' => 'M',
657
			// match exactly two capital Ms not preceeded or followed by an M
658
			'/(?<!M)MM(?!M)/' => 'mm',
659
			// match four capital Ms (maximum allowed)
660
			'/MMMM/' => 'MM',
661
			'/l/' => '',
662
			'/YYYY/' => 'yy',
663
			'/yyyy/' => 'yy',
664
			// See http://open.silverstripe.org/ticket/7669
665
			'/y{1,3}/' => 'yy',
666
			'/a/' => '',
667
			'/B/' => '',
668
			'/hh/' => '',
669
			'/h/' => '',
670
			'/([^H])H([^H])/' => '',
671
			'/^H([^H])/' => '',
672
			'/([^H])H$/' => '',
673
			'/HH/' => '',
674
			// '/mm/' => '',
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
675
			'/ss/' => '',
676
			'/zzzz/' => '',
677
			'/I/' => '',
678
			'/ZZZZ/' => '',
679
			'/Z/' => '',
680
			'/z/' => '',
681
			'/X/' => '',
682
			'/r/' => '',
683
			'/U/' => '',
684
		);
685
		$patterns = array_keys($convert);
686
		$replacements = array_values($convert);
687
688
		return preg_replace($patterns, $replacements, $format);
689
	}
690
}
691