~tsep-dev/tsep/0.9-beta

« back to all changes in this revision

Viewing changes to branches/symfony/cake/libs/view/helpers/form.php

  • Committer: geoffreyfishing
  • Date: 2011-01-11 23:46:12 UTC
  • Revision ID: svn-v4:ae0de26e-ed09-4cbe-9a20-e40b4c60ac6c::125
Created a symfony branch for future migration to symfony

Show diffs side-by-side

added added

removed removed

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