1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
15: App::uses('ClassRegistry', 'Utility');
16: App::uses('AppHelper', 'View/Helper');
17:
18: 19: 20: 21: 22: 23: 24: 25: 26:
27: class FormHelper extends AppHelper {
28:
29: 30: 31: 32: 33:
34: public $helpers = array('Html');
35:
36: 37: 38: 39: 40:
41: protected $_options = array(
42: 'day' => array(), 'minute' => array(), 'hour' => array(),
43: 'month' => array(), 'year' => array(), 'meridian' => array()
44: );
45:
46: 47: 48: 49: 50:
51: public $fields = array();
52:
53: 54: 55: 56: 57: 58:
59: const SECURE_SKIP = 'skip';
60:
61: 62: 63: 64: 65:
66: public $requestType = null;
67:
68: 69: 70: 71: 72:
73: public $defaultModel = null;
74:
75: 76: 77: 78: 79:
80: protected $_inputDefaults = array();
81:
82: 83: 84: 85: 86: 87: 88: 89:
90: protected $_unlockedFields = array();
91:
92: 93: 94: 95: 96: 97:
98: protected $_models = array();
99:
100: 101: 102: 103: 104: 105: 106:
107: public $validationErrors = array();
108:
109: 110: 111: 112: 113: 114:
115: public function __construct(View $View, $settings = array()) {
116: parent::__construct($View, $settings);
117: $this->validationErrors =& $View->validationErrors;
118: }
119:
120: 121: 122: 123: 124: 125: 126:
127: protected function _getModel($model) {
128: $object = null;
129: if (!$model || $model === 'Model') {
130: return $object;
131: }
132:
133: if (array_key_exists($model, $this->_models)) {
134: return $this->_models[$model];
135: }
136:
137: if (ClassRegistry::isKeySet($model)) {
138: $object = ClassRegistry::getObject($model);
139: } elseif (isset($this->request->params['models'][$model])) {
140: $plugin = $this->request->params['models'][$model]['plugin'];
141: $plugin .= ($plugin) ? '.' : null;
142: $object = ClassRegistry::init(array(
143: 'class' => $plugin . $this->request->params['models'][$model]['className'],
144: 'alias' => $model
145: ));
146: } elseif (ClassRegistry::isKeySet($this->defaultModel)) {
147: $defaultObject = ClassRegistry::getObject($this->defaultModel);
148: if (in_array($model, array_keys($defaultObject->getAssociated()), true) && isset($defaultObject->{$model})) {
149: $object = $defaultObject->{$model};
150: }
151: } else {
152: $object = ClassRegistry::init($model, true);
153: }
154:
155: $this->_models[$model] = $object;
156: if (!$object) {
157: return null;
158: }
159:
160: $this->fieldset[$model] = array('fields' => null, 'key' => $object->primaryKey, 'validates' => null);
161: return $object;
162: }
163:
164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183:
184: protected function _introspectModel($model, $key, $field = null) {
185: $object = $this->_getModel($model);
186: if (!$object) {
187: return;
188: }
189:
190: if ($key === 'key') {
191: return $this->fieldset[$model]['key'] = $object->primaryKey;
192: }
193:
194: if ($key === 'fields') {
195: if (!isset($this->fieldset[$model]['fields'])) {
196: $fields = $this->fieldset[$model]['fields'] = $object->schema();
197: foreach ($object->hasAndBelongsToMany as $alias => $assocData) {
198: $this->fieldset[$object->alias]['fields'][$alias] = array('type' => 'multiple');
199: }
200: }
201: if (empty($field)) {
202: return $this->fieldset[$model]['fields'];
203: } elseif (isset($this->fieldset[$model]['fields'][$field])) {
204: return $this->fieldset[$model]['fields'][$field];
205: } else {
206: return isset($object->hasAndBelongsToMany[$field]) ? array('type' => 'multiple') : null;
207: }
208: }
209:
210: if ($key === 'errors' && !isset($this->validationErrors[$model])) {
211: $this->validationErrors[$model] =& $object->validationErrors;
212: return $this->validationErrors[$model];
213: } elseif ($key === 'errors' && isset($this->validationErrors[$model])) {
214: return $this->validationErrors[$model];
215: }
216:
217: if ($key === 'validates' && !isset($this->fieldset[$model]['validates'])) {
218: $validates = array();
219: if (!empty($object->validate)) {
220: foreach ($object->validate as $validateField => $validateProperties) {
221: if ($this->_isRequiredField($validateProperties)) {
222: $validates[$validateField] = true;
223: }
224: }
225: }
226: $this->fieldset[$model]['validates'] = $validates;
227: }
228:
229: if ($key === 'validates') {
230: if (empty($field)) {
231: return $this->fieldset[$model]['validates'];
232: } else {
233: return isset($this->fieldset[$model]['validates'][$field]) ?
234: $this->fieldset[$model]['validates'] : null;
235: }
236: }
237: }
238:
239: 240: 241: 242: 243: 244:
245: protected function _isRequiredField($validateProperties) {
246: $required = false;
247: if (is_string($validateProperties)) {
248: return true;
249: } elseif (is_array($validateProperties)) {
250:
251: $dims = Set::countDim($validateProperties);
252: if ($dims == 1 || ($dims == 2 && isset($validateProperties['rule']))) {
253: $validateProperties = array($validateProperties);
254: }
255:
256: foreach ($validateProperties as $rule => $validateProp) {
257: if (isset($validateProp['allowEmpty']) && $validateProp['allowEmpty'] === true) {
258: return false;
259: }
260: $rule = isset($validateProp['rule']) ? $validateProp['rule'] : false;
261: $required = $rule || empty($validateProp);
262: if ($required) {
263: break;
264: }
265: }
266: }
267: return $required;
268: }
269:
270: 271: 272: 273: 274: 275: 276: 277:
278: public function tagIsInvalid() {
279: $entity = $this->entity();
280: $model = array_shift($entity);
281: $errors = array();
282: if (!empty($entity) && isset($this->validationErrors[$model])) {
283: $errors = $this->validationErrors[$model];
284: }
285: if (!empty($entity) && empty($errors)) {
286: $errors = $this->_introspectModel($model, 'errors');
287: }
288: if (empty($errors)) {
289: return false;
290: }
291: $errors = Set::classicExtract($errors, join('.', $entity));
292: return $errors === null ? false : $errors;
293: }
294:
295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318:
319: public function create($model = null, $options = array()) {
320: $created = $id = false;
321: $append = '';
322:
323: if (is_array($model) && empty($options)) {
324: $options = $model;
325: $model = null;
326: }
327:
328: if (empty($model) && $model !== false && !empty($this->request->params['models'])) {
329: $model = key($this->request->params['models']);
330: } elseif (empty($model) && empty($this->request->params['models'])) {
331: $model = false;
332: }
333: $this->defaultModel = $model;
334:
335: $key = null;
336: if ($model !== false) {
337: $object = $this->_getModel($model);
338: $key = $this->_introspectModel($model, 'key');
339: $this->setEntity($model, true);
340: }
341:
342: if ($model !== false && $key) {
343: $recordExists = (
344: isset($this->request->data[$model]) &&
345: !empty($this->request->data[$model][$key]) &&
346: !is_array($this->request->data[$model][$key])
347: );
348:
349: if ($recordExists) {
350: $created = true;
351: $id = $this->request->data[$model][$key];
352: }
353: }
354:
355: $options = array_merge(array(
356: 'type' => ($created && empty($options['action'])) ? 'put' : 'post',
357: 'action' => null,
358: 'url' => null,
359: 'default' => true,
360: 'encoding' => strtolower(Configure::read('App.encoding')),
361: 'inputDefaults' => array()),
362: $options);
363: $this->_inputDefaults = $options['inputDefaults'];
364: unset($options['inputDefaults']);
365:
366: if (!isset($options['id'])) {
367: $domId = isset($options['action']) ? $options['action'] : $this->request['action'];
368: $options['id'] = $this->domId($domId . 'Form');
369: }
370:
371: if ($options['action'] === null && $options['url'] === null) {
372: $options['action'] = $this->request->here(false);
373: } elseif (empty($options['url']) || is_array($options['url'])) {
374: if (empty($options['url']['controller'])) {
375: if (!empty($model)) {
376: $options['url']['controller'] = Inflector::underscore(Inflector::pluralize($model));
377: } elseif (!empty($this->request->params['controller'])) {
378: $options['url']['controller'] = Inflector::underscore($this->request->params['controller']);
379: }
380: }
381: if (empty($options['action'])) {
382: $options['action'] = $this->request->params['action'];
383: }
384:
385: $plugin = null;
386: if ($this->plugin) {
387: $plugin = Inflector::underscore($this->plugin);
388: }
389: $actionDefaults = array(
390: 'plugin' => $plugin,
391: 'controller' => $this->_View->viewPath,
392: 'action' => $options['action'],
393: );
394: $options['action'] = array_merge($actionDefaults, (array)$options['url']);
395: if (empty($options['action'][0]) && !empty($id)) {
396: $options['action'][0] = $id;
397: }
398: } elseif (is_string($options['url'])) {
399: $options['action'] = $options['url'];
400: }
401: unset($options['url']);
402:
403: switch (strtolower($options['type'])) {
404: case 'get':
405: $htmlAttributes['method'] = 'get';
406: break;
407: case 'file':
408: $htmlAttributes['enctype'] = 'multipart/form-data';
409: $options['type'] = ($created) ? 'put' : 'post';
410: case 'post':
411: case 'put':
412: case 'delete':
413: $append .= $this->hidden('_method', array(
414: 'name' => '_method', 'value' => strtoupper($options['type']), 'id' => null,
415: 'secure' => self::SECURE_SKIP
416: ));
417: default:
418: $htmlAttributes['method'] = 'post';
419: break;
420: }
421: $this->requestType = strtolower($options['type']);
422:
423: $action = $this->url($options['action']);
424: unset($options['type'], $options['action']);
425:
426: if ($options['default'] == false) {
427: if (!isset($options['onsubmit'])) {
428: $options['onsubmit'] = '';
429: }
430: $htmlAttributes['onsubmit'] = $options['onsubmit'] . 'event.returnValue = false; return false;';
431: }
432: unset($options['default']);
433:
434: if (!empty($options['encoding'])) {
435: $htmlAttributes['accept-charset'] = $options['encoding'];
436: unset($options['encoding']);
437: }
438:
439: $htmlAttributes = array_merge($options, $htmlAttributes);
440:
441: $this->fields = array();
442: $append .= $this->_csrfField();
443:
444: if (!empty($append)) {
445: $append = $this->Html->useTag('block', ' style="display:none;"', $append);
446: }
447:
448: if ($model !== false) {
449: $this->setEntity($model, true);
450: $this->_introspectModel($model, 'fields');
451: }
452: return $this->Html->useTag('form', $action, $htmlAttributes) . $append;
453: }
454:
455: 456: 457: 458: 459: 460:
461: protected function _csrfField() {
462: if (empty($this->request->params['_Token'])) {
463: return '';
464: }
465: if (!empty($this->request['_Token']['unlockedFields'])) {
466: foreach ((array)$this->request['_Token']['unlockedFields'] as $unlocked) {
467: $this->_unlockedFields[] = $unlocked;
468: }
469: }
470: return $this->hidden('_Token.key', array(
471: 'value' => $this->request->params['_Token']['key'], 'id' => 'Token' . mt_rand(),
472: 'secure' => self::SECURE_SKIP
473: ));
474: }
475:
476: 477: 478: 479: 480: 481: 482: 483: 484: 485: 486: 487: 488: 489: 490: 491: 492: 493: 494: 495:
496: public function end($options = null) {
497: $out = null;
498: $submit = null;
499:
500: if ($options !== null) {
501: $submitOptions = array();
502: if (is_string($options)) {
503: $submit = $options;
504: } else {
505: if (isset($options['label'])) {
506: $submit = $options['label'];
507: unset($options['label']);
508: }
509: $submitOptions = $options;
510: }
511: $out .= $this->submit($submit, $submitOptions);
512: }
513: if (isset($this->request['_Token']) && !empty($this->request['_Token'])) {
514: $out .= $this->secure($this->fields);
515: $this->fields = array();
516: }
517: $this->setEntity(null);
518: $out .= $this->Html->useTag('formend');
519:
520: $this->_View->modelScope = false;
521: return $out;
522: }
523:
524: 525: 526: 527: 528: 529: 530:
531: public function secure($fields = array()) {
532: if (!isset($this->request['_Token']) || empty($this->request['_Token'])) {
533: return;
534: }
535: $locked = array();
536: $unlockedFields = $this->_unlockedFields;
537:
538: foreach ($fields as $key => $value) {
539: if (!is_int($key)) {
540: $locked[$key] = $value;
541: unset($fields[$key]);
542: }
543: }
544:
545: sort($unlockedFields, SORT_STRING);
546: sort($fields, SORT_STRING);
547: ksort($locked, SORT_STRING);
548: $fields += $locked;
549:
550: $locked = implode(array_keys($locked), '|');
551: $unlocked = implode($unlockedFields, '|');
552: $fields = Security::hash(serialize($fields) . $unlocked . Configure::read('Security.salt'));
553:
554: $out = $this->hidden('_Token.fields', array(
555: 'value' => urlencode($fields . ':' . $locked),
556: 'id' => 'TokenFields' . mt_rand()
557: ));
558: $out .= $this->hidden('_Token.unlocked', array(
559: 'value' => urlencode($unlocked),
560: 'id' => 'TokenUnlocked' . mt_rand()
561: ));
562: return $this->Html->useTag('block', ' style="display:none;"', $out);
563: }
564:
565: 566: 567: 568: 569: 570: 571: 572: 573: 574:
575: public function unlockField($name = null) {
576: if ($name === null) {
577: return $this->_unlockedFields;
578: }
579: if (!in_array($name, $this->_unlockedFields)) {
580: $this->_unlockedFields[] = $name;
581: }
582: $index = array_search($name, $this->fields);
583: if ($index !== false) {
584: unset($this->fields[$index]);
585: }
586: unset($this->fields[$name]);
587: }
588:
589: 590: 591: 592: 593: 594: 595: 596: 597: 598:
599: protected function _secure($lock, $field = null, $value = null) {
600: if (!$field) {
601: $field = $this->entity();
602: } elseif (is_string($field)) {
603: $field = Set::filter(explode('.', $field), true);
604: }
605:
606: foreach ($this->_unlockedFields as $unlockField) {
607: $unlockParts = explode('.', $unlockField);
608: if (array_values(array_intersect($field, $unlockParts)) === $unlockParts) {
609: return;
610: }
611: }
612:
613: $field = implode('.', $field);
614: $field = preg_replace('/(\.\d+)+$/', '', $field);
615:
616: if ($lock) {
617: if (!in_array($field, $this->fields)) {
618: if ($value !== null) {
619: return $this->fields[$field] = $value;
620: }
621: $this->fields[] = $field;
622: }
623: } else {
624: $this->unlockField($field);
625: }
626: }
627:
628: 629: 630: 631: 632: 633: 634:
635: public function isFieldError($field) {
636: $this->setEntity($field);
637: return (bool)$this->tagIsInvalid();
638: }
639:
640: 641: 642: 643: 644: 645: 646: 647: 648: 649: 650: 651: 652: 653: 654: 655: 656:
657: public function error($field, $text = null, $options = array()) {
658: $defaults = array('wrap' => true, 'class' => 'error-message', 'escape' => true);
659: $options = array_merge($defaults, $options);
660: $this->setEntity($field);
661:
662: $error = $this->tagIsInvalid();
663: if ($error === false) {
664: return null;
665: }
666: if (is_array($text)) {
667: if (isset($text['attributes']) && is_array($text['attributes'])) {
668: $options = array_merge($options, $text['attributes']);
669: unset($text['attributes']);
670: }
671: $tmp = array();
672: foreach ($error as &$e) {
673: if (isset($text[$e])) {
674: $tmp[] = $text[$e];
675: } else {
676: $tmp[] = $e;
677: }
678: }
679: $text = $tmp;
680: }
681:
682: if ($text !== null) {
683: $error = $text;
684: }
685: if (is_array($error)) {
686: foreach ($error as &$e) {
687: if (is_numeric($e)) {
688: $e = __d('cake', 'Error in field %s', Inflector::humanize($this->field()));
689: }
690: }
691: }
692: if ($options['escape']) {
693: $error = h($error);
694: unset($options['escape']);
695: }
696: if (is_array($error)) {
697: if (count($error) > 1) {
698: $listParams = array();
699: if (isset($options['listOptions'])) {
700: if (is_string($options['listOptions'])) {
701: $listParams[] = $options['listOptions'];
702: } else {
703: if (isset($options['listOptions']['itemOptions'])) {
704: $listParams[] = $options['listOptions']['itemOptions'];
705: unset($options['listOptions']['itemOptions']);
706: } else {
707: $listParams[] = array();
708: }
709: if (isset($options['listOptions']['tag'])) {
710: $listParams[] = $options['listOptions']['tag'];
711: unset($options['listOptions']['tag']);
712: }
713: array_unshift($listParams, $options['listOptions']);
714: }
715: unset($options['listOptions']);
716: }
717: array_unshift($listParams, $error);
718: $error = call_user_func_array(array($this->Html, 'nestedList'), $listParams);
719: } else {
720: $error = array_pop($error);
721: }
722: }
723: if ($options['wrap']) {
724: $tag = is_string($options['wrap']) ? $options['wrap'] : 'div';
725: unset($options['wrap']);
726: return $this->Html->tag($tag, $error, $options);
727: } else {
728: return $error;
729: }
730: }
731:
732: 733: 734: 735: 736: 737: 738: 739: 740: 741: 742: 743: 744: 745: 746: 747: 748: 749: 750: 751: 752: 753: 754: 755: 756: 757: 758: 759: 760: 761: 762: 763: 764: 765: 766: 767: 768: 769: 770: 771: 772: 773: 774: 775: 776: 777: 778: 779: 780: 781:
782: public function label($fieldName = null, $text = null, $options = array()) {
783: if (empty($fieldName)) {
784: $fieldName = implode('.', $this->entity());
785: }
786:
787: if ($text === null) {
788: if (strpos($fieldName, '.') !== false) {
789: $fieldElements = explode('.', $fieldName);
790: $text = array_pop($fieldElements);
791: } else {
792: $text = $fieldName;
793: }
794: if (substr($text, -3) == '_id') {
795: $text = substr($text, 0, -3);
796: }
797: $text = __(Inflector::humanize(Inflector::underscore($text)));
798: }
799:
800: if (is_string($options)) {
801: $options = array('class' => $options);
802: }
803:
804: if (isset($options['for'])) {
805: $labelFor = $options['for'];
806: unset($options['for']);
807: } else {
808: $labelFor = $this->domId($fieldName);
809: }
810:
811: return $this->Html->useTag('label', $labelFor, $options, $text);
812: }
813:
814: 815: 816: 817: 818: 819: 820: 821: 822: 823: 824: 825: 826: 827: 828: 829: 830: 831: 832: 833: 834: 835: 836: 837: 838: 839: 840:
841: public function inputs($fields = null, $blacklist = null) {
842: $fieldset = $legend = true;
843: $model = $this->model();
844: if (is_array($fields)) {
845: if (array_key_exists('legend', $fields)) {
846: $legend = $fields['legend'];
847: unset($fields['legend']);
848: }
849:
850: if (isset($fields['fieldset'])) {
851: $fieldset = $fields['fieldset'];
852: unset($fields['fieldset']);
853: }
854: } elseif ($fields !== null) {
855: $fieldset = $legend = $fields;
856: if (!is_bool($fieldset)) {
857: $fieldset = true;
858: }
859: $fields = array();
860: }
861:
862: if (empty($fields)) {
863: $fields = array_keys($this->_introspectModel($model, 'fields'));
864: }
865:
866: if ($legend === true) {
867: $actionName = __d('cake', 'New %s');
868: $isEdit = (
869: strpos($this->request->params['action'], 'update') !== false ||
870: strpos($this->request->params['action'], 'edit') !== false
871: );
872: if ($isEdit) {
873: $actionName = __d('cake', 'Edit %s');
874: }
875: $modelName = Inflector::humanize(Inflector::underscore($model));
876: $legend = sprintf($actionName, __($modelName));
877: }
878:
879: $out = null;
880: foreach ($fields as $name => $options) {
881: if (is_numeric($name) && !is_array($options)) {
882: $name = $options;
883: $options = array();
884: }
885: $entity = explode('.', $name);
886: $blacklisted = (
887: is_array($blacklist) &&
888: (in_array($name, $blacklist) || in_array(end($entity), $blacklist))
889: );
890: if ($blacklisted) {
891: continue;
892: }
893: $out .= $this->input($name, $options);
894: }
895:
896: if (is_string($fieldset)) {
897: $fieldsetClass = sprintf(' class="%s"', $fieldset);
898: } else {
899: $fieldsetClass = '';
900: }
901:
902: if ($fieldset && $legend) {
903: return $this->Html->useTag('fieldset', $fieldsetClass, $this->Html->useTag('legend', $legend) . $out);
904: } elseif ($fieldset) {
905: return $this->Html->useTag('fieldset', $fieldsetClass, $out);
906: } else {
907: return $out;
908: }
909: }
910:
911: 912: 913: 914: 915: 916: 917: 918: 919: 920: 921: 922: 923: 924: 925: 926: 927: 928: 929: 930: 931: 932: 933: 934: 935: 936: 937: 938: 939: 940: 941:
942: public function input($fieldName, $options = array()) {
943: $this->setEntity($fieldName);
944:
945: $options = array_merge(
946: array('before' => null, 'between' => null, 'after' => null, 'format' => null),
947: $this->_inputDefaults,
948: $options
949: );
950:
951: $modelKey = $this->model();
952: $fieldKey = $this->field();
953:
954: if (!isset($options['type'])) {
955: $magicType = true;
956: $options['type'] = 'text';
957: if (isset($options['options'])) {
958: $options['type'] = 'select';
959: } elseif (in_array($fieldKey, array('psword', 'passwd', 'password'))) {
960: $options['type'] = 'password';
961: } elseif (isset($options['checked'])) {
962: $options['type'] = 'checkbox';
963: } elseif ($fieldDef = $this->_introspectModel($modelKey, 'fields', $fieldKey)) {
964: $type = $fieldDef['type'];
965: $primaryKey = $this->fieldset[$modelKey]['key'];
966: }
967:
968: if (isset($type)) {
969: $map = array(
970: 'string' => 'text', 'datetime' => 'datetime',
971: 'boolean' => 'checkbox', 'timestamp' => 'datetime',
972: 'text' => 'textarea', 'time' => 'time',
973: 'date' => 'date', 'float' => 'number',
974: 'integer' => 'number'
975: );
976:
977: if (isset($this->map[$type])) {
978: $options['type'] = $this->map[$type];
979: } elseif (isset($map[$type])) {
980: $options['type'] = $map[$type];
981: }
982: if ($fieldKey == $primaryKey) {
983: $options['type'] = 'hidden';
984: }
985: if (
986: $options['type'] === 'number' &&
987: $type === 'float' &&
988: !isset($options['step'])
989: ) {
990: $options['step'] = 'any';
991: }
992: }
993: if (preg_match('/_id$/', $fieldKey) && $options['type'] !== 'hidden') {
994: $options['type'] = 'select';
995: }
996:
997: if ($modelKey === $fieldKey) {
998: $options['type'] = 'select';
999: if (!isset($options['multiple'])) {
1000: $options['multiple'] = 'multiple';
1001: }
1002: }
1003: }
1004: $types = array('checkbox', 'radio', 'select');
1005:
1006: if (
1007: (!isset($options['options']) && in_array($options['type'], $types)) ||
1008: (isset($magicType) && $options['type'] == 'text')
1009: ) {
1010: $varName = Inflector::variable(
1011: Inflector::pluralize(preg_replace('/_id$/', '', $fieldKey))
1012: );
1013: $varOptions = $this->_View->getVar($varName);
1014: if (is_array($varOptions)) {
1015: if ($options['type'] !== 'radio') {
1016: $options['type'] = 'select';
1017: }
1018: $options['options'] = $varOptions;
1019: }
1020: }
1021:
1022: $autoLength = (!array_key_exists('maxlength', $options) && isset($fieldDef['length']));
1023: if ($autoLength && $options['type'] == 'text') {
1024: $options['maxlength'] = $fieldDef['length'];
1025: }
1026: if ($autoLength && $fieldDef['type'] == 'float') {
1027: $options['maxlength'] = array_sum(explode(',', $fieldDef['length'])) + 1;
1028: }
1029:
1030: $divOptions = array();
1031: $div = $this->_extractOption('div', $options, true);
1032: unset($options['div']);
1033:
1034: if (!empty($div)) {
1035: $divOptions['class'] = 'input';
1036: $divOptions = $this->addClass($divOptions, $options['type']);
1037: if (is_string($div)) {
1038: $divOptions['class'] = $div;
1039: } elseif (is_array($div)) {
1040: $divOptions = array_merge($divOptions, $div);
1041: }
1042: if ($this->_introspectModel($modelKey, 'validates', $fieldKey)) {
1043: $divOptions = $this->addClass($divOptions, 'required');
1044: }
1045: if (!isset($divOptions['tag'])) {
1046: $divOptions['tag'] = 'div';
1047: }
1048: }
1049:
1050: $label = null;
1051: if (isset($options['label']) && $options['type'] !== 'radio') {
1052: $label = $options['label'];
1053: unset($options['label']);
1054: }
1055:
1056: if ($options['type'] === 'radio') {
1057: $label = false;
1058: if (isset($options['options'])) {
1059: $radioOptions = (array)$options['options'];
1060: unset($options['options']);
1061: }
1062: }
1063:
1064: if ($label !== false) {
1065: $label = $this->_inputLabel($fieldName, $label, $options);
1066: }
1067:
1068: $error = $this->_extractOption('error', $options, null);
1069: unset($options['error']);
1070:
1071: $selected = $this->_extractOption('selected', $options, null);
1072: unset($options['selected']);
1073:
1074: if (isset($options['rows']) || isset($options['cols'])) {
1075: $options['type'] = 'textarea';
1076: }
1077:
1078: if ($options['type'] === 'datetime' || $options['type'] === 'date' || $options['type'] === 'time' || $options['type'] === 'select') {
1079: $options += array('empty' => false);
1080: }
1081: if ($options['type'] === 'datetime' || $options['type'] === 'date' || $options['type'] === 'time') {
1082: $dateFormat = $this->_extractOption('dateFormat', $options, 'MDY');
1083: $timeFormat = $this->_extractOption('timeFormat', $options, 12);
1084: unset($options['dateFormat'], $options['timeFormat']);
1085: }
1086:
1087: $type = $options['type'];
1088: $out = array_merge(
1089: array('before' => null, 'label' => null, 'between' => null, 'input' => null, 'after' => null, 'error' => null),
1090: array('before' => $options['before'], 'label' => $label, 'between' => $options['between'], 'after' => $options['after'])
1091: );
1092: $format = null;
1093: if (is_array($options['format']) && in_array('input', $options['format'])) {
1094: $format = $options['format'];
1095: }
1096: unset($options['type'], $options['before'], $options['between'], $options['after'], $options['format']);
1097:
1098: switch ($type) {
1099: case 'hidden':
1100: $input = $this->hidden($fieldName, $options);
1101: $format = array('input');
1102: unset($divOptions);
1103: break;
1104: case 'checkbox':
1105: $input = $this->checkbox($fieldName, $options);
1106: $format = $format ? $format : array('before', 'input', 'between', 'label', 'after', 'error');
1107: break;
1108: case 'radio':
1109: if (isset($out['between'])) {
1110: $options['between'] = $out['between'];
1111: $out['between'] = null;
1112: }
1113: $input = $this->radio($fieldName, $radioOptions, $options);
1114: break;
1115: case 'file':
1116: $input = $this->file($fieldName, $options);
1117: break;
1118: case 'select':
1119: $options += array('options' => array(), 'value' => $selected);
1120: $list = $options['options'];
1121: unset($options['options']);
1122: $input = $this->select($fieldName, $list, $options);
1123: break;
1124: case 'time':
1125: $options['value'] = $selected;
1126: $input = $this->dateTime($fieldName, null, $timeFormat, $options);
1127: break;
1128: case 'date':
1129: $options['value'] = $selected;
1130: $input = $this->dateTime($fieldName, $dateFormat, null, $options);
1131: break;
1132: case 'datetime':
1133: $options['value'] = $selected;
1134: $input = $this->dateTime($fieldName, $dateFormat, $timeFormat, $options);
1135: break;
1136: case 'textarea':
1137: $input = $this->textarea($fieldName, $options + array('cols' => '30', 'rows' => '6'));
1138: break;
1139: case 'url':
1140: $input = $this->text($fieldName, array('type' => 'url') + $options);
1141: break;
1142: default:
1143: $input = $this->{$type}($fieldName, $options);
1144: }
1145:
1146: if ($type != 'hidden' && $error !== false) {
1147: $errMsg = $this->error($fieldName, $error);
1148: if ($errMsg) {
1149: $divOptions = $this->addClass($divOptions, 'error');
1150: $out['error'] = $errMsg;
1151: }
1152: }
1153:
1154: $out['input'] = $input;
1155: $format = $format ? $format : array('before', 'label', 'between', 'input', 'after', 'error');
1156: $output = '';
1157: foreach ($format as $element) {
1158: $output .= $out[$element];
1159: unset($out[$element]);
1160: }
1161:
1162: if (!empty($divOptions['tag'])) {
1163: $tag = $divOptions['tag'];
1164: unset($divOptions['tag']);
1165: $output = $this->Html->tag($tag, $output, $divOptions);
1166: }
1167: return $output;
1168: }
1169:
1170: 1171: 1172: 1173: 1174: 1175: 1176: 1177:
1178: protected function _extractOption($name, $options, $default = null) {
1179: if (array_key_exists($name, $options)) {
1180: return $options[$name];
1181: }
1182: return $default;
1183: }
1184:
1185: 1186: 1187: 1188: 1189: 1190: 1191: 1192: 1193: 1194: 1195: 1196:
1197: protected function _inputLabel($fieldName, $label, $options) {
1198: $labelAttributes = $this->domId(array(), 'for');
1199: $idKey = null;
1200: if ($options['type'] === 'date' || $options['type'] === 'datetime') {
1201: $firstInput = 'M';
1202: if (
1203: array_key_exists('dateFormat', $options) &&
1204: ($options['dateFormat'] === null || $options['dateFormat'] === 'NONE')
1205: ) {
1206: $firstInput = 'H';
1207: } elseif (!empty($options['dateFormat'])) {
1208: $firstInput = substr($options['dateFormat'], 0, 1);
1209: }
1210: switch ($firstInput) {
1211: case 'D':
1212: $idKey = 'day';
1213: $labelAttributes['for'] .= 'Day';
1214: break;
1215: case 'Y':
1216: $idKey = 'year';
1217: $labelAttributes['for'] .= 'Year';
1218: break;
1219: case 'M':
1220: $idKey = 'month';
1221: $labelAttributes['for'] .= 'Month';
1222: break;
1223: case 'H':
1224: $idKey = 'hour';
1225: $labelAttributes['for'] .= 'Hour';
1226: }
1227: }
1228: if ($options['type'] === 'time') {
1229: $labelAttributes['for'] .= 'Hour';
1230: $idKey = 'hour';
1231: }
1232: if (isset($idKey) && isset($options['id']) && isset($options['id'][$idKey])) {
1233: $labelAttributes['for'] = $options['id'][$idKey];
1234: }
1235:
1236: if (is_array($label)) {
1237: $labelText = null;
1238: if (isset($label['text'])) {
1239: $labelText = $label['text'];
1240: unset($label['text']);
1241: }
1242: $labelAttributes = array_merge($labelAttributes, $label);
1243: } else {
1244: $labelText = $label;
1245: }
1246:
1247: if (isset($options['id']) && is_string($options['id'])) {
1248: $labelAttributes = array_merge($labelAttributes, array('for' => $options['id']));
1249: }
1250: return $this->label($fieldName, $labelText, $labelAttributes);
1251: }
1252:
1253: 1254: 1255: 1256: 1257: 1258: 1259: 1260: 1261: 1262: 1263: 1264: 1265: 1266: 1267: 1268: 1269: 1270: 1271:
1272: public function checkbox($fieldName, $options = array()) {
1273: $valueOptions = array();
1274: if (isset($options['default'])) {
1275: $valueOptions['default'] = $options['default'];
1276: unset($options['default']);
1277: }
1278:
1279: $options = $this->_initInputField($fieldName, $options) + array('hiddenField' => true);
1280: $value = current($this->value($valueOptions));
1281: $output = "";
1282:
1283: if (empty($options['value'])) {
1284: $options['value'] = 1;
1285: }
1286: if (
1287: (!isset($options['checked']) && !empty($value) && $value == $options['value']) ||
1288: !empty($options['checked'])
1289: ) {
1290: $options['checked'] = 'checked';
1291: }
1292: if ($options['hiddenField']) {
1293: $hiddenOptions = array(
1294: 'id' => $options['id'] . '_',
1295: 'name' => $options['name'],
1296: 'value' => ($options['hiddenField'] !== true ? $options['hiddenField'] : '0'),
1297: 'secure' => false
1298: );
1299: if (isset($options['disabled']) && $options['disabled'] == true) {
1300: $hiddenOptions['disabled'] = 'disabled';
1301: }
1302: $output = $this->hidden($fieldName, $hiddenOptions);
1303: }
1304: unset($options['hiddenField']);
1305:
1306: return $output . $this->Html->useTag('checkbox', $options['name'], array_diff_key($options, array('name' => '')));
1307: }
1308:
1309: 1310: 1311: 1312: 1313: 1314: 1315: 1316: 1317: 1318: 1319: 1320: 1321: 1322: 1323: 1324: 1325: 1326: 1327: 1328: 1329:
1330: public function radio($fieldName, $options = array(), $attributes = array()) {
1331: $attributes = $this->_initInputField($fieldName, $attributes);
1332:
1333: $legend = false;
1334: if (isset($attributes['legend'])) {
1335: $legend = $attributes['legend'];
1336: unset($attributes['legend']);
1337: } elseif (count($options) > 1) {
1338: $legend = __(Inflector::humanize($this->field()));
1339: }
1340:
1341: $label = true;
1342: if (isset($attributes['label'])) {
1343: $label = $attributes['label'];
1344: unset($attributes['label']);
1345: }
1346:
1347: $separator = null;
1348: if (isset($attributes['separator'])) {
1349: $separator = $attributes['separator'];
1350: unset($attributes['separator']);
1351: }
1352:
1353: $between = null;
1354: if (isset($attributes['between'])) {
1355: $between = $attributes['between'];
1356: unset($attributes['between']);
1357: }
1358:
1359: $value = null;
1360: if (isset($attributes['value'])) {
1361: $value = $attributes['value'];
1362: } else {
1363: $value = $this->value($fieldName);
1364: }
1365:
1366: $disabled = array();
1367: if (isset($attributes['disabled'])) {
1368: $disabled = $attributes['disabled'];
1369: }
1370:
1371: $out = array();
1372:
1373: $hiddenField = isset($attributes['hiddenField']) ? $attributes['hiddenField'] : true;
1374: unset($attributes['hiddenField']);
1375:
1376: foreach ($options as $optValue => $optTitle) {
1377: $optionsHere = array('value' => $optValue);
1378:
1379: if (isset($value) && $optValue == $value) {
1380: $optionsHere['checked'] = 'checked';
1381: }
1382: if ($disabled && (!is_array($disabled) || in_array($optValue, $disabled))) {
1383: $optionsHere['disabled'] = true;
1384: }
1385: $tagName = Inflector::camelize(
1386: $attributes['id'] . '_' . Inflector::slug($optValue)
1387: );
1388:
1389: if ($label) {
1390: $optTitle = $this->Html->useTag('label', $tagName, '', $optTitle);
1391: }
1392: $allOptions = array_merge($attributes, $optionsHere);
1393: $out[] = $this->Html->useTag('radio', $attributes['name'], $tagName,
1394: array_diff_key($allOptions, array('name' => '', 'type' => '', 'id' => '')),
1395: $optTitle
1396: );
1397: }
1398: $hidden = null;
1399:
1400: if ($hiddenField) {
1401: if (!isset($value) || $value === '') {
1402: $hidden = $this->hidden($fieldName, array(
1403: 'id' => $attributes['id'] . '_', 'value' => '', 'name' => $attributes['name']
1404: ));
1405: }
1406: }
1407: $out = $hidden . implode($separator, $out);
1408:
1409: if ($legend) {
1410: $out = $this->Html->useTag('fieldset', '', $this->Html->useTag('legend', $legend) . $between . $out);
1411: }
1412: return $out;
1413: }
1414:
1415: 1416: 1417: 1418: 1419: 1420: 1421: 1422: 1423: 1424: 1425: 1426: 1427: 1428: 1429: 1430: 1431: 1432: 1433: 1434: 1435:
1436: public function __call($method, $params) {
1437: $options = array();
1438: if (empty($params)) {
1439: throw new CakeException(__d('cake_dev', 'Missing field name for FormHelper::%s', $method));
1440: }
1441: if (isset($params[1])) {
1442: $options = $params[1];
1443: }
1444: if (!isset($options['type'])) {
1445: $options['type'] = $method;
1446: }
1447: $options = $this->_initInputField($params[0], $options);
1448: return $this->Html->useTag('input', $options['name'], array_diff_key($options, array('name' => '')));
1449: }
1450:
1451: 1452: 1453: 1454: 1455: 1456: 1457: 1458: 1459: 1460: 1461: 1462:
1463: public function textarea($fieldName, $options = array()) {
1464: $options = $this->_initInputField($fieldName, $options);
1465: $value = null;
1466:
1467: if (array_key_exists('value', $options)) {
1468: $value = $options['value'];
1469: if (!array_key_exists('escape', $options) || $options['escape'] !== false) {
1470: $value = h($value);
1471: }
1472: unset($options['value']);
1473: }
1474: return $this->Html->useTag('textarea', $options['name'], array_diff_key($options, array('type' => '', 'name' => '')), $value);
1475: }
1476:
1477: 1478: 1479: 1480: 1481: 1482: 1483: 1484:
1485: public function hidden($fieldName, $options = array()) {
1486: $secure = true;
1487:
1488: if (isset($options['secure'])) {
1489: $secure = $options['secure'];
1490: unset($options['secure']);
1491: }
1492: $options = $this->_initInputField($fieldName, array_merge(
1493: $options, array('secure' => self::SECURE_SKIP)
1494: ));
1495:
1496: if ($secure && $secure !== self::SECURE_SKIP) {
1497: $this->_secure(true, null, '' . $options['value']);
1498: }
1499:
1500: return $this->Html->useTag('hidden', $options['name'], array_diff_key($options, array('name' => '')));
1501: }
1502:
1503: 1504: 1505: 1506: 1507: 1508: 1509: 1510:
1511: public function file($fieldName, $options = array()) {
1512: $options += array('secure' => true);
1513: $secure = $options['secure'];
1514: $options['secure'] = self::SECURE_SKIP;
1515:
1516: $options = $this->_initInputField($fieldName, $options);
1517: $field = $this->entity();
1518:
1519: foreach (array('name', 'type', 'tmp_name', 'error', 'size') as $suffix) {
1520: $this->_secure($secure, array_merge($field, array($suffix)));
1521: }
1522:
1523: return $this->Html->useTag('file', $options['name'], array_diff_key($options, array('name' => '')));
1524: }
1525:
1526: 1527: 1528: 1529: 1530: 1531: 1532: 1533: 1534: 1535: 1536: 1537: 1538:
1539: public function button($title, $options = array()) {
1540: $options += array('type' => 'submit', 'escape' => false, 'secure' => false);
1541: if ($options['escape']) {
1542: $title = h($title);
1543: }
1544: if (isset($options['name'])) {
1545: $name = str_replace(array('[', ']'), array('.', ''), $options['name']);
1546: $this->_secure($options['secure'], $name);
1547: }
1548: return $this->Html->useTag('button', $options, $title);
1549: }
1550:
1551: 1552: 1553: 1554: 1555: 1556: 1557: 1558: 1559: 1560: 1561: 1562: 1563: 1564: 1565: 1566: 1567:
1568: public function postButton($title, $url, $options = array()) {
1569: $out = $this->create(false, array('id' => false, 'url' => $url));
1570: if (isset($options['data']) && is_array($options['data'])) {
1571: foreach ($options['data'] as $key => $value) {
1572: $out .= $this->hidden($key, array('value' => $value, 'id' => false));
1573: }
1574: unset($options['data']);
1575: }
1576: $out .= $this->button($title, $options);
1577: $out .= $this->end();
1578: return $out;
1579: }
1580:
1581: 1582: 1583: 1584: 1585: 1586: 1587: 1588: 1589: 1590: 1591: 1592: 1593: 1594: 1595: 1596: 1597: 1598: 1599: 1600: 1601:
1602: public function postLink($title, $url = null, $options = array(), $confirmMessage = false) {
1603: if (!empty($options['confirm'])) {
1604: $confirmMessage = $options['confirm'];
1605: unset($options['confirm']);
1606: }
1607:
1608: $formName = uniqid('post_');
1609: $formUrl = $this->url($url);
1610: $out = $this->Html->useTag('form', $formUrl, array('name' => $formName, 'id' => $formName, 'style' => 'display:none;', 'method' => 'post'));
1611: $out .= $this->Html->useTag('hidden', '_method', ' value="POST"');
1612: $out .= $this->_csrfField();
1613:
1614: $fields = array();
1615: if (isset($options['data']) && is_array($options['data'])) {
1616: foreach ($options['data'] as $key => $value) {
1617: $fields[$key] = $value;
1618: $out .= $this->hidden($key, array('value' => $value, 'id' => false));
1619: }
1620: unset($options['data']);
1621: }
1622: $out .= $this->secure($fields);
1623: $out .= $this->Html->useTag('formend');
1624:
1625: $url = '#';
1626: $onClick = 'document.' . $formName . '.submit();';
1627: if ($confirmMessage) {
1628: $confirmMessage = str_replace(array("'", '"'), array("\'", '\"'), $confirmMessage);
1629: $options['onclick'] = "if (confirm('{$confirmMessage}')) { {$onClick} }";
1630: } else {
1631: $options['onclick'] = $onClick;
1632: }
1633: $options['onclick'] .= ' event.returnValue = false; return false;';
1634:
1635: $out .= $this->Html->link($title, $url, $options);
1636: return $out;
1637: }
1638:
1639: 1640: 1641: 1642: 1643: 1644: 1645: 1646: 1647: 1648: 1649: 1650: 1651: 1652: 1653: 1654: 1655: 1656: 1657: 1658: 1659: 1660: 1661: 1662: 1663: 1664: 1665: 1666:
1667: public function submit($caption = null, $options = array()) {
1668: if (!is_string($caption) && empty($caption)) {
1669: $caption = __d('cake', 'Submit');
1670: }
1671: $out = null;
1672: $div = true;
1673:
1674: if (isset($options['div'])) {
1675: $div = $options['div'];
1676: unset($options['div']);
1677: }
1678: $options += array('type' => 'submit', 'before' => null, 'after' => null, 'secure' => false);
1679: $divOptions = array('tag' => 'div');
1680:
1681: if ($div === true) {
1682: $divOptions['class'] = 'submit';
1683: } elseif ($div === false) {
1684: unset($divOptions);
1685: } elseif (is_string($div)) {
1686: $divOptions['class'] = $div;
1687: } elseif (is_array($div)) {
1688: $divOptions = array_merge(array('class' => 'submit', 'tag' => 'div'), $div);
1689: }
1690:
1691: if (isset($options['name'])) {
1692: $name = str_replace(array('[', ']'), array('.', ''), $options['name']);
1693: $this->_secure($options['secure'], $name);
1694: }
1695: unset($options['secure']);
1696:
1697: $before = $options['before'];
1698: $after = $options['after'];
1699: unset($options['before'], $options['after']);
1700:
1701: $isUrl = strpos($caption, '://') !== false;
1702: $isImage = preg_match('/\.(jpg|jpe|jpeg|gif|png|ico)$/', $caption);
1703:
1704: if ($isUrl || $isImage) {
1705: $unlockFields = array('x', 'y');
1706: if (isset($options['name'])) {
1707: $unlockFields = array(
1708: $options['name'] . '_x', $options['name'] . '_y'
1709: );
1710: }
1711: foreach ($unlockFields as $ignore) {
1712: $this->unlockField($ignore);
1713: }
1714: }
1715:
1716: if ($isUrl) {
1717: unset($options['type']);
1718: $tag = $this->Html->useTag('submitimage', $caption, $options);
1719: } elseif ($isImage) {
1720: unset($options['type']);
1721: if ($caption{0} !== '/') {
1722: $url = $this->webroot(IMAGES_URL . $caption);
1723: } else {
1724: $url = $this->webroot(trim($caption, '/'));
1725: }
1726: $url = $this->assetTimestamp($url);
1727: $tag = $this->Html->useTag('submitimage', $url, $options);
1728: } else {
1729: $options['value'] = $caption;
1730: $tag = $this->Html->useTag('submit', $options);
1731: }
1732: $out = $before . $tag . $after;
1733:
1734: if (isset($divOptions)) {
1735: $tag = $divOptions['tag'];
1736: unset($divOptions['tag']);
1737: $out = $this->Html->tag($tag, $out, $divOptions);
1738: }
1739: return $out;
1740: }
1741:
1742: 1743: 1744: 1745: 1746: 1747: 1748: 1749: 1750: 1751: 1752: 1753: 1754: 1755: 1756: 1757: 1758: 1759: 1760: 1761: 1762: 1763: 1764: 1765: 1766: 1767: 1768: 1769: 1770: 1771: 1772: 1773: 1774: 1775: 1776: 1777: 1778: 1779: 1780: 1781: 1782: 1783: 1784: 1785: 1786: 1787: 1788: 1789: 1790: 1791: 1792: 1793: 1794: 1795: 1796: 1797: 1798:
1799: public function select($fieldName, $options = array(), $attributes = array()) {
1800: $select = array();
1801: $style = null;
1802: $tag = null;
1803: $attributes += array(
1804: 'class' => null,
1805: 'escape' => true,
1806: 'secure' => true,
1807: 'empty' => '',
1808: 'showParents' => false,
1809: 'hiddenField' => true
1810: );
1811:
1812: $escapeOptions = $this->_extractOption('escape', $attributes);
1813: $secure = $this->_extractOption('secure', $attributes);
1814: $showEmpty = $this->_extractOption('empty', $attributes);
1815: $showParents = $this->_extractOption('showParents', $attributes);
1816: $hiddenField = $this->_extractOption('hiddenField', $attributes);
1817: unset($attributes['escape'], $attributes['secure'], $attributes['empty'], $attributes['showParents'], $attributes['hiddenField']);
1818: $id = $this->_extractOption('id', $attributes);
1819:
1820: $attributes = $this->_initInputField($fieldName, array_merge(
1821: (array)$attributes, array('secure' => self::SECURE_SKIP)
1822: ));
1823:
1824: if (is_string($options) && isset($this->_options[$options])) {
1825: $options = $this->_generateOptions($options);
1826: } elseif (!is_array($options)) {
1827: $options = array();
1828: }
1829: if (isset($attributes['type'])) {
1830: unset($attributes['type']);
1831: }
1832:
1833: if (!empty($attributes['multiple'])) {
1834: $style = ($attributes['multiple'] === 'checkbox') ? 'checkbox' : null;
1835: $template = ($style) ? 'checkboxmultiplestart' : 'selectmultiplestart';
1836: $tag = $template;
1837: if ($hiddenField) {
1838: $hiddenAttributes = array(
1839: 'value' => '',
1840: 'id' => $attributes['id'] . ($style ? '' : '_'),
1841: 'secure' => false,
1842: 'name' => $attributes['name']
1843: );
1844: $select[] = $this->hidden(null, $hiddenAttributes);
1845: }
1846: } else {
1847: $tag = 'selectstart';
1848: }
1849:
1850: if (!empty($tag) || isset($template)) {
1851: if ((!isset($secure) || $secure == true) && empty($attributes['disabled'])) {
1852: $this->_secure(true);
1853: }
1854: $select[] = $this->Html->useTag($tag, $attributes['name'], array_diff_key($attributes, array('name' => '', 'value' => '')));
1855: }
1856: $emptyMulti = (
1857: $showEmpty !== null && $showEmpty !== false && !(
1858: empty($showEmpty) && (isset($attributes) &&
1859: array_key_exists('multiple', $attributes))
1860: )
1861: );
1862:
1863: if ($emptyMulti) {
1864: $showEmpty = ($showEmpty === true) ? '' : $showEmpty;
1865: $options = array_reverse($options, true);
1866: $options[''] = $showEmpty;
1867: $options = array_reverse($options, true);
1868: }
1869:
1870: if (!$id) {
1871: $attributes['id'] = Inflector::camelize($attributes['id']);
1872: }
1873:
1874: $select = array_merge($select, $this->_selectOptions(
1875: array_reverse($options, true),
1876: array(),
1877: $showParents,
1878: array(
1879: 'escape' => $escapeOptions,
1880: 'style' => $style,
1881: 'name' => $attributes['name'],
1882: 'value' => $attributes['value'],
1883: 'class' => $attributes['class'],
1884: 'id' => $attributes['id']
1885: )
1886: ));
1887:
1888: $template = ($style == 'checkbox') ? 'checkboxmultipleend' : 'selectend';
1889: $select[] = $this->Html->useTag($template);
1890: return implode("\n", $select);
1891: }
1892:
1893: 1894: 1895: 1896: 1897: 1898: 1899: 1900: 1901: 1902: 1903: 1904: 1905: 1906:
1907: public function day($fieldName = null, $attributes = array()) {
1908: $attributes += array('empty' => true, 'value' => null);
1909: $attributes = $this->_dateTimeSelected('day', $fieldName, $attributes);
1910:
1911: if (strlen($attributes['value']) > 2) {
1912: $attributes['value'] = date('d', strtotime($attributes['value']));
1913: } elseif ($attributes['value'] === false) {
1914: $attributes['value'] = null;
1915: }
1916: return $this->select($fieldName . ".day", $this->_generateOptions('day'), $attributes);
1917: }
1918:
1919: 1920: 1921: 1922: 1923: 1924: 1925: 1926: 1927: 1928: 1929: 1930: 1931: 1932: 1933: 1934: 1935: 1936:
1937: public function year($fieldName, $minYear = null, $maxYear = null, $attributes = array()) {
1938: $attributes += array('empty' => true, 'value' => null);
1939: if ((empty($attributes['value']) || $attributes['value'] === true) && $value = $this->value($fieldName)) {
1940: if (is_array($value)) {
1941: extract($value);
1942: $attributes['value'] = $year;
1943: } else {
1944: if (empty($value)) {
1945: if (!$attributes['empty'] && !$maxYear) {
1946: $attributes['value'] = 'now';
1947:
1948: } elseif (!$attributes['empty'] && $maxYear && !$attributes['value']) {
1949: $attributes['value'] = $maxYear;
1950: }
1951: } else {
1952: $attributes['value'] = $value;
1953: }
1954: }
1955: }
1956:
1957: if (strlen($attributes['value']) > 4 || $attributes['value'] === 'now') {
1958: $attributes['value'] = date('Y', strtotime($attributes['value']));
1959: } elseif ($attributes['value'] === false) {
1960: $attributes['value'] = null;
1961: }
1962: $yearOptions = array('min' => $minYear, 'max' => $maxYear, 'order' => 'desc');
1963: if (isset($attributes['orderYear'])) {
1964: $yearOptions['order'] = $attributes['orderYear'];
1965: unset($attributes['orderYear']);
1966: }
1967: return $this->select(
1968: $fieldName . '.year', $this->_generateOptions('year', $yearOptions),
1969: $attributes
1970: );
1971: }
1972:
1973: 1974: 1975: 1976: 1977: 1978: 1979: 1980: 1981: 1982: 1983: 1984: 1985: 1986: 1987: 1988:
1989: public function month($fieldName, $attributes = array()) {
1990: $attributes += array('empty' => true, 'value' => null);
1991: $attributes = $this->_dateTimeSelected('month', $fieldName, $attributes);
1992:
1993: if (strlen($attributes['value']) > 2) {
1994: $attributes['value'] = date('m', strtotime($attributes['value']));
1995: } elseif ($attributes['value'] === false) {
1996: $attributes['value'] = null;
1997: }
1998: $defaults = array('monthNames' => true);
1999: $attributes = array_merge($defaults, (array)$attributes);
2000: $monthNames = $attributes['monthNames'];
2001: unset($attributes['monthNames']);
2002:
2003: return $this->select(
2004: $fieldName . ".month",
2005: $this->_generateOptions('month', array('monthNames' => $monthNames)),
2006: $attributes
2007: );
2008: }
2009:
2010: 2011: 2012: 2013: 2014: 2015: 2016: 2017: 2018: 2019: 2020: 2021: 2022: 2023: 2024:
2025: public function hour($fieldName, $format24Hours = false, $attributes = array()) {
2026: $attributes += array('empty' => true, 'value' => null);
2027: $attributes = $this->_dateTimeSelected('hour', $fieldName, $attributes);
2028:
2029: if (strlen($attributes['value']) > 2) {
2030: if ($format24Hours) {
2031: $attributes['value'] = date('H', strtotime($attributes['value']));
2032: } else {
2033: $attributes['value'] = date('g', strtotime($attributes['value']));
2034: }
2035: } elseif ($attributes['value'] === false) {
2036: $attributes['value'] = null;
2037: }
2038: return $this->select(
2039: $fieldName . ".hour",
2040: $this->_generateOptions($format24Hours ? 'hour24' : 'hour'),
2041: $attributes
2042: );
2043: }
2044:
2045: 2046: 2047: 2048: 2049: 2050: 2051: 2052: 2053: 2054: 2055: 2056: 2057: 2058:
2059: public function minute($fieldName, $attributes = array()) {
2060: $attributes += array('empty' => true, 'value' => null);
2061: $attributes = $this->_dateTimeSelected('min', $fieldName, $attributes);
2062:
2063: if (strlen($attributes['value']) > 2) {
2064: $attributes['value'] = date('i', strtotime($attributes['value']));
2065: } elseif ($attributes['value'] === false) {
2066: $attributes['value'] = null;
2067: }
2068: $minuteOptions = array();
2069:
2070: if (isset($attributes['interval'])) {
2071: $minuteOptions['interval'] = $attributes['interval'];
2072: unset($attributes['interval']);
2073: }
2074: return $this->select(
2075: $fieldName . ".min", $this->_generateOptions('minute', $minuteOptions),
2076: $attributes
2077: );
2078: }
2079:
2080: 2081: 2082: 2083: 2084: 2085: 2086: 2087:
2088: protected function _dateTimeSelected($select, $fieldName, $attributes) {
2089: if ((empty($attributes['value']) || $attributes['value'] === true) && $value = $this->value($fieldName)) {
2090: if (is_array($value) && isset($value[$select])) {
2091: $attributes['value'] = $value[$select];
2092: } else {
2093: if (empty($value)) {
2094: if (!$attributes['empty']) {
2095: $attributes['value'] = 'now';
2096: }
2097: } else {
2098: $attributes['value'] = $value;
2099: }
2100: }
2101: }
2102: return $attributes;
2103: }
2104:
2105: 2106: 2107: 2108: 2109: 2110: 2111: 2112: 2113: 2114: 2115: 2116: 2117: 2118:
2119: public function meridian($fieldName, $attributes = array()) {
2120: $attributes += array('empty' => true, 'value' => null);
2121: if ((empty($attributes['value']) || $attributes['value'] === true) && $value = $this->value($fieldName)) {
2122: if (is_array($value)) {
2123: extract($value);
2124: $attributes['value'] = $meridian;
2125: } else {
2126: if (empty($value)) {
2127: if (!$attributes['empty']) {
2128: $attributes['value'] = date('a');
2129: }
2130: } else {
2131: $attributes['value'] = date('a', strtotime($value));
2132: }
2133: }
2134: }
2135:
2136: if ($attributes['value'] === false) {
2137: $attributes['value'] = null;
2138: }
2139: return $this->select(
2140: $fieldName . ".meridian", $this->_generateOptions('meridian'),
2141: $attributes
2142: );
2143: }
2144:
2145: 2146: 2147: 2148: 2149: 2150: 2151: 2152: 2153: 2154: 2155: 2156: 2157: 2158: 2159: 2160: 2161: 2162: 2163: 2164: 2165: 2166: 2167:
2168: public function dateTime($fieldName, $dateFormat = 'DMY', $timeFormat = '12', $attributes = array()) {
2169: $attributes += array('empty' => true, 'value' => null);
2170: $year = $month = $day = $hour = $min = $meridian = null;
2171:
2172: if (empty($attributes['value'])) {
2173: $attributes = $this->value($attributes, $fieldName);
2174: }
2175:
2176: if ($attributes['value'] === null && $attributes['empty'] != true) {
2177: $attributes['value'] = time();
2178: }
2179:
2180: if (!empty($attributes['value'])) {
2181: if (is_array($attributes['value'])) {
2182: extract($attributes['value']);
2183: } else {
2184: if (is_numeric($attributes['value'])) {
2185: $attributes['value'] = strftime('%Y-%m-%d %H:%M:%S', $attributes['value']);
2186: }
2187: $meridian = 'am';
2188: $pos = strpos($attributes['value'], '-');
2189: if ($pos !== false) {
2190: $date = explode('-', $attributes['value']);
2191: $days = explode(' ', $date[2]);
2192: $day = $days[0];
2193: $month = $date[1];
2194: $year = $date[0];
2195: } else {
2196: $days[1] = $attributes['value'];
2197: }
2198:
2199: if (!empty($timeFormat)) {
2200: $time = explode(':', $days[1]);
2201:
2202: if (($time[0] > 12) && $timeFormat == '12') {
2203: $time[0] = $time[0] - 12;
2204: $meridian = 'pm';
2205: } elseif ($time[0] == '12' && $timeFormat == '12') {
2206: $meridian = 'pm';
2207: } elseif ($time[0] == '00' && $timeFormat == '12') {
2208: $time[0] = 12;
2209: } elseif ($time[0] >= 12) {
2210: $meridian = 'pm';
2211: }
2212: if ($time[0] == 0 && $timeFormat == '12') {
2213: $time[0] = 12;
2214: }
2215: $hour = $min = null;
2216: if (isset($time[1])) {
2217: $hour = $time[0];
2218: $min = $time[1];
2219: }
2220: }
2221: }
2222: }
2223:
2224: $elements = array('Day', 'Month', 'Year', 'Hour', 'Minute', 'Meridian');
2225: $defaults = array(
2226: 'minYear' => null, 'maxYear' => null, 'separator' => '-',
2227: 'interval' => 1, 'monthNames' => true
2228: );
2229: $attributes = array_merge($defaults, (array)$attributes);
2230: if (isset($attributes['minuteInterval'])) {
2231: $attributes['interval'] = $attributes['minuteInterval'];
2232: unset($attributes['minuteInterval']);
2233: }
2234: $minYear = $attributes['minYear'];
2235: $maxYear = $attributes['maxYear'];
2236: $separator = $attributes['separator'];
2237: $interval = $attributes['interval'];
2238: $monthNames = $attributes['monthNames'];
2239: $attributes = array_diff_key($attributes, $defaults);
2240:
2241: if (isset($attributes['id'])) {
2242: if (is_string($attributes['id'])) {
2243:
2244: foreach ($elements as $element) {
2245: $selectAttrName = 'select' . $element . 'Attr';
2246: ${$selectAttrName} = $attributes;
2247: ${$selectAttrName}['id'] = $attributes['id'] . $element;
2248: }
2249: } elseif (is_array($attributes['id'])) {
2250:
2251: $attributes['id'] += array(
2252: 'month' => '', 'year' => '', 'day' => '',
2253: 'hour' => '', 'minute' => '', 'meridian' => ''
2254: );
2255: foreach ($elements as $element) {
2256: $selectAttrName = 'select' . $element . 'Attr';
2257: ${$selectAttrName} = $attributes;
2258: ${$selectAttrName}['id'] = $attributes['id'][strtolower($element)];
2259: }
2260: }
2261: } else {
2262:
2263: foreach ($elements as $element) {
2264: $selectAttrName = 'select' . $element . 'Attr';
2265: ${$selectAttrName} = $attributes;
2266: }
2267: }
2268:
2269: $selects = array();
2270: foreach (preg_split('//', $dateFormat, -1, PREG_SPLIT_NO_EMPTY) as $char) {
2271: switch ($char) {
2272: case 'Y':
2273: $selectYearAttr['value'] = $year;
2274: $selects[] = $this->year(
2275: $fieldName, $minYear, $maxYear, $selectYearAttr
2276: );
2277: break;
2278: case 'M':
2279: $selectMonthAttr['value'] = $month;
2280: $selectMonthAttr['monthNames'] = $monthNames;
2281: $selects[] = $this->month($fieldName, $selectMonthAttr);
2282: break;
2283: case 'D':
2284: $selectDayAttr['value'] = $day;
2285: $selects[] = $this->day($fieldName, $selectDayAttr);
2286: break;
2287: }
2288: }
2289: $opt = implode($separator, $selects);
2290:
2291: if (!empty($interval) && $interval > 1 && !empty($min)) {
2292: $min = round($min * (1 / $interval)) * $interval;
2293: }
2294: $selectMinuteAttr['interval'] = $interval;
2295: switch ($timeFormat) {
2296: case '24':
2297: $selectHourAttr['value'] = $hour;
2298: $selectMinuteAttr['value'] = $min;
2299: $opt .= $this->hour($fieldName, true, $selectHourAttr) . ':' .
2300: $this->minute($fieldName, $selectMinuteAttr);
2301: break;
2302: case '12':
2303: $selectHourAttr['value'] = $hour;
2304: $selectMinuteAttr['value'] = $min;
2305: $selectMeridianAttr['value'] = $meridian;
2306: $opt .= $this->hour($fieldName, false, $selectHourAttr) . ':' .
2307: $this->minute($fieldName, $selectMinuteAttr) . ' ' .
2308: $this->meridian($fieldName, $selectMeridianAttr);
2309: break;
2310: default:
2311: $opt .= '';
2312: break;
2313: }
2314: return $opt;
2315: }
2316:
2317: 2318: 2319: 2320: 2321: 2322: 2323: 2324:
2325: protected function _name($options = array(), $field = null, $key = 'name') {
2326: if ($this->requestType == 'get') {
2327: if ($options === null) {
2328: $options = array();
2329: } elseif (is_string($options)) {
2330: $field = $options;
2331: $options = 0;
2332: }
2333:
2334: if (!empty($field)) {
2335: $this->setEntity($field);
2336: }
2337:
2338: if (is_array($options) && isset($options[$key])) {
2339: return $options;
2340: }
2341:
2342: $entity = $this->entity();
2343: $model = $this->model();
2344: $name = $model === $entity[0] && isset($entity[1]) ? $entity[1] : $entity[0];
2345: $last = $entity[count($entity) - 1];
2346: if (in_array($last, $this->_fieldSuffixes)) {
2347: $name .= '[' . $last . ']';
2348: }
2349:
2350: if (is_array($options)) {
2351: $options[$key] = $name;
2352: return $options;
2353: } else {
2354: return $name;
2355: }
2356: }
2357: return parent::_name($options, $field, $key);
2358: }
2359:
2360: 2361: 2362: 2363: 2364: 2365: 2366: 2367: 2368:
2369: protected function _selectOptions($elements = array(), $parents = array(), $showParents = null, $attributes = array()) {
2370: $select = array();
2371: $attributes = array_merge(
2372: array('escape' => true, 'style' => null, 'value' => null, 'class' => null),
2373: $attributes
2374: );
2375: $selectedIsEmpty = ($attributes['value'] === '' || $attributes['value'] === null);
2376: $selectedIsArray = is_array($attributes['value']);
2377:
2378: foreach ($elements as $name => $title) {
2379: $htmlOptions = array();
2380: if (is_array($title) && (!isset($title['name']) || !isset($title['value']))) {
2381: if (!empty($name)) {
2382: if ($attributes['style'] === 'checkbox') {
2383: $select[] = $this->Html->useTag('fieldsetend');
2384: } else {
2385: $select[] = $this->Html->useTag('optiongroupend');
2386: }
2387: $parents[] = $name;
2388: }
2389: $select = array_merge($select, $this->_selectOptions(
2390: $title, $parents, $showParents, $attributes
2391: ));
2392:
2393: if (!empty($name)) {
2394: $name = $attributes['escape'] ? h($name) : $name;
2395: if ($attributes['style'] === 'checkbox') {
2396: $select[] = $this->Html->useTag('fieldsetstart', $name);
2397: } else {
2398: $select[] = $this->Html->useTag('optiongroup', $name, '');
2399: }
2400: }
2401: $name = null;
2402: } elseif (is_array($title)) {
2403: $htmlOptions = $title;
2404: $name = $title['value'];
2405: $title = $title['name'];
2406: unset($htmlOptions['name'], $htmlOptions['value']);
2407: }
2408:
2409: if ($name !== null) {
2410: if (
2411: (!$selectedIsArray && !$selectedIsEmpty && (string)$attributes['value'] == (string)$name) ||
2412: ($selectedIsArray && in_array($name, $attributes['value']))
2413: ) {
2414: if ($attributes['style'] === 'checkbox') {
2415: $htmlOptions['checked'] = true;
2416: } else {
2417: $htmlOptions['selected'] = 'selected';
2418: }
2419: }
2420:
2421: if ($showParents || (!in_array($title, $parents))) {
2422: $title = ($attributes['escape']) ? h($title) : $title;
2423:
2424: if ($attributes['style'] === 'checkbox') {
2425: $htmlOptions['value'] = $name;
2426:
2427: $tagName = $attributes['id'] . Inflector::camelize(Inflector::slug($name));
2428: $htmlOptions['id'] = $tagName;
2429: $label = array('for' => $tagName);
2430:
2431: if (isset($htmlOptions['checked']) && $htmlOptions['checked'] === true) {
2432: $label['class'] = 'selected';
2433: }
2434:
2435: $name = $attributes['name'];
2436:
2437: if (empty($attributes['class'])) {
2438: $attributes['class'] = 'checkbox';
2439: } elseif ($attributes['class'] === 'form-error') {
2440: $attributes['class'] = 'checkbox ' . $attributes['class'];
2441: }
2442: $label = $this->label(null, $title, $label);
2443: $item = $this->Html->useTag('checkboxmultiple', $name, $htmlOptions);
2444: $select[] = $this->Html->div($attributes['class'], $item . $label);
2445: } else {
2446: $select[] = $this->Html->useTag('selectoption', $name, $htmlOptions, $title);
2447: }
2448: }
2449: }
2450: }
2451:
2452: return array_reverse($select, true);
2453: }
2454:
2455: 2456: 2457: 2458: 2459: 2460: 2461:
2462: protected function _generateOptions($name, $options = array()) {
2463: if (!empty($this->options[$name])) {
2464: return $this->options[$name];
2465: }
2466: $data = array();
2467:
2468: switch ($name) {
2469: case 'minute':
2470: if (isset($options['interval'])) {
2471: $interval = $options['interval'];
2472: } else {
2473: $interval = 1;
2474: }
2475: $i = 0;
2476: while ($i < 60) {
2477: $data[sprintf('%02d', $i)] = sprintf('%02d', $i);
2478: $i += $interval;
2479: }
2480: break;
2481: case 'hour':
2482: for ($i = 1; $i <= 12; $i++) {
2483: $data[sprintf('%02d', $i)] = $i;
2484: }
2485: break;
2486: case 'hour24':
2487: for ($i = 0; $i <= 23; $i++) {
2488: $data[sprintf('%02d', $i)] = $i;
2489: }
2490: break;
2491: case 'meridian':
2492: $data = array('am' => 'am', 'pm' => 'pm');
2493: break;
2494: case 'day':
2495: $min = 1;
2496: $max = 31;
2497:
2498: if (isset($options['min'])) {
2499: $min = $options['min'];
2500: }
2501: if (isset($options['max'])) {
2502: $max = $options['max'];
2503: }
2504:
2505: for ($i = $min; $i <= $max; $i++) {
2506: $data[sprintf('%02d', $i)] = $i;
2507: }
2508: break;
2509: case 'month':
2510: if ($options['monthNames'] === true) {
2511: $data['01'] = __d('cake', 'January');
2512: $data['02'] = __d('cake', 'February');
2513: $data['03'] = __d('cake', 'March');
2514: $data['04'] = __d('cake', 'April');
2515: $data['05'] = __d('cake', 'May');
2516: $data['06'] = __d('cake', 'June');
2517: $data['07'] = __d('cake', 'July');
2518: $data['08'] = __d('cake', 'August');
2519: $data['09'] = __d('cake', 'September');
2520: $data['10'] = __d('cake', 'October');
2521: $data['11'] = __d('cake', 'November');
2522: $data['12'] = __d('cake', 'December');
2523: } elseif (is_array($options['monthNames'])) {
2524: $data = $options['monthNames'];
2525: } else {
2526: for ($m = 1; $m <= 12; $m++) {
2527: $data[sprintf("%02s", $m)] = strftime("%m", mktime(1, 1, 1, $m, 1, 1999));
2528: }
2529: }
2530: break;
2531: case 'year':
2532: $current = intval(date('Y'));
2533:
2534: $min = !isset($options['min']) ? $current - 20 : (int)$options['min'];
2535: $max = !isset($options['max']) ? $current + 20 : (int)$options['max'];
2536:
2537: if ($min > $max) {
2538: list($min, $max) = array($max, $min);
2539: }
2540: for ($i = $min; $i <= $max; $i++) {
2541: $data[$i] = $i;
2542: }
2543: if ($options['order'] != 'asc') {
2544: $data = array_reverse($data, true);
2545: }
2546: break;
2547: }
2548: $this->_options[$name] = $data;
2549: return $this->_options[$name];
2550: }
2551:
2552: 2553: 2554: 2555: 2556: 2557: 2558: 2559: 2560: 2561: 2562: 2563: 2564:
2565: protected function _initInputField($field, $options = array()) {
2566: if (isset($options['secure'])) {
2567: $secure = $options['secure'];
2568: unset($options['secure']);
2569: } else {
2570: $secure = (isset($this->request['_Token']) && !empty($this->request['_Token']));
2571: }
2572:
2573: $result = parent::_initInputField($field, $options);
2574: if ($this->tagIsInvalid() !== false) {
2575: $result = $this->addClass($result, 'form-error');
2576: }
2577: if (!empty($result['disabled']) || $secure === self::SECURE_SKIP) {
2578: return $result;
2579: }
2580:
2581: $fieldName = null;
2582: if (!empty($options['name'])) {
2583: preg_match_all('/\[(.*?)\]/', $options['name'], $matches);
2584: if (isset($matches[1])) {
2585: $fieldName = $matches[1];
2586: }
2587: }
2588:
2589: $this->_secure($secure, $fieldName);
2590: return $result;
2591: }
2592:
2593: }
2594: