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