Mercurial > hg > Members > shoshi > webvirt
comparison cake/libs/view/helpers/form.php @ 0:261e66bd5a0c
hg init
author | Shoshi TAMAKI <shoshi@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Sun, 24 Jul 2011 21:08:31 +0900 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:261e66bd5a0c |
---|---|
1 <?php | |
2 /** | |
3 * Automatic generation of HTML FORMs from given data. | |
4 * | |
5 * Used for scaffolding. | |
6 * | |
7 * PHP versions 4 and 5 | |
8 * | |
9 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org) | |
10 * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org) | |
11 * | |
12 * Licensed under The MIT License | |
13 * Redistributions of files must retain the above copyright notice. | |
14 * | |
15 * @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org) | |
16 * @link http://cakephp.org CakePHP(tm) Project | |
17 * @package cake | |
18 * @subpackage cake.cake.libs.view.helpers | |
19 * @since CakePHP(tm) v 0.10.0.1076 | |
20 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) | |
21 */ | |
22 | |
23 /** | |
24 * Form helper library. | |
25 * | |
26 * Automatic generation of HTML FORMs from given data. | |
27 * | |
28 * @package cake | |
29 * @subpackage cake.cake.libs.view.helpers | |
30 * @link http://book.cakephp.org/view/1383/Form | |
31 */ | |
32 class FormHelper extends AppHelper { | |
33 | |
34 /** | |
35 * Other helpers used by FormHelper | |
36 * | |
37 * @var array | |
38 * @access public | |
39 */ | |
40 var $helpers = array('Html'); | |
41 | |
42 /** | |
43 * Holds the fields array('field_name' => array('type'=> 'string', 'length'=> 100), | |
44 * primaryKey and validates array('field_name') | |
45 * | |
46 * @access public | |
47 */ | |
48 var $fieldset = array(); | |
49 | |
50 /** | |
51 * Options used by DateTime fields | |
52 * | |
53 * @var array | |
54 */ | |
55 var $__options = array( | |
56 'day' => array(), 'minute' => array(), 'hour' => array(), | |
57 'month' => array(), 'year' => array(), 'meridian' => array() | |
58 ); | |
59 | |
60 /** | |
61 * List of fields created, used with secure forms. | |
62 * | |
63 * @var array | |
64 * @access public | |
65 */ | |
66 var $fields = array(); | |
67 | |
68 /** | |
69 * Defines the type of form being created. Set by FormHelper::create(). | |
70 * | |
71 * @var string | |
72 * @access public | |
73 */ | |
74 var $requestType = null; | |
75 | |
76 /** | |
77 * The default model being used for the current form. | |
78 * | |
79 * @var string | |
80 * @access public | |
81 */ | |
82 var $defaultModel = null; | |
83 | |
84 | |
85 /** | |
86 * Persistent default options used by input(). Set by FormHelper::create(). | |
87 * | |
88 * @var array | |
89 * @access protected | |
90 */ | |
91 var $_inputDefaults = array(); | |
92 | |
93 /** | |
94 * Introspects model information and extracts information related | |
95 * to validation, field length and field type. Appends information into | |
96 * $this->fieldset. | |
97 * | |
98 * @return Model Returns a model instance | |
99 * @access protected | |
100 */ | |
101 function &_introspectModel($model) { | |
102 $object = null; | |
103 if (is_string($model) && strpos($model, '.') !== false) { | |
104 $path = explode('.', $model); | |
105 $model = end($path); | |
106 } | |
107 | |
108 if (ClassRegistry::isKeySet($model)) { | |
109 $object =& ClassRegistry::getObject($model); | |
110 } | |
111 | |
112 if (!empty($object)) { | |
113 $fields = $object->schema(); | |
114 foreach ($fields as $key => $value) { | |
115 unset($fields[$key]); | |
116 $fields[$key] = $value; | |
117 } | |
118 | |
119 if (!empty($object->hasAndBelongsToMany)) { | |
120 foreach ($object->hasAndBelongsToMany as $alias => $assocData) { | |
121 $fields[$alias] = array('type' => 'multiple'); | |
122 } | |
123 } | |
124 $validates = array(); | |
125 if (!empty($object->validate)) { | |
126 foreach ($object->validate as $validateField => $validateProperties) { | |
127 if ($this->_isRequiredField($validateProperties)) { | |
128 $validates[] = $validateField; | |
129 } | |
130 } | |
131 } | |
132 $defaults = array('fields' => array(), 'key' => 'id', 'validates' => array()); | |
133 $key = $object->primaryKey; | |
134 $this->fieldset[$model] = array_merge($defaults, compact('fields', 'key', 'validates')); | |
135 } | |
136 | |
137 return $object; | |
138 } | |
139 | |
140 /** | |
141 * Returns if a field is required to be filled based on validation properties from the validating object | |
142 * | |
143 * @return boolean true if field is required to be filled, false otherwise | |
144 * @access protected | |
145 */ | |
146 function _isRequiredField($validateProperties) { | |
147 $required = false; | |
148 if (is_array($validateProperties)) { | |
149 | |
150 $dims = Set::countDim($validateProperties); | |
151 if ($dims == 1 || ($dims == 2 && isset($validateProperties['rule']))) { | |
152 $validateProperties = array($validateProperties); | |
153 } | |
154 | |
155 foreach ($validateProperties as $rule => $validateProp) { | |
156 if (isset($validateProp['allowEmpty']) && $validateProp['allowEmpty'] === true) { | |
157 return false; | |
158 } | |
159 $rule = isset($validateProp['rule']) ? $validateProp['rule'] : false; | |
160 $required = $rule || empty($validateProp); | |
161 if ($required) { | |
162 break; | |
163 } | |
164 } | |
165 } | |
166 return $required; | |
167 } | |
168 | |
169 /** | |
170 * Returns an HTML FORM element. | |
171 * | |
172 * ### Options: | |
173 * | |
174 * - `type` Form method defaults to POST | |
175 * - `action` The controller action the form submits to, (optional). | |
176 * - `url` The url the form submits to. Can be a string or a url array, | |
177 * - `default` Allows for the creation of Ajax forms. | |
178 * - `onsubmit` Used in conjunction with 'default' to create ajax forms. | |
179 * - `inputDefaults` set the default $options for FormHelper::input(). Any options that would | |
180 * be set when using FormHelper::input() can be set here. Options set with `inputDefaults` | |
181 * can be overridden when calling input() | |
182 * - `encoding` Set the accept-charset encoding for the form. Defaults to `Configure::read('App.encoding')` | |
183 * | |
184 * @access public | |
185 * @param string $model The model object which the form is being defined for | |
186 * @param array $options An array of html attributes and options. | |
187 * @return string An formatted opening FORM tag. | |
188 * @link http://book.cakephp.org/view/1384/Creating-Forms | |
189 */ | |
190 function create($model = null, $options = array()) { | |
191 $created = $id = false; | |
192 $append = ''; | |
193 $view =& ClassRegistry::getObject('view'); | |
194 | |
195 if (is_array($model) && empty($options)) { | |
196 $options = $model; | |
197 $model = null; | |
198 } | |
199 if (empty($model) && $model !== false && !empty($this->params['models'])) { | |
200 $model = $this->params['models'][0]; | |
201 $this->defaultModel = $this->params['models'][0]; | |
202 } elseif (empty($model) && empty($this->params['models'])) { | |
203 $model = false; | |
204 } | |
205 | |
206 $models = ClassRegistry::keys(); | |
207 foreach ($models as $currentModel) { | |
208 if (ClassRegistry::isKeySet($currentModel)) { | |
209 $currentObject =& ClassRegistry::getObject($currentModel); | |
210 if (is_a($currentObject, 'Model') && !empty($currentObject->validationErrors)) { | |
211 $this->validationErrors[Inflector::camelize($currentModel)] =& $currentObject->validationErrors; | |
212 } | |
213 } | |
214 } | |
215 | |
216 $object = $this->_introspectModel($model); | |
217 $this->setEntity($model . '.', true); | |
218 | |
219 $modelEntity = $this->model(); | |
220 if (isset($this->fieldset[$modelEntity]['key'])) { | |
221 $data = $this->fieldset[$modelEntity]; | |
222 $recordExists = ( | |
223 isset($this->data[$model]) && | |
224 !empty($this->data[$model][$data['key']]) && | |
225 !is_array($this->data[$model][$data['key']]) | |
226 ); | |
227 | |
228 if ($recordExists) { | |
229 $created = true; | |
230 $id = $this->data[$model][$data['key']]; | |
231 } | |
232 } | |
233 | |
234 $options = array_merge(array( | |
235 'type' => ($created && empty($options['action'])) ? 'put' : 'post', | |
236 'action' => null, | |
237 'url' => null, | |
238 'default' => true, | |
239 'encoding' => strtolower(Configure::read('App.encoding')), | |
240 'inputDefaults' => array()), | |
241 $options); | |
242 $this->_inputDefaults = $options['inputDefaults']; | |
243 unset($options['inputDefaults']); | |
244 | |
245 if (empty($options['url']) || is_array($options['url'])) { | |
246 if (empty($options['url']['controller'])) { | |
247 if (!empty($model) && $model != $this->defaultModel) { | |
248 $options['url']['controller'] = Inflector::underscore(Inflector::pluralize($model)); | |
249 } elseif (!empty($this->params['controller'])) { | |
250 $options['url']['controller'] = Inflector::underscore($this->params['controller']); | |
251 } | |
252 } | |
253 if (empty($options['action'])) { | |
254 $options['action'] = $this->params['action']; | |
255 } | |
256 | |
257 $actionDefaults = array( | |
258 'plugin' => $this->plugin, | |
259 'controller' => $view->viewPath, | |
260 'action' => $options['action'] | |
261 ); | |
262 if (!empty($options['action']) && !isset($options['id'])) { | |
263 $options['id'] = $this->domId($options['action'] . 'Form'); | |
264 } | |
265 $options['action'] = array_merge($actionDefaults, (array)$options['url']); | |
266 if (empty($options['action'][0])) { | |
267 $options['action'][0] = $id; | |
268 } | |
269 } elseif (is_string($options['url'])) { | |
270 $options['action'] = $options['url']; | |
271 } | |
272 unset($options['url']); | |
273 | |
274 switch (strtolower($options['type'])) { | |
275 case 'get': | |
276 $htmlAttributes['method'] = 'get'; | |
277 break; | |
278 case 'file': | |
279 $htmlAttributes['enctype'] = 'multipart/form-data'; | |
280 $options['type'] = ($created) ? 'put' : 'post'; | |
281 case 'post': | |
282 case 'put': | |
283 case 'delete': | |
284 $append .= $this->hidden('_method', array( | |
285 'name' => '_method', 'value' => strtoupper($options['type']), 'id' => null | |
286 )); | |
287 default: | |
288 $htmlAttributes['method'] = 'post'; | |
289 break; | |
290 } | |
291 $this->requestType = strtolower($options['type']); | |
292 | |
293 $htmlAttributes['action'] = $this->url($options['action']); | |
294 unset($options['type'], $options['action']); | |
295 | |
296 if ($options['default'] == false) { | |
297 if (isset($htmlAttributes['onSubmit']) || isset($htmlAttributes['onsubmit'])) { | |
298 $htmlAttributes['onsubmit'] .= ' event.returnValue = false; return false;'; | |
299 } else { | |
300 $htmlAttributes['onsubmit'] = 'event.returnValue = false; return false;'; | |
301 } | |
302 } | |
303 | |
304 if (!empty($options['encoding'])) { | |
305 $htmlAttributes['accept-charset'] = $options['encoding']; | |
306 unset($options['encoding']); | |
307 } | |
308 | |
309 unset($options['default']); | |
310 $htmlAttributes = array_merge($options, $htmlAttributes); | |
311 | |
312 $this->fields = array(); | |
313 if (isset($this->params['_Token']) && !empty($this->params['_Token'])) { | |
314 $append .= $this->hidden('_Token.key', array( | |
315 'value' => $this->params['_Token']['key'], 'id' => 'Token' . mt_rand()) | |
316 ); | |
317 } | |
318 | |
319 if (!empty($append)) { | |
320 $append = sprintf($this->Html->tags['block'], ' style="display:none;"', $append); | |
321 } | |
322 | |
323 $this->setEntity($model . '.', true); | |
324 $attributes = $this->_parseAttributes($htmlAttributes, null, ''); | |
325 return sprintf($this->Html->tags['form'], $attributes) . $append; | |
326 } | |
327 | |
328 /** | |
329 * Closes an HTML form, cleans up values set by FormHelper::create(), and writes hidden | |
330 * input fields where appropriate. | |
331 * | |
332 * If $options is set a form submit button will be created. Options can be either a string or an array. | |
333 * | |
334 * {{{ | |
335 * array usage: | |
336 * | |
337 * array('label' => 'save'); value="save" | |
338 * array('label' => 'save', 'name' => 'Whatever'); value="save" name="Whatever" | |
339 * array('name' => 'Whatever'); value="Submit" name="Whatever" | |
340 * array('label' => 'save', 'name' => 'Whatever', 'div' => 'good') <div class="good"> value="save" name="Whatever" | |
341 * array('label' => 'save', 'name' => 'Whatever', 'div' => array('class' => 'good')); <div class="good"> value="save" name="Whatever" | |
342 * }}} | |
343 * | |
344 * @param mixed $options as a string will use $options as the value of button, | |
345 * @return string a closing FORM tag optional submit button. | |
346 * @access public | |
347 * @link http://book.cakephp.org/view/1389/Closing-the-Form | |
348 */ | |
349 function end($options = null) { | |
350 if (!empty($this->params['models'])) { | |
351 $models = $this->params['models'][0]; | |
352 } | |
353 $out = null; | |
354 $submit = null; | |
355 | |
356 if ($options !== null) { | |
357 $submitOptions = array(); | |
358 if (is_string($options)) { | |
359 $submit = $options; | |
360 } else { | |
361 if (isset($options['label'])) { | |
362 $submit = $options['label']; | |
363 unset($options['label']); | |
364 } | |
365 $submitOptions = $options; | |
366 } | |
367 $out .= $this->submit($submit, $submitOptions); | |
368 } | |
369 if (isset($this->params['_Token']) && !empty($this->params['_Token'])) { | |
370 $out .= $this->secure($this->fields); | |
371 $this->fields = array(); | |
372 } | |
373 $this->setEntity(null); | |
374 $out .= $this->Html->tags['formend']; | |
375 | |
376 $view =& ClassRegistry::getObject('view'); | |
377 $view->modelScope = false; | |
378 return $out; | |
379 } | |
380 | |
381 /** | |
382 * Generates a hidden field with a security hash based on the fields used in the form. | |
383 * | |
384 * @param array $fields The list of fields to use when generating the hash | |
385 * @return string A hidden input field with a security hash | |
386 * @access public | |
387 */ | |
388 function secure($fields = array()) { | |
389 if (!isset($this->params['_Token']) || empty($this->params['_Token'])) { | |
390 return; | |
391 } | |
392 $locked = array(); | |
393 | |
394 foreach ($fields as $key => $value) { | |
395 if (!is_int($key)) { | |
396 $locked[$key] = $value; | |
397 unset($fields[$key]); | |
398 } | |
399 } | |
400 sort($fields, SORT_STRING); | |
401 ksort($locked, SORT_STRING); | |
402 $fields += $locked; | |
403 | |
404 $fields = Security::hash(serialize($fields) . Configure::read('Security.salt')); | |
405 $locked = implode(array_keys($locked), '|'); | |
406 | |
407 $out = $this->hidden('_Token.fields', array( | |
408 'value' => urlencode($fields . ':' . $locked), | |
409 'id' => 'TokenFields' . mt_rand() | |
410 )); | |
411 $out = sprintf($this->Html->tags['block'], ' style="display:none;"', $out); | |
412 return $out; | |
413 } | |
414 | |
415 /** | |
416 * Determine which fields of a form should be used for hash. | |
417 * Populates $this->fields | |
418 * | |
419 * @param mixed $field Reference to field to be secured | |
420 * @param mixed $value Field value, if value should not be tampered with. | |
421 * @return void | |
422 * @access private | |
423 */ | |
424 function __secure($field = null, $value = null) { | |
425 if (!$field) { | |
426 $view =& ClassRegistry::getObject('view'); | |
427 $field = $view->entity(); | |
428 } elseif (is_string($field)) { | |
429 $field = Set::filter(explode('.', $field), true); | |
430 } | |
431 | |
432 if (!empty($this->params['_Token']['disabledFields'])) { | |
433 foreach ((array)$this->params['_Token']['disabledFields'] as $disabled) { | |
434 $disabled = explode('.', $disabled); | |
435 if (array_values(array_intersect($field, $disabled)) === $disabled) { | |
436 return; | |
437 } | |
438 } | |
439 } | |
440 $field = implode('.', $field); | |
441 if (!in_array($field, $this->fields)) { | |
442 if ($value !== null) { | |
443 return $this->fields[$field] = $value; | |
444 } | |
445 $this->fields[] = $field; | |
446 } | |
447 } | |
448 | |
449 /** | |
450 * Returns true if there is an error for the given field, otherwise false | |
451 * | |
452 * @param string $field This should be "Modelname.fieldname" | |
453 * @return boolean If there are errors this method returns true, else false. | |
454 * @access public | |
455 * @link http://book.cakephp.org/view/1426/isFieldError | |
456 */ | |
457 function isFieldError($field) { | |
458 $this->setEntity($field); | |
459 return (bool)$this->tagIsInvalid(); | |
460 } | |
461 | |
462 /** | |
463 * Returns a formatted error message for given FORM field, NULL if no errors. | |
464 * | |
465 * ### Options: | |
466 * | |
467 * - `escape` bool Whether or not to html escape the contents of the error. | |
468 * - `wrap` mixed Whether or not the error message should be wrapped in a div. If a | |
469 * string, will be used as the HTML tag to use. | |
470 * - `class` string The classname for the error message | |
471 * | |
472 * @param string $field A field name, like "Modelname.fieldname" | |
473 * @param mixed $text Error message or array of $options. If array, `attributes` key | |
474 * will get used as html attributes for error container | |
475 * @param array $options Rendering options for <div /> wrapper tag | |
476 * @return string If there are errors this method returns an error message, otherwise null. | |
477 * @access public | |
478 * @link http://book.cakephp.org/view/1423/error | |
479 */ | |
480 function error($field, $text = null, $options = array()) { | |
481 $defaults = array('wrap' => true, 'class' => 'error-message', 'escape' => true); | |
482 $options = array_merge($defaults, $options); | |
483 $this->setEntity($field); | |
484 | |
485 if ($error = $this->tagIsInvalid()) { | |
486 if (is_array($error)) { | |
487 list(,,$field) = explode('.', $field); | |
488 if (isset($error[$field])) { | |
489 $error = $error[$field]; | |
490 } else { | |
491 return null; | |
492 } | |
493 } | |
494 | |
495 if (is_array($text) && is_numeric($error) && $error > 0) { | |
496 $error--; | |
497 } | |
498 if (is_array($text)) { | |
499 $options = array_merge($options, array_intersect_key($text, $defaults)); | |
500 if (isset($text['attributes']) && is_array($text['attributes'])) { | |
501 $options = array_merge($options, $text['attributes']); | |
502 } | |
503 $text = isset($text[$error]) ? $text[$error] : null; | |
504 unset($options[$error]); | |
505 } | |
506 | |
507 if ($text != null) { | |
508 $error = $text; | |
509 } elseif (is_numeric($error)) { | |
510 $error = sprintf(__('Error in field %s', true), Inflector::humanize($this->field())); | |
511 } | |
512 if ($options['escape']) { | |
513 $error = h($error); | |
514 unset($options['escape']); | |
515 } | |
516 if ($options['wrap']) { | |
517 $tag = is_string($options['wrap']) ? $options['wrap'] : 'div'; | |
518 unset($options['wrap']); | |
519 return $this->Html->tag($tag, $error, $options); | |
520 } else { | |
521 return $error; | |
522 } | |
523 } else { | |
524 return null; | |
525 } | |
526 } | |
527 | |
528 /** | |
529 * Returns a formatted LABEL element for HTML FORMs. Will automatically generate | |
530 * a for attribute if one is not provided. | |
531 * | |
532 * @param string $fieldName This should be "Modelname.fieldname" | |
533 * @param string $text Text that will appear in the label field. | |
534 * @param mixed $options An array of HTML attributes, or a string, to be used as a class name. | |
535 * @return string The formatted LABEL element | |
536 * @link http://book.cakephp.org/view/1427/label | |
537 */ | |
538 function label($fieldName = null, $text = null, $options = array()) { | |
539 if (empty($fieldName)) { | |
540 $view = ClassRegistry::getObject('view'); | |
541 $fieldName = implode('.', $view->entity()); | |
542 } | |
543 | |
544 if ($text === null) { | |
545 if (strpos($fieldName, '.') !== false) { | |
546 $text = array_pop(explode('.', $fieldName)); | |
547 } else { | |
548 $text = $fieldName; | |
549 } | |
550 if (substr($text, -3) == '_id') { | |
551 $text = substr($text, 0, strlen($text) - 3); | |
552 } | |
553 $text = __(Inflector::humanize(Inflector::underscore($text)), true); | |
554 } | |
555 | |
556 if (is_string($options)) { | |
557 $options = array('class' => $options); | |
558 } | |
559 | |
560 if (isset($options['for'])) { | |
561 $labelFor = $options['for']; | |
562 unset($options['for']); | |
563 } else { | |
564 $labelFor = $this->domId($fieldName); | |
565 } | |
566 | |
567 return sprintf( | |
568 $this->Html->tags['label'], | |
569 $labelFor, | |
570 $this->_parseAttributes($options), $text | |
571 ); | |
572 } | |
573 | |
574 /** | |
575 * Generate a set of inputs for `$fields`. If $fields is null the current model | |
576 * will be used. | |
577 * | |
578 * In addition to controller fields output, `$fields` can be used to control legend | |
579 * and fieldset rendering with the `fieldset` and `legend` keys. | |
580 * `$form->inputs(array('legend' => 'My legend'));` Would generate an input set with | |
581 * a custom legend. You can customize individual inputs through `$fields` as well. | |
582 * | |
583 * {{{ | |
584 * $form->inputs(array( | |
585 * 'name' => array('label' => 'custom label') | |
586 * )); | |
587 * }}} | |
588 * | |
589 * In addition to fields control, inputs() allows you to use a few additional options. | |
590 * | |
591 * - `fieldset` Set to false to disable the fieldset. If a string is supplied it will be used as | |
592 * the classname for the fieldset element. | |
593 * - `legend` Set to false to disable the legend for the generated input set. Or supply a string | |
594 * to customize the legend text. | |
595 * | |
596 * @param mixed $fields An array of fields to generate inputs for, or null. | |
597 * @param array $blacklist a simple array of fields to not create inputs for. | |
598 * @return string Completed form inputs. | |
599 * @access public | |
600 */ | |
601 function inputs($fields = null, $blacklist = null) { | |
602 $fieldset = $legend = true; | |
603 $model = $this->model(); | |
604 if (is_array($fields)) { | |
605 if (array_key_exists('legend', $fields)) { | |
606 $legend = $fields['legend']; | |
607 unset($fields['legend']); | |
608 } | |
609 | |
610 if (isset($fields['fieldset'])) { | |
611 $fieldset = $fields['fieldset']; | |
612 unset($fields['fieldset']); | |
613 } | |
614 } elseif ($fields !== null) { | |
615 $fieldset = $legend = $fields; | |
616 if (!is_bool($fieldset)) { | |
617 $fieldset = true; | |
618 } | |
619 $fields = array(); | |
620 } | |
621 | |
622 if (empty($fields)) { | |
623 $fields = array_keys($this->fieldset[$model]['fields']); | |
624 } | |
625 | |
626 if ($legend === true) { | |
627 $actionName = __('New %s', true); | |
628 $isEdit = ( | |
629 strpos($this->action, 'update') !== false || | |
630 strpos($this->action, 'edit') !== false | |
631 ); | |
632 if ($isEdit) { | |
633 $actionName = __('Edit %s', true); | |
634 } | |
635 $modelName = Inflector::humanize(Inflector::underscore($model)); | |
636 $legend = sprintf($actionName, __($modelName, true)); | |
637 } | |
638 | |
639 $out = null; | |
640 foreach ($fields as $name => $options) { | |
641 if (is_numeric($name) && !is_array($options)) { | |
642 $name = $options; | |
643 $options = array(); | |
644 } | |
645 $entity = explode('.', $name); | |
646 $blacklisted = ( | |
647 is_array($blacklist) && | |
648 (in_array($name, $blacklist) || in_array(end($entity), $blacklist)) | |
649 ); | |
650 if ($blacklisted) { | |
651 continue; | |
652 } | |
653 $out .= $this->input($name, $options); | |
654 } | |
655 | |
656 if (is_string($fieldset)) { | |
657 $fieldsetClass = sprintf(' class="%s"', $fieldset); | |
658 } else { | |
659 $fieldsetClass = ''; | |
660 } | |
661 | |
662 if ($fieldset && $legend) { | |
663 return sprintf( | |
664 $this->Html->tags['fieldset'], | |
665 $fieldsetClass, | |
666 sprintf($this->Html->tags['legend'], $legend) . $out | |
667 ); | |
668 } elseif ($fieldset) { | |
669 return sprintf($this->Html->tags['fieldset'], $fieldsetClass, $out); | |
670 } else { | |
671 return $out; | |
672 } | |
673 } | |
674 | |
675 /** | |
676 * Generates a form input element complete with label and wrapper div | |
677 * | |
678 * ### Options | |
679 * | |
680 * See each field type method for more information. Any options that are part of | |
681 * $attributes or $options for the different **type** methods can be included in `$options` for input().i | |
682 * Additionally, any unknown keys that are not in the list below, or part of the selected type's options | |
683 * will be treated as a regular html attribute for the generated input. | |
684 * | |
685 * - `type` - Force the type of widget you want. e.g. `type => 'select'` | |
686 * - `label` - Either a string label, or an array of options for the label. See FormHelper::label() | |
687 * - `div` - Either `false` to disable the div, or an array of options for the div. | |
688 * See HtmlHelper::div() for more options. | |
689 * - `options` - for widgets that take options e.g. radio, select | |
690 * - `error` - control the error message that is produced | |
691 * - `empty` - String or boolean to enable empty select box options. | |
692 * - `before` - Content to place before the label + input. | |
693 * - `after` - Content to place after the label + input. | |
694 * - `between` - Content to place between the label + input. | |
695 * - `format` - format template for element order. Any element that is not in the array, will not be in the output. | |
696 * - Default input format order: array('before', 'label', 'between', 'input', 'after', 'error') | |
697 * - Default checkbox format order: array('before', 'input', 'between', 'label', 'after', 'error') | |
698 * - Hidden input will not be formatted | |
699 * - Radio buttons cannot have the order of input and label elements controlled with these settings. | |
700 * | |
701 * @param string $fieldName This should be "Modelname.fieldname" | |
702 * @param array $options Each type of input takes different options. | |
703 * @return string Completed form widget. | |
704 * @access public | |
705 * @link http://book.cakephp.org/view/1390/Automagic-Form-Elements | |
706 */ | |
707 function input($fieldName, $options = array()) { | |
708 $this->setEntity($fieldName); | |
709 | |
710 $options = array_merge( | |
711 array('before' => null, 'between' => null, 'after' => null, 'format' => null), | |
712 $this->_inputDefaults, | |
713 $options | |
714 ); | |
715 | |
716 $modelKey = $this->model(); | |
717 $fieldKey = $this->field(); | |
718 if (!isset($this->fieldset[$modelKey])) { | |
719 $this->_introspectModel($modelKey); | |
720 } | |
721 | |
722 if (!isset($options['type'])) { | |
723 $magicType = true; | |
724 $options['type'] = 'text'; | |
725 if (isset($options['options'])) { | |
726 $options['type'] = 'select'; | |
727 } elseif (in_array($fieldKey, array('psword', 'passwd', 'password'))) { | |
728 $options['type'] = 'password'; | |
729 } elseif (isset($this->fieldset[$modelKey]['fields'][$fieldKey])) { | |
730 $fieldDef = $this->fieldset[$modelKey]['fields'][$fieldKey]; | |
731 $type = $fieldDef['type']; | |
732 $primaryKey = $this->fieldset[$modelKey]['key']; | |
733 } | |
734 | |
735 if (isset($type)) { | |
736 $map = array( | |
737 'string' => 'text', 'datetime' => 'datetime', | |
738 'boolean' => 'checkbox', 'timestamp' => 'datetime', | |
739 'text' => 'textarea', 'time' => 'time', | |
740 'date' => 'date', 'float' => 'text' | |
741 ); | |
742 | |
743 if (isset($this->map[$type])) { | |
744 $options['type'] = $this->map[$type]; | |
745 } elseif (isset($map[$type])) { | |
746 $options['type'] = $map[$type]; | |
747 } | |
748 if ($fieldKey == $primaryKey) { | |
749 $options['type'] = 'hidden'; | |
750 } | |
751 } | |
752 if (preg_match('/_id$/', $fieldKey) && $options['type'] !== 'hidden') { | |
753 $options['type'] = 'select'; | |
754 } | |
755 | |
756 if ($modelKey === $fieldKey) { | |
757 $options['type'] = 'select'; | |
758 if (!isset($options['multiple'])) { | |
759 $options['multiple'] = 'multiple'; | |
760 } | |
761 } | |
762 } | |
763 $types = array('checkbox', 'radio', 'select'); | |
764 | |
765 if ( | |
766 (!isset($options['options']) && in_array($options['type'], $types)) || | |
767 (isset($magicType) && $options['type'] == 'text') | |
768 ) { | |
769 $view =& ClassRegistry::getObject('view'); | |
770 $varName = Inflector::variable( | |
771 Inflector::pluralize(preg_replace('/_id$/', '', $fieldKey)) | |
772 ); | |
773 $varOptions = $view->getVar($varName); | |
774 if (is_array($varOptions)) { | |
775 if ($options['type'] !== 'radio') { | |
776 $options['type'] = 'select'; | |
777 } | |
778 $options['options'] = $varOptions; | |
779 } | |
780 } | |
781 | |
782 $autoLength = (!array_key_exists('maxlength', $options) && isset($fieldDef['length'])); | |
783 if ($autoLength && $options['type'] == 'text') { | |
784 $options['maxlength'] = $fieldDef['length']; | |
785 } | |
786 if ($autoLength && $fieldDef['type'] == 'float') { | |
787 $options['maxlength'] = array_sum(explode(',', $fieldDef['length']))+1; | |
788 } | |
789 | |
790 $divOptions = array(); | |
791 $div = $this->_extractOption('div', $options, true); | |
792 unset($options['div']); | |
793 | |
794 if (!empty($div)) { | |
795 $divOptions['class'] = 'input'; | |
796 $divOptions = $this->addClass($divOptions, $options['type']); | |
797 if (is_string($div)) { | |
798 $divOptions['class'] = $div; | |
799 } elseif (is_array($div)) { | |
800 $divOptions = array_merge($divOptions, $div); | |
801 } | |
802 if ( | |
803 isset($this->fieldset[$modelKey]) && | |
804 in_array($fieldKey, $this->fieldset[$modelKey]['validates']) | |
805 ) { | |
806 $divOptions = $this->addClass($divOptions, 'required'); | |
807 } | |
808 if (!isset($divOptions['tag'])) { | |
809 $divOptions['tag'] = 'div'; | |
810 } | |
811 } | |
812 | |
813 $label = null; | |
814 if (isset($options['label']) && $options['type'] !== 'radio') { | |
815 $label = $options['label']; | |
816 unset($options['label']); | |
817 } | |
818 | |
819 if ($options['type'] === 'radio') { | |
820 $label = false; | |
821 if (isset($options['options'])) { | |
822 $radioOptions = (array)$options['options']; | |
823 unset($options['options']); | |
824 } | |
825 } | |
826 | |
827 if ($label !== false) { | |
828 $label = $this->_inputLabel($fieldName, $label, $options); | |
829 } | |
830 | |
831 $error = $this->_extractOption('error', $options, null); | |
832 unset($options['error']); | |
833 | |
834 $selected = $this->_extractOption('selected', $options, null); | |
835 unset($options['selected']); | |
836 | |
837 if (isset($options['rows']) || isset($options['cols'])) { | |
838 $options['type'] = 'textarea'; | |
839 } | |
840 | |
841 if ($options['type'] === 'datetime' || $options['type'] === 'date' || $options['type'] === 'time' || $options['type'] === 'select') { | |
842 $options += array('empty' => false); | |
843 } | |
844 if ($options['type'] === 'datetime' || $options['type'] === 'date' || $options['type'] === 'time') { | |
845 $dateFormat = $this->_extractOption('dateFormat', $options, 'MDY'); | |
846 $timeFormat = $this->_extractOption('timeFormat', $options, 12); | |
847 unset($options['dateFormat'], $options['timeFormat']); | |
848 } | |
849 | |
850 $type = $options['type']; | |
851 $out = array_merge( | |
852 array('before' => null, 'label' => null, 'between' => null, 'input' => null, 'after' => null, 'error' => null), | |
853 array('before' => $options['before'], 'label' => $label, 'between' => $options['between'], 'after' => $options['after']) | |
854 ); | |
855 $format = null; | |
856 if (is_array($options['format']) && in_array('input', $options['format'])) { | |
857 $format = $options['format']; | |
858 } | |
859 unset($options['type'], $options['before'], $options['between'], $options['after'], $options['format']); | |
860 | |
861 switch ($type) { | |
862 case 'hidden': | |
863 $input = $this->hidden($fieldName, $options); | |
864 $format = array('input'); | |
865 unset($divOptions); | |
866 break; | |
867 case 'checkbox': | |
868 $input = $this->checkbox($fieldName, $options); | |
869 $format = $format ? $format : array('before', 'input', 'between', 'label', 'after', 'error'); | |
870 break; | |
871 case 'radio': | |
872 $input = $this->radio($fieldName, $radioOptions, $options); | |
873 break; | |
874 case 'text': | |
875 case 'password': | |
876 case 'file': | |
877 $input = $this->{$type}($fieldName, $options); | |
878 break; | |
879 case 'select': | |
880 $options += array('options' => array()); | |
881 $list = $options['options']; | |
882 unset($options['options']); | |
883 $input = $this->select($fieldName, $list, $selected, $options); | |
884 break; | |
885 case 'time': | |
886 $input = $this->dateTime($fieldName, null, $timeFormat, $selected, $options); | |
887 break; | |
888 case 'date': | |
889 $input = $this->dateTime($fieldName, $dateFormat, null, $selected, $options); | |
890 break; | |
891 case 'datetime': | |
892 $input = $this->dateTime($fieldName, $dateFormat, $timeFormat, $selected, $options); | |
893 break; | |
894 case 'textarea': | |
895 default: | |
896 $input = $this->textarea($fieldName, $options + array('cols' => '30', 'rows' => '6')); | |
897 break; | |
898 } | |
899 | |
900 if ($type != 'hidden' && $error !== false) { | |
901 $errMsg = $this->error($fieldName, $error); | |
902 if ($errMsg) { | |
903 $divOptions = $this->addClass($divOptions, 'error'); | |
904 $out['error'] = $errMsg; | |
905 } | |
906 } | |
907 | |
908 $out['input'] = $input; | |
909 $format = $format ? $format : array('before', 'label', 'between', 'input', 'after', 'error'); | |
910 $output = ''; | |
911 foreach ($format as $element) { | |
912 $output .= $out[$element]; | |
913 unset($out[$element]); | |
914 } | |
915 | |
916 if (!empty($divOptions['tag'])) { | |
917 $tag = $divOptions['tag']; | |
918 unset($divOptions['tag']); | |
919 $output = $this->Html->tag($tag, $output, $divOptions); | |
920 } | |
921 return $output; | |
922 } | |
923 | |
924 /** | |
925 * Extracts a single option from an options array. | |
926 * | |
927 * @param string $name The name of the option to pull out. | |
928 * @param array $options The array of options you want to extract. | |
929 * @param mixed $default The default option value | |
930 * @return the contents of the option or default | |
931 * @access protected | |
932 */ | |
933 function _extractOption($name, $options, $default = null) { | |
934 if (array_key_exists($name, $options)) { | |
935 return $options[$name]; | |
936 } | |
937 return $default; | |
938 } | |
939 | |
940 /** | |
941 * Generate a label for an input() call. | |
942 * | |
943 * @param array $options Options for the label element. | |
944 * @return string Generated label element | |
945 * @access protected | |
946 */ | |
947 function _inputLabel($fieldName, $label, $options) { | |
948 $labelAttributes = $this->domId(array(), 'for'); | |
949 if ($options['type'] === 'date' || $options['type'] === 'datetime') { | |
950 if (isset($options['dateFormat']) && $options['dateFormat'] === 'NONE') { | |
951 $labelAttributes['for'] .= 'Hour'; | |
952 $idKey = 'hour'; | |
953 } else { | |
954 $labelAttributes['for'] .= 'Month'; | |
955 $idKey = 'month'; | |
956 } | |
957 if (isset($options['id']) && isset($options['id'][$idKey])) { | |
958 $labelAttributes['for'] = $options['id'][$idKey]; | |
959 } | |
960 } elseif ($options['type'] === 'time') { | |
961 $labelAttributes['for'] .= 'Hour'; | |
962 if (isset($options['id']) && isset($options['id']['hour'])) { | |
963 $labelAttributes['for'] = $options['id']['hour']; | |
964 } | |
965 } | |
966 | |
967 if (is_array($label)) { | |
968 $labelText = null; | |
969 if (isset($label['text'])) { | |
970 $labelText = $label['text']; | |
971 unset($label['text']); | |
972 } | |
973 $labelAttributes = array_merge($labelAttributes, $label); | |
974 } else { | |
975 $labelText = $label; | |
976 } | |
977 | |
978 if (isset($options['id']) && is_string($options['id'])) { | |
979 $labelAttributes = array_merge($labelAttributes, array('for' => $options['id'])); | |
980 } | |
981 return $this->label($fieldName, $labelText, $labelAttributes); | |
982 } | |
983 | |
984 /** | |
985 * Creates a checkbox input widget. | |
986 * | |
987 * ### Options: | |
988 * | |
989 * - `value` - the value of the checkbox | |
990 * - `checked` - boolean indicate that this checkbox is checked. | |
991 * - `hiddenField` - boolean to indicate if you want the results of checkbox() to include | |
992 * a hidden input with a value of ''. | |
993 * - `disabled` - create a disabled input. | |
994 * | |
995 * @param string $fieldName Name of a field, like this "Modelname.fieldname" | |
996 * @param array $options Array of HTML attributes. | |
997 * @return string An HTML text input element. | |
998 * @access public | |
999 * @link http://book.cakephp.org/view/1414/checkbox | |
1000 */ | |
1001 function checkbox($fieldName, $options = array()) { | |
1002 $options = $this->_initInputField($fieldName, $options) + array('hiddenField' => true); | |
1003 $value = current($this->value()); | |
1004 $output = ""; | |
1005 | |
1006 if (empty($options['value'])) { | |
1007 $options['value'] = 1; | |
1008 } elseif ( | |
1009 (!isset($options['checked']) && !empty($value) && $value === $options['value']) || | |
1010 !empty($options['checked']) | |
1011 ) { | |
1012 $options['checked'] = 'checked'; | |
1013 } | |
1014 if ($options['hiddenField']) { | |
1015 $hiddenOptions = array( | |
1016 'id' => $options['id'] . '_', 'name' => $options['name'], | |
1017 'value' => '0', 'secure' => false | |
1018 ); | |
1019 if (isset($options['disabled']) && $options['disabled'] == true) { | |
1020 $hiddenOptions['disabled'] = 'disabled'; | |
1021 } | |
1022 $output = $this->hidden($fieldName, $hiddenOptions); | |
1023 } | |
1024 unset($options['hiddenField']); | |
1025 | |
1026 return $output . sprintf( | |
1027 $this->Html->tags['checkbox'], | |
1028 $options['name'], | |
1029 $this->_parseAttributes($options, array('name'), null, ' ') | |
1030 ); | |
1031 } | |
1032 | |
1033 /** | |
1034 * Creates a set of radio widgets. Will create a legend and fieldset | |
1035 * by default. Use $options to control this | |
1036 * | |
1037 * ### Attributes: | |
1038 * | |
1039 * - `separator` - define the string in between the radio buttons | |
1040 * - `legend` - control whether or not the widget set has a fieldset & legend | |
1041 * - `value` - indicate a value that is should be checked | |
1042 * - `label` - boolean to indicate whether or not labels for widgets show be displayed | |
1043 * - `hiddenField` - boolean to indicate if you want the results of radio() to include | |
1044 * a hidden input with a value of ''. This is useful for creating radio sets that non-continuous | |
1045 * | |
1046 * @param string $fieldName Name of a field, like this "Modelname.fieldname" | |
1047 * @param array $options Radio button options array. | |
1048 * @param array $attributes Array of HTML attributes, and special attributes above. | |
1049 * @return string Completed radio widget set. | |
1050 * @access public | |
1051 * @link http://book.cakephp.org/view/1429/radio | |
1052 */ | |
1053 function radio($fieldName, $options = array(), $attributes = array()) { | |
1054 $attributes = $this->_initInputField($fieldName, $attributes); | |
1055 $legend = false; | |
1056 | |
1057 if (isset($attributes['legend'])) { | |
1058 $legend = $attributes['legend']; | |
1059 unset($attributes['legend']); | |
1060 } elseif (count($options) > 1) { | |
1061 $legend = __(Inflector::humanize($this->field()), true); | |
1062 } | |
1063 $label = true; | |
1064 | |
1065 if (isset($attributes['label'])) { | |
1066 $label = $attributes['label']; | |
1067 unset($attributes['label']); | |
1068 } | |
1069 $inbetween = null; | |
1070 | |
1071 if (isset($attributes['separator'])) { | |
1072 $inbetween = $attributes['separator']; | |
1073 unset($attributes['separator']); | |
1074 } | |
1075 | |
1076 if (isset($attributes['value'])) { | |
1077 $value = $attributes['value']; | |
1078 } else { | |
1079 $value = $this->value($fieldName); | |
1080 } | |
1081 $out = array(); | |
1082 | |
1083 $hiddenField = isset($attributes['hiddenField']) ? $attributes['hiddenField'] : true; | |
1084 unset($attributes['hiddenField']); | |
1085 | |
1086 foreach ($options as $optValue => $optTitle) { | |
1087 $optionsHere = array('value' => $optValue); | |
1088 | |
1089 if (isset($value) && $optValue == $value) { | |
1090 $optionsHere['checked'] = 'checked'; | |
1091 } | |
1092 $parsedOptions = $this->_parseAttributes( | |
1093 array_merge($attributes, $optionsHere), | |
1094 array('name', 'type', 'id'), '', ' ' | |
1095 ); | |
1096 $tagName = Inflector::camelize( | |
1097 $attributes['id'] . '_' . Inflector::slug($optValue) | |
1098 ); | |
1099 | |
1100 if ($label) { | |
1101 $optTitle = sprintf($this->Html->tags['label'], $tagName, null, $optTitle); | |
1102 } | |
1103 $out[] = sprintf( | |
1104 $this->Html->tags['radio'], $attributes['name'], | |
1105 $tagName, $parsedOptions, $optTitle | |
1106 ); | |
1107 } | |
1108 $hidden = null; | |
1109 | |
1110 if ($hiddenField) { | |
1111 if (!isset($value) || $value === '') { | |
1112 $hidden = $this->hidden($fieldName, array( | |
1113 'id' => $attributes['id'] . '_', 'value' => '', 'name' => $attributes['name'] | |
1114 )); | |
1115 } | |
1116 } | |
1117 $out = $hidden . implode($inbetween, $out); | |
1118 | |
1119 if ($legend) { | |
1120 $out = sprintf( | |
1121 $this->Html->tags['fieldset'], '', | |
1122 sprintf($this->Html->tags['legend'], $legend) . $out | |
1123 ); | |
1124 } | |
1125 return $out; | |
1126 } | |
1127 | |
1128 /** | |
1129 * Creates a text input widget. | |
1130 * | |
1131 * @param string $fieldName Name of a field, in the form "Modelname.fieldname" | |
1132 * @param array $options Array of HTML attributes. | |
1133 * @return string A generated HTML text input element | |
1134 * @access public | |
1135 * @link http://book.cakephp.org/view/1432/text | |
1136 */ | |
1137 function text($fieldName, $options = array()) { | |
1138 $options = $this->_initInputField($fieldName, array_merge( | |
1139 array('type' => 'text'), $options | |
1140 )); | |
1141 return sprintf( | |
1142 $this->Html->tags['input'], | |
1143 $options['name'], | |
1144 $this->_parseAttributes($options, array('name'), null, ' ') | |
1145 ); | |
1146 } | |
1147 | |
1148 /** | |
1149 * Creates a password input widget. | |
1150 * | |
1151 * @param string $fieldName Name of a field, like in the form "Modelname.fieldname" | |
1152 * @param array $options Array of HTML attributes. | |
1153 * @return string A generated password input. | |
1154 * @access public | |
1155 * @link http://book.cakephp.org/view/1428/password | |
1156 */ | |
1157 function password($fieldName, $options = array()) { | |
1158 $options = $this->_initInputField($fieldName, $options); | |
1159 return sprintf( | |
1160 $this->Html->tags['password'], | |
1161 $options['name'], | |
1162 $this->_parseAttributes($options, array('name'), null, ' ') | |
1163 ); | |
1164 } | |
1165 | |
1166 /** | |
1167 * Creates a textarea widget. | |
1168 * | |
1169 * ### Options: | |
1170 * | |
1171 * - `escape` - Whether or not the contents of the textarea should be escaped. Defaults to true. | |
1172 * | |
1173 * @param string $fieldName Name of a field, in the form "Modelname.fieldname" | |
1174 * @param array $options Array of HTML attributes, and special options above. | |
1175 * @return string A generated HTML text input element | |
1176 * @access public | |
1177 * @link http://book.cakephp.org/view/1433/textarea | |
1178 */ | |
1179 function textarea($fieldName, $options = array()) { | |
1180 $options = $this->_initInputField($fieldName, $options); | |
1181 $value = null; | |
1182 | |
1183 if (array_key_exists('value', $options)) { | |
1184 $value = $options['value']; | |
1185 if (!array_key_exists('escape', $options) || $options['escape'] !== false) { | |
1186 $value = h($value); | |
1187 } | |
1188 unset($options['value']); | |
1189 } | |
1190 return sprintf( | |
1191 $this->Html->tags['textarea'], | |
1192 $options['name'], | |
1193 $this->_parseAttributes($options, array('type', 'name'), null, ' '), | |
1194 $value | |
1195 ); | |
1196 } | |
1197 | |
1198 /** | |
1199 * Creates a hidden input field. | |
1200 * | |
1201 * @param string $fieldName Name of a field, in the form of "Modelname.fieldname" | |
1202 * @param array $options Array of HTML attributes. | |
1203 * @return string A generated hidden input | |
1204 * @access public | |
1205 * @link http://book.cakephp.org/view/1425/hidden | |
1206 */ | |
1207 function hidden($fieldName, $options = array()) { | |
1208 $secure = true; | |
1209 | |
1210 if (isset($options['secure'])) { | |
1211 $secure = $options['secure']; | |
1212 unset($options['secure']); | |
1213 } | |
1214 $options = $this->_initInputField($fieldName, array_merge( | |
1215 $options, array('secure' => false) | |
1216 )); | |
1217 $model = $this->model(); | |
1218 | |
1219 if ($fieldName !== '_method' && $model !== '_Token' && $secure) { | |
1220 $this->__secure(null, '' . $options['value']); | |
1221 } | |
1222 | |
1223 return sprintf( | |
1224 $this->Html->tags['hidden'], | |
1225 $options['name'], | |
1226 $this->_parseAttributes($options, array('name', 'class'), '', ' ') | |
1227 ); | |
1228 } | |
1229 | |
1230 /** | |
1231 * Creates file input widget. | |
1232 * | |
1233 * @param string $fieldName Name of a field, in the form "Modelname.fieldname" | |
1234 * @param array $options Array of HTML attributes. | |
1235 * @return string A generated file input. | |
1236 * @access public | |
1237 * @link http://book.cakephp.org/view/1424/file | |
1238 */ | |
1239 function file($fieldName, $options = array()) { | |
1240 $options = array_merge($options, array('secure' => false)); | |
1241 $options = $this->_initInputField($fieldName, $options); | |
1242 $view =& ClassRegistry::getObject('view'); | |
1243 $field = $view->entity(); | |
1244 | |
1245 foreach (array('name', 'type', 'tmp_name', 'error', 'size') as $suffix) { | |
1246 $this->__secure(array_merge($field, array($suffix))); | |
1247 } | |
1248 | |
1249 $attributes = $this->_parseAttributes($options, array('name'), '', ' '); | |
1250 return sprintf($this->Html->tags['file'], $options['name'], $attributes); | |
1251 } | |
1252 | |
1253 /** | |
1254 * Creates a `<button>` tag. The type attribute defaults to `type="submit"` | |
1255 * You can change it to a different value by using `$options['type']`. | |
1256 * | |
1257 * ### Options: | |
1258 * | |
1259 * - `escape` - HTML entity encode the $title of the button. Defaults to false. | |
1260 * | |
1261 * @param string $title The button's caption. Not automatically HTML encoded | |
1262 * @param array $options Array of options and HTML attributes. | |
1263 * @return string A HTML button tag. | |
1264 * @access public | |
1265 * @link http://book.cakephp.org/view/1415/button | |
1266 */ | |
1267 function button($title, $options = array()) { | |
1268 $options += array('type' => 'submit', 'escape' => false); | |
1269 if ($options['escape']) { | |
1270 $title = h($title); | |
1271 } | |
1272 return sprintf( | |
1273 $this->Html->tags['button'], | |
1274 $options['type'], | |
1275 $this->_parseAttributes($options, array('type'), ' ', ''), | |
1276 $title | |
1277 ); | |
1278 } | |
1279 | |
1280 /** | |
1281 * Creates a submit button element. This method will generate `<input />` elements that | |
1282 * can be used to submit, and reset forms by using $options. image submits can be created by supplying an | |
1283 * image path for $caption. | |
1284 * | |
1285 * ### Options | |
1286 * | |
1287 * - `div` - Include a wrapping div? Defaults to true. Accepts sub options similar to | |
1288 * FormHelper::input(). | |
1289 * - `before` - Content to include before the input. | |
1290 * - `after` - Content to include after the input. | |
1291 * - `type` - Set to 'reset' for reset inputs. Defaults to 'submit' | |
1292 * - Other attributes will be assigned to the input element. | |
1293 * | |
1294 * ### Options | |
1295 * | |
1296 * - `div` - Include a wrapping div? Defaults to true. Accepts sub options similar to | |
1297 * FormHelper::input(). | |
1298 * - Other attributes will be assigned to the input element. | |
1299 * | |
1300 * @param string $caption The label appearing on the button OR if string contains :// or the | |
1301 * extension .jpg, .jpe, .jpeg, .gif, .png use an image if the extension | |
1302 * exists, AND the first character is /, image is relative to webroot, | |
1303 * OR if the first character is not /, image is relative to webroot/img. | |
1304 * @param array $options Array of options. See above. | |
1305 * @return string A HTML submit button | |
1306 * @access public | |
1307 * @link http://book.cakephp.org/view/1431/submit | |
1308 */ | |
1309 function submit($caption = null, $options = array()) { | |
1310 if (!is_string($caption) && empty($caption)) { | |
1311 $caption = __('Submit', true); | |
1312 } | |
1313 $out = null; | |
1314 $div = true; | |
1315 | |
1316 if (isset($options['div'])) { | |
1317 $div = $options['div']; | |
1318 unset($options['div']); | |
1319 } | |
1320 $options += array('type' => 'submit', 'before' => null, 'after' => null); | |
1321 $divOptions = array('tag' => 'div'); | |
1322 | |
1323 if ($div === true) { | |
1324 $divOptions['class'] = 'submit'; | |
1325 } elseif ($div === false) { | |
1326 unset($divOptions); | |
1327 } elseif (is_string($div)) { | |
1328 $divOptions['class'] = $div; | |
1329 } elseif (is_array($div)) { | |
1330 $divOptions = array_merge(array('class' => 'submit', 'tag' => 'div'), $div); | |
1331 } | |
1332 | |
1333 $before = $options['before']; | |
1334 $after = $options['after']; | |
1335 unset($options['before'], $options['after']); | |
1336 | |
1337 if (strpos($caption, '://') !== false) { | |
1338 unset($options['type']); | |
1339 $out .= $before . sprintf( | |
1340 $this->Html->tags['submitimage'], | |
1341 $caption, | |
1342 $this->_parseAttributes($options, null, '', ' ') | |
1343 ) . $after; | |
1344 } elseif (preg_match('/\.(jpg|jpe|jpeg|gif|png|ico)$/', $caption)) { | |
1345 unset($options['type']); | |
1346 if ($caption{0} !== '/') { | |
1347 $url = $this->webroot(IMAGES_URL . $caption); | |
1348 } else { | |
1349 $caption = trim($caption, '/'); | |
1350 $url = $this->webroot($caption); | |
1351 } | |
1352 $out .= $before . sprintf( | |
1353 $this->Html->tags['submitimage'], | |
1354 $url, | |
1355 $this->_parseAttributes($options, null, '', ' ') | |
1356 ) . $after; | |
1357 } else { | |
1358 $options['value'] = $caption; | |
1359 $out .= $before . sprintf( | |
1360 $this->Html->tags['submit'], | |
1361 $this->_parseAttributes($options, null, '', ' ') | |
1362 ). $after; | |
1363 } | |
1364 | |
1365 if (isset($divOptions)) { | |
1366 $tag = $divOptions['tag']; | |
1367 unset($divOptions['tag']); | |
1368 $out = $this->Html->tag($tag, $out, $divOptions); | |
1369 } | |
1370 return $out; | |
1371 } | |
1372 | |
1373 /** | |
1374 * Returns a formatted SELECT element. | |
1375 * | |
1376 * ### Attributes: | |
1377 * | |
1378 * - `showParents` - If included in the array and set to true, an additional option element | |
1379 * will be added for the parent of each option group. You can set an option with the same name | |
1380 * and it's key will be used for the value of the option. | |
1381 * - `multiple` - show a multiple select box. If set to 'checkbox' multiple checkboxes will be | |
1382 * created instead. | |
1383 * - `empty` - If true, the empty select option is shown. If a string, | |
1384 * that string is displayed as the empty element. | |
1385 * - `escape` - If true contents of options will be HTML entity encoded. Defaults to true. | |
1386 * - `class` - When using multiple = checkbox the classname to apply to the divs. Defaults to 'checkbox'. | |
1387 * | |
1388 * ### Using options | |
1389 * | |
1390 * A simple array will create normal options: | |
1391 * | |
1392 * {{{ | |
1393 * $options = array(1 => 'one', 2 => 'two); | |
1394 * $this->Form->select('Model.field', $options)); | |
1395 * }}} | |
1396 * | |
1397 * While a nested options array will create optgroups with options inside them. | |
1398 * {{{ | |
1399 * $options = array( | |
1400 * 1 => 'bill', | |
1401 * 'fred' => array( | |
1402 * 2 => 'fred', | |
1403 * 3 => 'fred jr.' | |
1404 * ) | |
1405 * ); | |
1406 * $this->Form->select('Model.field', $options); | |
1407 * }}} | |
1408 * | |
1409 * In the above `2 => 'fred'` will not generate an option element. You should enable the `showParents` | |
1410 * attribute to show the fred option. | |
1411 * | |
1412 * @param string $fieldName Name attribute of the SELECT | |
1413 * @param array $options Array of the OPTION elements (as 'value'=>'Text' pairs) to be used in the | |
1414 * SELECT element | |
1415 * @param mixed $selected The option selected by default. If null, the default value | |
1416 * from POST data will be used when available. | |
1417 * @param array $attributes The HTML attributes of the select element. | |
1418 * @return string Formatted SELECT element | |
1419 * @access public | |
1420 * @link http://book.cakephp.org/view/1430/select | |
1421 */ | |
1422 function select($fieldName, $options = array(), $selected = null, $attributes = array()) { | |
1423 $select = array(); | |
1424 $style = null; | |
1425 $tag = null; | |
1426 $attributes += array( | |
1427 'class' => null, | |
1428 'escape' => true, | |
1429 'secure' => null, | |
1430 'empty' => '', | |
1431 'showParents' => false, | |
1432 'hiddenField' => true | |
1433 ); | |
1434 | |
1435 $escapeOptions = $this->_extractOption('escape', $attributes); | |
1436 $secure = $this->_extractOption('secure', $attributes); | |
1437 $showEmpty = $this->_extractOption('empty', $attributes); | |
1438 $showParents = $this->_extractOption('showParents', $attributes); | |
1439 $hiddenField = $this->_extractOption('hiddenField', $attributes); | |
1440 unset($attributes['escape'], $attributes['secure'], $attributes['empty'], $attributes['showParents'], $attributes['hiddenField']); | |
1441 | |
1442 $attributes = $this->_initInputField($fieldName, array_merge( | |
1443 (array)$attributes, array('secure' => false) | |
1444 )); | |
1445 | |
1446 if (is_string($options) && isset($this->__options[$options])) { | |
1447 $options = $this->__generateOptions($options); | |
1448 } elseif (!is_array($options)) { | |
1449 $options = array(); | |
1450 } | |
1451 if (isset($attributes['type'])) { | |
1452 unset($attributes['type']); | |
1453 } | |
1454 | |
1455 if (!isset($selected)) { | |
1456 $selected = $attributes['value']; | |
1457 } | |
1458 | |
1459 if (!empty($attributes['multiple'])) { | |
1460 $style = ($attributes['multiple'] === 'checkbox') ? 'checkbox' : null; | |
1461 $template = ($style) ? 'checkboxmultiplestart' : 'selectmultiplestart'; | |
1462 $tag = $this->Html->tags[$template]; | |
1463 if ($hiddenField) { | |
1464 $hiddenAttributes = array( | |
1465 'value' => '', | |
1466 'id' => $attributes['id'] . ($style ? '' : '_'), | |
1467 'secure' => false, | |
1468 'name' => $attributes['name'] | |
1469 ); | |
1470 $select[] = $this->hidden(null, $hiddenAttributes); | |
1471 } | |
1472 } else { | |
1473 $tag = $this->Html->tags['selectstart']; | |
1474 } | |
1475 | |
1476 if (!empty($tag) || isset($template)) { | |
1477 if (!isset($secure) || $secure == true) { | |
1478 $this->__secure(); | |
1479 } | |
1480 $select[] = sprintf($tag, $attributes['name'], $this->_parseAttributes( | |
1481 $attributes, array('name', 'value')) | |
1482 ); | |
1483 } | |
1484 $emptyMulti = ( | |
1485 $showEmpty !== null && $showEmpty !== false && !( | |
1486 empty($showEmpty) && (isset($attributes) && | |
1487 array_key_exists('multiple', $attributes)) | |
1488 ) | |
1489 ); | |
1490 | |
1491 if ($emptyMulti) { | |
1492 $showEmpty = ($showEmpty === true) ? '' : $showEmpty; | |
1493 $options = array_reverse($options, true); | |
1494 $options[''] = $showEmpty; | |
1495 $options = array_reverse($options, true); | |
1496 } | |
1497 | |
1498 $select = array_merge($select, $this->__selectOptions( | |
1499 array_reverse($options, true), | |
1500 $selected, | |
1501 array(), | |
1502 $showParents, | |
1503 array('escape' => $escapeOptions, 'style' => $style, 'name' => $attributes['name'], 'class' => $attributes['class']) | |
1504 )); | |
1505 | |
1506 $template = ($style == 'checkbox') ? 'checkboxmultipleend' : 'selectend'; | |
1507 $select[] = $this->Html->tags[$template]; | |
1508 return implode("\n", $select); | |
1509 } | |
1510 | |
1511 /** | |
1512 * Returns a SELECT element for days. | |
1513 * | |
1514 * ### Attributes: | |
1515 * | |
1516 * - `empty` - If true, the empty select option is shown. If a string, | |
1517 * that string is displayed as the empty element. | |
1518 * | |
1519 * @param string $fieldName Prefix name for the SELECT element | |
1520 * @param string $selected Option which is selected. | |
1521 * @param array $attributes HTML attributes for the select element | |
1522 * @return string A generated day select box. | |
1523 * @access public | |
1524 * @link http://book.cakephp.org/view/1419/day | |
1525 */ | |
1526 function day($fieldName, $selected = null, $attributes = array()) { | |
1527 $attributes += array('empty' => true); | |
1528 $selected = $this->__dateTimeSelected('day', $fieldName, $selected, $attributes); | |
1529 | |
1530 if (strlen($selected) > 2) { | |
1531 $selected = date('d', strtotime($selected)); | |
1532 } elseif ($selected === false) { | |
1533 $selected = null; | |
1534 } | |
1535 return $this->select($fieldName . ".day", $this->__generateOptions('day'), $selected, $attributes); | |
1536 } | |
1537 | |
1538 /** | |
1539 * Returns a SELECT element for years | |
1540 * | |
1541 * ### Attributes: | |
1542 * | |
1543 * - `empty` - If true, the empty select option is shown. If a string, | |
1544 * that string is displayed as the empty element. | |
1545 * - `orderYear` - Ordering of year values in select options. | |
1546 * Possible values 'asc', 'desc'. Default 'desc' | |
1547 * | |
1548 * @param string $fieldName Prefix name for the SELECT element | |
1549 * @param integer $minYear First year in sequence | |
1550 * @param integer $maxYear Last year in sequence | |
1551 * @param string $selected Option which is selected. | |
1552 * @param array $attributes Attribute array for the select elements. | |
1553 * @return string Completed year select input | |
1554 * @access public | |
1555 * @link http://book.cakephp.org/view/1416/year | |
1556 */ | |
1557 function year($fieldName, $minYear = null, $maxYear = null, $selected = null, $attributes = array()) { | |
1558 $attributes += array('empty' => true); | |
1559 if ((empty($selected) || $selected === true) && $value = $this->value($fieldName)) { | |
1560 if (is_array($value)) { | |
1561 extract($value); | |
1562 $selected = $year; | |
1563 } else { | |
1564 if (empty($value)) { | |
1565 if (!$attributes['empty'] && !$maxYear) { | |
1566 $selected = 'now'; | |
1567 | |
1568 } elseif (!$attributes['empty'] && $maxYear && !$selected) { | |
1569 $selected = $maxYear; | |
1570 } | |
1571 } else { | |
1572 $selected = $value; | |
1573 } | |
1574 } | |
1575 } | |
1576 | |
1577 if (strlen($selected) > 4 || $selected === 'now') { | |
1578 $selected = date('Y', strtotime($selected)); | |
1579 } elseif ($selected === false) { | |
1580 $selected = null; | |
1581 } | |
1582 $yearOptions = array('min' => $minYear, 'max' => $maxYear, 'order' => 'desc'); | |
1583 if (isset($attributes['orderYear'])) { | |
1584 $yearOptions['order'] = $attributes['orderYear']; | |
1585 unset($attributes['orderYear']); | |
1586 } | |
1587 return $this->select( | |
1588 $fieldName . '.year', $this->__generateOptions('year', $yearOptions), | |
1589 $selected, $attributes | |
1590 ); | |
1591 } | |
1592 | |
1593 /** | |
1594 * Returns a SELECT element for months. | |
1595 * | |
1596 * ### Attributes: | |
1597 * | |
1598 * - `monthNames` - If false, 2 digit numbers will be used instead of text. | |
1599 * If a array, the given array will be used. | |
1600 * - `empty` - If true, the empty select option is shown. If a string, | |
1601 * that string is displayed as the empty element. | |
1602 * | |
1603 * @param string $fieldName Prefix name for the SELECT element | |
1604 * @param string $selected Option which is selected. | |
1605 * @param array $attributes Attributes for the select element | |
1606 * @return string A generated month select dropdown. | |
1607 * @access public | |
1608 * @link http://book.cakephp.org/view/1417/month | |
1609 */ | |
1610 function month($fieldName, $selected = null, $attributes = array()) { | |
1611 $attributes += array('empty' => true); | |
1612 $selected = $this->__dateTimeSelected('month', $fieldName, $selected, $attributes); | |
1613 | |
1614 if (strlen($selected) > 2) { | |
1615 $selected = date('m', strtotime($selected)); | |
1616 } elseif ($selected === false) { | |
1617 $selected = null; | |
1618 } | |
1619 $defaults = array('monthNames' => true); | |
1620 $attributes = array_merge($defaults, (array) $attributes); | |
1621 $monthNames = $attributes['monthNames']; | |
1622 unset($attributes['monthNames']); | |
1623 | |
1624 return $this->select( | |
1625 $fieldName . ".month", | |
1626 $this->__generateOptions('month', array('monthNames' => $monthNames)), | |
1627 $selected, $attributes | |
1628 ); | |
1629 } | |
1630 | |
1631 /** | |
1632 * Returns a SELECT element for hours. | |
1633 * | |
1634 * ### Attributes: | |
1635 * | |
1636 * - `empty` - If true, the empty select option is shown. If a string, | |
1637 * that string is displayed as the empty element. | |
1638 * | |
1639 * @param string $fieldName Prefix name for the SELECT element | |
1640 * @param boolean $format24Hours True for 24 hours format | |
1641 * @param string $selected Option which is selected. | |
1642 * @param array $attributes List of HTML attributes | |
1643 * @return string Completed hour select input | |
1644 * @access public | |
1645 * @link http://book.cakephp.org/view/1420/hour | |
1646 */ | |
1647 function hour($fieldName, $format24Hours = false, $selected = null, $attributes = array()) { | |
1648 $attributes += array('empty' => true); | |
1649 $selected = $this->__dateTimeSelected('hour', $fieldName, $selected, $attributes); | |
1650 | |
1651 if (strlen($selected) > 2) { | |
1652 if ($format24Hours) { | |
1653 $selected = date('H', strtotime($selected)); | |
1654 } else { | |
1655 $selected = date('g', strtotime($selected)); | |
1656 } | |
1657 } elseif ($selected === false) { | |
1658 $selected = null; | |
1659 } | |
1660 return $this->select( | |
1661 $fieldName . ".hour", | |
1662 $this->__generateOptions($format24Hours ? 'hour24' : 'hour'), | |
1663 $selected, $attributes | |
1664 ); | |
1665 } | |
1666 | |
1667 /** | |
1668 * Returns a SELECT element for minutes. | |
1669 * | |
1670 * ### Attributes: | |
1671 * | |
1672 * - `empty` - If true, the empty select option is shown. If a string, | |
1673 * that string is displayed as the empty element. | |
1674 * | |
1675 * @param string $fieldName Prefix name for the SELECT element | |
1676 * @param string $selected Option which is selected. | |
1677 * @param string $attributes Array of Attributes | |
1678 * @return string Completed minute select input. | |
1679 * @access public | |
1680 * @link http://book.cakephp.org/view/1421/minute | |
1681 */ | |
1682 function minute($fieldName, $selected = null, $attributes = array()) { | |
1683 $attributes += array('empty' => true); | |
1684 $selected = $this->__dateTimeSelected('min', $fieldName, $selected, $attributes); | |
1685 | |
1686 if (strlen($selected) > 2) { | |
1687 $selected = date('i', strtotime($selected)); | |
1688 } elseif ($selected === false) { | |
1689 $selected = null; | |
1690 } | |
1691 $minuteOptions = array(); | |
1692 | |
1693 if (isset($attributes['interval'])) { | |
1694 $minuteOptions['interval'] = $attributes['interval']; | |
1695 unset($attributes['interval']); | |
1696 } | |
1697 return $this->select( | |
1698 $fieldName . ".min", $this->__generateOptions('minute', $minuteOptions), | |
1699 $selected, $attributes | |
1700 ); | |
1701 } | |
1702 | |
1703 /** | |
1704 * Selects values for dateTime selects. | |
1705 * | |
1706 * @param string $select Name of element field. ex. 'day' | |
1707 * @param string $fieldName Name of fieldName being generated ex. Model.created | |
1708 * @param mixed $selected The current selected value. | |
1709 * @param array $attributes Array of attributes, must contain 'empty' key. | |
1710 * @return string Currently selected value. | |
1711 * @access private | |
1712 */ | |
1713 function __dateTimeSelected($select, $fieldName, $selected, $attributes) { | |
1714 if ((empty($selected) || $selected === true) && $value = $this->value($fieldName)) { | |
1715 if (is_array($value) && isset($value[$select])) { | |
1716 $selected = $value[$select]; | |
1717 } else { | |
1718 if (empty($value)) { | |
1719 if (!$attributes['empty']) { | |
1720 $selected = 'now'; | |
1721 } | |
1722 } else { | |
1723 $selected = $value; | |
1724 } | |
1725 } | |
1726 } | |
1727 return $selected; | |
1728 } | |
1729 | |
1730 /** | |
1731 * Returns a SELECT element for AM or PM. | |
1732 * | |
1733 * ### Attributes: | |
1734 * | |
1735 * - `empty` - If true, the empty select option is shown. If a string, | |
1736 * that string is displayed as the empty element. | |
1737 * | |
1738 * @param string $fieldName Prefix name for the SELECT element | |
1739 * @param string $selected Option which is selected. | |
1740 * @param string $attributes Array of Attributes | |
1741 * @param bool $showEmpty Show/Hide an empty option | |
1742 * @return string Completed meridian select input | |
1743 * @access public | |
1744 * @link http://book.cakephp.org/view/1422/meridian | |
1745 */ | |
1746 function meridian($fieldName, $selected = null, $attributes = array()) { | |
1747 $attributes += array('empty' => true); | |
1748 if ((empty($selected) || $selected === true) && $value = $this->value($fieldName)) { | |
1749 if (is_array($value)) { | |
1750 extract($value); | |
1751 $selected = $meridian; | |
1752 } else { | |
1753 if (empty($value)) { | |
1754 if (!$attribues['empty']) { | |
1755 $selected = date('a'); | |
1756 } | |
1757 } else { | |
1758 $selected = date('a', strtotime($value)); | |
1759 } | |
1760 } | |
1761 } | |
1762 | |
1763 if ($selected === false) { | |
1764 $selected = null; | |
1765 } | |
1766 return $this->select( | |
1767 $fieldName . ".meridian", $this->__generateOptions('meridian'), | |
1768 $selected, $attributes | |
1769 ); | |
1770 } | |
1771 | |
1772 /** | |
1773 * Returns a set of SELECT elements for a full datetime setup: day, month and year, and then time. | |
1774 * | |
1775 * ### Attributes: | |
1776 * | |
1777 * - `monthNames` If false, 2 digit numbers will be used instead of text. | |
1778 * If a array, the given array will be used. | |
1779 * - `minYear` The lowest year to use in the year select | |
1780 * - `maxYear` The maximum year to use in the year select | |
1781 * - `interval` The interval for the minutes select. Defaults to 1 | |
1782 * - `separator` The contents of the string between select elements. Defaults to '-' | |
1783 * - `empty` - If true, the empty select option is shown. If a string, | |
1784 * that string is displayed as the empty element. | |
1785 * - `value` | `default` The default value to be used by the input. A value in `$this->data` | |
1786 * matching the field name will override this value. If no default is provided `time()` will be used. | |
1787 * | |
1788 * @param string $fieldName Prefix name for the SELECT element | |
1789 * @param string $dateFormat DMY, MDY, YMD. | |
1790 * @param string $timeFormat 12, 24. | |
1791 * @param string $selected Option which is selected. | |
1792 * @param string $attributes array of Attributes | |
1793 * @return string Generated set of select boxes for the date and time formats chosen. | |
1794 * @access public | |
1795 * @link http://book.cakephp.org/view/1418/dateTime | |
1796 */ | |
1797 function dateTime($fieldName, $dateFormat = 'DMY', $timeFormat = '12', $selected = null, $attributes = array()) { | |
1798 $attributes += array('empty' => true); | |
1799 $year = $month = $day = $hour = $min = $meridian = null; | |
1800 | |
1801 if (empty($selected)) { | |
1802 $selected = $this->value($attributes, $fieldName); | |
1803 if (isset($selected['value'])) { | |
1804 $selected = $selected['value']; | |
1805 } else { | |
1806 $selected = null; | |
1807 } | |
1808 } | |
1809 | |
1810 if ($selected === null && $attributes['empty'] != true) { | |
1811 $selected = time(); | |
1812 } | |
1813 | |
1814 if (!empty($selected)) { | |
1815 if (is_array($selected)) { | |
1816 extract($selected); | |
1817 } else { | |
1818 if (is_numeric($selected)) { | |
1819 $selected = strftime('%Y-%m-%d %H:%M:%S', $selected); | |
1820 } | |
1821 $meridian = 'am'; | |
1822 $pos = strpos($selected, '-'); | |
1823 if ($pos !== false) { | |
1824 $date = explode('-', $selected); | |
1825 $days = explode(' ', $date[2]); | |
1826 $day = $days[0]; | |
1827 $month = $date[1]; | |
1828 $year = $date[0]; | |
1829 } else { | |
1830 $days[1] = $selected; | |
1831 } | |
1832 | |
1833 if (!empty($timeFormat)) { | |
1834 $time = explode(':', $days[1]); | |
1835 | |
1836 if (($time[0] > 12) && $timeFormat == '12') { | |
1837 $time[0] = $time[0] - 12; | |
1838 $meridian = 'pm'; | |
1839 } elseif ($time[0] == '00' && $timeFormat == '12') { | |
1840 $time[0] = 12; | |
1841 } elseif ($time[0] > 12) { | |
1842 $meridian = 'pm'; | |
1843 } | |
1844 if ($time[0] == 0 && $timeFormat == '12') { | |
1845 $time[0] = 12; | |
1846 } | |
1847 $hour = $min = null; | |
1848 if (isset($time[1])) { | |
1849 $hour = $time[0]; | |
1850 $min = $time[1]; | |
1851 } | |
1852 } | |
1853 } | |
1854 } | |
1855 | |
1856 $elements = array('Day', 'Month', 'Year', 'Hour', 'Minute', 'Meridian'); | |
1857 $defaults = array( | |
1858 'minYear' => null, 'maxYear' => null, 'separator' => '-', | |
1859 'interval' => 1, 'monthNames' => true | |
1860 ); | |
1861 $attributes = array_merge($defaults, (array) $attributes); | |
1862 if (isset($attributes['minuteInterval'])) { | |
1863 $attributes['interval'] = $attributes['minuteInterval']; | |
1864 unset($attributes['minuteInterval']); | |
1865 } | |
1866 $minYear = $attributes['minYear']; | |
1867 $maxYear = $attributes['maxYear']; | |
1868 $separator = $attributes['separator']; | |
1869 $interval = $attributes['interval']; | |
1870 $monthNames = $attributes['monthNames']; | |
1871 $attributes = array_diff_key($attributes, $defaults); | |
1872 | |
1873 if (isset($attributes['id'])) { | |
1874 if (is_string($attributes['id'])) { | |
1875 // build out an array version | |
1876 foreach ($elements as $element) { | |
1877 $selectAttrName = 'select' . $element . 'Attr'; | |
1878 ${$selectAttrName} = $attributes; | |
1879 ${$selectAttrName}['id'] = $attributes['id'] . $element; | |
1880 } | |
1881 } elseif (is_array($attributes['id'])) { | |
1882 // check for missing ones and build selectAttr for each element | |
1883 $attributes['id'] += array( | |
1884 'month' => '', 'year' => '', 'day' => '', | |
1885 'hour' => '', 'minute' => '', 'meridian' => '' | |
1886 ); | |
1887 foreach ($elements as $element) { | |
1888 $selectAttrName = 'select' . $element . 'Attr'; | |
1889 ${$selectAttrName} = $attributes; | |
1890 ${$selectAttrName}['id'] = $attributes['id'][strtolower($element)]; | |
1891 } | |
1892 } | |
1893 } else { | |
1894 // build the selectAttrName with empty id's to pass | |
1895 foreach ($elements as $element) { | |
1896 $selectAttrName = 'select' . $element . 'Attr'; | |
1897 ${$selectAttrName} = $attributes; | |
1898 } | |
1899 } | |
1900 | |
1901 $selects = array(); | |
1902 foreach (preg_split('//', $dateFormat, -1, PREG_SPLIT_NO_EMPTY) as $char) { | |
1903 switch ($char) { | |
1904 case 'Y': | |
1905 $selects[] = $this->year( | |
1906 $fieldName, $minYear, $maxYear, $year, $selectYearAttr | |
1907 ); | |
1908 break; | |
1909 case 'M': | |
1910 $selectMonthAttr['monthNames'] = $monthNames; | |
1911 $selects[] = $this->month($fieldName, $month, $selectMonthAttr); | |
1912 break; | |
1913 case 'D': | |
1914 $selects[] = $this->day($fieldName, $day, $selectDayAttr); | |
1915 break; | |
1916 } | |
1917 } | |
1918 $opt = implode($separator, $selects); | |
1919 | |
1920 if (!empty($interval) && $interval > 1 && !empty($min)) { | |
1921 $min = round($min * (1 / $interval)) * $interval; | |
1922 } | |
1923 $selectMinuteAttr['interval'] = $interval; | |
1924 switch ($timeFormat) { | |
1925 case '24': | |
1926 $opt .= $this->hour($fieldName, true, $hour, $selectHourAttr) . ':' . | |
1927 $this->minute($fieldName, $min, $selectMinuteAttr); | |
1928 break; | |
1929 case '12': | |
1930 $opt .= $this->hour($fieldName, false, $hour, $selectHourAttr) . ':' . | |
1931 $this->minute($fieldName, $min, $selectMinuteAttr) . ' ' . | |
1932 $this->meridian($fieldName, $meridian, $selectMeridianAttr); | |
1933 break; | |
1934 default: | |
1935 $opt .= ''; | |
1936 break; | |
1937 } | |
1938 return $opt; | |
1939 } | |
1940 | |
1941 /** | |
1942 * Gets the input field name for the current tag | |
1943 * | |
1944 * @param array $options | |
1945 * @param string $key | |
1946 * @return array | |
1947 * @access protected | |
1948 */ | |
1949 function _name($options = array(), $field = null, $key = 'name') { | |
1950 if ($this->requestType == 'get') { | |
1951 if ($options === null) { | |
1952 $options = array(); | |
1953 } elseif (is_string($options)) { | |
1954 $field = $options; | |
1955 $options = 0; | |
1956 } | |
1957 | |
1958 if (!empty($field)) { | |
1959 $this->setEntity($field); | |
1960 } | |
1961 | |
1962 if (is_array($options) && isset($options[$key])) { | |
1963 return $options; | |
1964 } | |
1965 | |
1966 $view = ClassRegistry::getObject('view'); | |
1967 $name = !empty($view->field) ? $view->field : $view->model; | |
1968 if (!empty($view->fieldSuffix)) { | |
1969 $name .= '[' . $view->fieldSuffix . ']'; | |
1970 } | |
1971 | |
1972 if (is_array($options)) { | |
1973 $options[$key] = $name; | |
1974 return $options; | |
1975 } else { | |
1976 return $name; | |
1977 } | |
1978 } | |
1979 return parent::_name($options, $field, $key); | |
1980 } | |
1981 | |
1982 /** | |
1983 * Returns an array of formatted OPTION/OPTGROUP elements | |
1984 * @access private | |
1985 * @return array | |
1986 */ | |
1987 function __selectOptions($elements = array(), $selected = null, $parents = array(), $showParents = null, $attributes = array()) { | |
1988 $select = array(); | |
1989 $attributes = array_merge(array('escape' => true, 'style' => null, 'class' => null), $attributes); | |
1990 $selectedIsEmpty = ($selected === '' || $selected === null); | |
1991 $selectedIsArray = is_array($selected); | |
1992 | |
1993 foreach ($elements as $name => $title) { | |
1994 $htmlOptions = array(); | |
1995 if (is_array($title) && (!isset($title['name']) || !isset($title['value']))) { | |
1996 if (!empty($name)) { | |
1997 if ($attributes['style'] === 'checkbox') { | |
1998 $select[] = $this->Html->tags['fieldsetend']; | |
1999 } else { | |
2000 $select[] = $this->Html->tags['optiongroupend']; | |
2001 } | |
2002 $parents[] = $name; | |
2003 } | |
2004 $select = array_merge($select, $this->__selectOptions( | |
2005 $title, $selected, $parents, $showParents, $attributes | |
2006 )); | |
2007 | |
2008 if (!empty($name)) { | |
2009 $name = $attributes['escape'] ? h($name) : $name; | |
2010 if ($attributes['style'] === 'checkbox') { | |
2011 $select[] = sprintf($this->Html->tags['fieldsetstart'], $name); | |
2012 } else { | |
2013 $select[] = sprintf($this->Html->tags['optiongroup'], $name, ''); | |
2014 } | |
2015 } | |
2016 $name = null; | |
2017 } elseif (is_array($title)) { | |
2018 $htmlOptions = $title; | |
2019 $name = $title['value']; | |
2020 $title = $title['name']; | |
2021 unset($htmlOptions['name'], $htmlOptions['value']); | |
2022 } | |
2023 | |
2024 if ($name !== null) { | |
2025 if ( | |
2026 (!$selectedIsArray && !$selectedIsEmpty && (string)$selected == (string)$name) || | |
2027 ($selectedIsArray && in_array($name, $selected)) | |
2028 ) { | |
2029 if ($attributes['style'] === 'checkbox') { | |
2030 $htmlOptions['checked'] = true; | |
2031 } else { | |
2032 $htmlOptions['selected'] = 'selected'; | |
2033 } | |
2034 } | |
2035 | |
2036 if ($showParents || (!in_array($title, $parents))) { | |
2037 $title = ($attributes['escape']) ? h($title) : $title; | |
2038 | |
2039 if ($attributes['style'] === 'checkbox') { | |
2040 $htmlOptions['value'] = $name; | |
2041 | |
2042 $tagName = Inflector::camelize( | |
2043 $this->model() . '_' . $this->field().'_'.Inflector::slug($name) | |
2044 ); | |
2045 $htmlOptions['id'] = $tagName; | |
2046 $label = array('for' => $tagName); | |
2047 | |
2048 if (isset($htmlOptions['checked']) && $htmlOptions['checked'] === true) { | |
2049 $label['class'] = 'selected'; | |
2050 } | |
2051 | |
2052 $name = $attributes['name']; | |
2053 | |
2054 if (empty($attributes['class'])) { | |
2055 $attributes['class'] = 'checkbox'; | |
2056 } elseif ($attributes['class'] === 'form-error') { | |
2057 $attributes['class'] = 'checkbox ' . $attributes['class']; | |
2058 } | |
2059 $label = $this->label(null, $title, $label); | |
2060 $item = sprintf( | |
2061 $this->Html->tags['checkboxmultiple'], $name, | |
2062 $this->_parseAttributes($htmlOptions) | |
2063 ); | |
2064 $select[] = $this->Html->div($attributes['class'], $item . $label); | |
2065 } else { | |
2066 $select[] = sprintf( | |
2067 $this->Html->tags['selectoption'], | |
2068 $name, $this->_parseAttributes($htmlOptions), $title | |
2069 ); | |
2070 } | |
2071 } | |
2072 } | |
2073 } | |
2074 | |
2075 return array_reverse($select, true); | |
2076 } | |
2077 | |
2078 /** | |
2079 * Generates option lists for common <select /> menus | |
2080 * @access private | |
2081 */ | |
2082 function __generateOptions($name, $options = array()) { | |
2083 if (!empty($this->options[$name])) { | |
2084 return $this->options[$name]; | |
2085 } | |
2086 $data = array(); | |
2087 | |
2088 switch ($name) { | |
2089 case 'minute': | |
2090 if (isset($options['interval'])) { | |
2091 $interval = $options['interval']; | |
2092 } else { | |
2093 $interval = 1; | |
2094 } | |
2095 $i = 0; | |
2096 while ($i < 60) { | |
2097 $data[sprintf('%02d', $i)] = sprintf('%02d', $i); | |
2098 $i += $interval; | |
2099 } | |
2100 break; | |
2101 case 'hour': | |
2102 for ($i = 1; $i <= 12; $i++) { | |
2103 $data[sprintf('%02d', $i)] = $i; | |
2104 } | |
2105 break; | |
2106 case 'hour24': | |
2107 for ($i = 0; $i <= 23; $i++) { | |
2108 $data[sprintf('%02d', $i)] = $i; | |
2109 } | |
2110 break; | |
2111 case 'meridian': | |
2112 $data = array('am' => 'am', 'pm' => 'pm'); | |
2113 break; | |
2114 case 'day': | |
2115 $min = 1; | |
2116 $max = 31; | |
2117 | |
2118 if (isset($options['min'])) { | |
2119 $min = $options['min']; | |
2120 } | |
2121 if (isset($options['max'])) { | |
2122 $max = $options['max']; | |
2123 } | |
2124 | |
2125 for ($i = $min; $i <= $max; $i++) { | |
2126 $data[sprintf('%02d', $i)] = $i; | |
2127 } | |
2128 break; | |
2129 case 'month': | |
2130 if ($options['monthNames'] === true) { | |
2131 $data['01'] = __('January', true); | |
2132 $data['02'] = __('February', true); | |
2133 $data['03'] = __('March', true); | |
2134 $data['04'] = __('April', true); | |
2135 $data['05'] = __('May', true); | |
2136 $data['06'] = __('June', true); | |
2137 $data['07'] = __('July', true); | |
2138 $data['08'] = __('August', true); | |
2139 $data['09'] = __('September', true); | |
2140 $data['10'] = __('October', true); | |
2141 $data['11'] = __('November', true); | |
2142 $data['12'] = __('December', true); | |
2143 } else if (is_array($options['monthNames'])) { | |
2144 $data = $options['monthNames']; | |
2145 } else { | |
2146 for ($m = 1; $m <= 12; $m++) { | |
2147 $data[sprintf("%02s", $m)] = strftime("%m", mktime(1, 1, 1, $m, 1, 1999)); | |
2148 } | |
2149 } | |
2150 break; | |
2151 case 'year': | |
2152 $current = intval(date('Y')); | |
2153 | |
2154 if (!isset($options['min'])) { | |
2155 $min = $current - 20; | |
2156 } else { | |
2157 $min = $options['min']; | |
2158 } | |
2159 | |
2160 if (!isset($options['max'])) { | |
2161 $max = $current + 20; | |
2162 } else { | |
2163 $max = $options['max']; | |
2164 } | |
2165 if ($min > $max) { | |
2166 list($min, $max) = array($max, $min); | |
2167 } | |
2168 for ($i = $min; $i <= $max; $i++) { | |
2169 $data[$i] = $i; | |
2170 } | |
2171 if ($options['order'] != 'asc') { | |
2172 $data = array_reverse($data, true); | |
2173 } | |
2174 break; | |
2175 } | |
2176 $this->__options[$name] = $data; | |
2177 return $this->__options[$name]; | |
2178 } | |
2179 | |
2180 /** | |
2181 * Sets field defaults and adds field to form security input hash | |
2182 * | |
2183 * Options | |
2184 * | |
2185 * - `secure` - boolean whether or not the field should be added to the security fields. | |
2186 * | |
2187 * @param string $field Name of the field to initialize options for. | |
2188 * @param array $options Array of options to append options into. | |
2189 * @return array Array of options for the input. | |
2190 * @access protected | |
2191 */ | |
2192 function _initInputField($field, $options = array()) { | |
2193 if (isset($options['secure'])) { | |
2194 $secure = $options['secure']; | |
2195 unset($options['secure']); | |
2196 } else { | |
2197 $secure = (isset($this->params['_Token']) && !empty($this->params['_Token'])); | |
2198 } | |
2199 | |
2200 $fieldName = null; | |
2201 if ($secure && !empty($options['name'])) { | |
2202 preg_match_all('/\[(.*?)\]/', $options['name'], $matches); | |
2203 if (isset($matches[1])) { | |
2204 $fieldName = $matches[1]; | |
2205 } | |
2206 } | |
2207 | |
2208 $result = parent::_initInputField($field, $options); | |
2209 | |
2210 if ($secure) { | |
2211 $this->__secure($fieldName); | |
2212 } | |
2213 return $result; | |
2214 } | |
2215 } |