1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12: namespace Nette\Forms;
13:
14: use Nette,
15: Nette\Web\Html;
16:
17:
18:
19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39:
40: abstract class FormControl extends Nette\Component implements IFormControl
41: {
42:
43: public static $idMask = 'frm%s-%s';
44:
45:
46: public $caption;
47:
48:
49: protected $value;
50:
51:
52: protected $control;
53:
54:
55: protected $label;
56:
57:
58: private $errors = array();
59:
60:
61: private $disabled = FALSE;
62:
63:
64: private $htmlId;
65:
66:
67: private $htmlName;
68:
69:
70: private $rules;
71:
72:
73: private $translator = TRUE; 74:
75:
76: private $options = array();
77:
78:
79:
80: 81: 82:
83: public function __construct($caption = NULL)
84: {
85: $this->monitor('Nette\Forms\Form');
86: parent::__construct();
87: $this->control = Html::el('input');
88: $this->label = Html::el('label');
89: $this->caption = $caption;
90: $this->rules = new Rules($this);
91: }
92:
93:
94:
95: 96: 97: 98: 99:
100: protected function attached($form)
101: {
102: if (!$this->disabled && $form instanceof Form && $form->isAnchored() && $form->isSubmitted()) {
103: $this->htmlName = NULL;
104: $this->loadHttpData();
105: }
106: }
107:
108:
109:
110: 111: 112: 113: 114:
115: public function getForm($need = TRUE)
116: {
117: return $this->lookup('Nette\Forms\Form', $need);
118: }
119:
120:
121:
122: 123: 124: 125:
126: public function getHtmlName()
127: {
128: if ($this->htmlName === NULL) {
129: $s = '';
130: $name = $this->getName();
131: $obj = $this->lookup('Nette\Forms\FormContainer', TRUE);
132: while (!($obj instanceof Form)) {
133: $s = "[$name]$s";
134: $name = $obj->getName();
135: $obj = $obj->lookup('Nette\Forms\FormContainer', TRUE);
136: }
137: $name .= $s;
138: if (is_numeric($name) || in_array($name, array('attributes','children','elements','focus','length','reset','style','submit','onsubmit'))) {
139: $name .= '_';
140: }
141: $this->htmlName = $name;
142: }
143: return $this->htmlName;
144: }
145:
146:
147:
148: 149: 150: 151: 152:
153: public function setHtmlId($id)
154: {
155: $this->htmlId = $id;
156: return $this;
157: }
158:
159:
160:
161: 162: 163: 164:
165: public function getHtmlId()
166: {
167: if ($this->htmlId === FALSE) {
168: return NULL;
169:
170: } elseif ($this->htmlId === NULL) {
171: $this->htmlId = sprintf(self::$idMask, $this->getForm()->getName(), $this->getHtmlName());
172: $this->htmlId = str_replace(array('[]', '[', ']'), array('', '-', ''), $this->htmlId);
173: }
174: return $this->htmlId;
175: }
176:
177:
178:
179: 180: 181: 182: 183: 184:
185: public function setAttribute($name, $value = TRUE)
186: {
187: $this->control->$name = $value;
188: return $this;
189: }
190:
191:
192:
193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203:
204: public function setOption($key, $value)
205: {
206: if ($value === NULL) {
207: unset($this->options[$key]);
208:
209: } else {
210: $this->options[$key] = $value;
211: }
212: return $this;
213: }
214:
215:
216:
217: 218: 219: 220: 221: 222:
223: final public function getOption($key, $default = NULL)
224: {
225: return isset($this->options[$key]) ? $this->options[$key] : $default;
226: }
227:
228:
229:
230: 231: 232: 233:
234: final public function getOptions()
235: {
236: return $this->options;
237: }
238:
239:
240:
241:
242:
243:
244:
245: 246: 247: 248: 249:
250: public function setTranslator(Nette\ITranslator $translator = NULL)
251: {
252: $this->translator = $translator;
253: return $this;
254: }
255:
256:
257:
258: 259: 260: 261:
262: final public function getTranslator()
263: {
264: if ($this->translator === TRUE) {
265: return $this->getForm(FALSE) ? $this->getForm()->getTranslator() : NULL;
266: }
267: return $this->translator;
268: }
269:
270:
271:
272: 273: 274: 275: 276: 277:
278: public function translate($s, $count = NULL)
279: {
280: $translator = $this->getTranslator();
281: return $translator === NULL || $s == NULL ? $s : $translator->translate($s, $count); 282: }
283:
284:
285:
286:
287:
288:
289:
290: 291: 292: 293: 294:
295: public function setValue($value)
296: {
297: $this->value = $value;
298: return $this;
299: }
300:
301:
302:
303: 304: 305: 306:
307: public function getValue()
308: {
309: return $this->value;
310: }
311:
312:
313:
314: 315: 316: 317: 318:
319: public function setDefaultValue($value)
320: {
321: $form = $this->getForm(FALSE);
322: if (!$form || !$form->isAnchored() || !$form->isSubmitted()) {
323: $this->setValue($value);
324: }
325: return $this;
326: }
327:
328:
329:
330: 331: 332: 333:
334: public function loadHttpData()
335: {
336: $path = explode('[', strtr(str_replace(array('[]', ']'), '', $this->getHtmlName()), '.', '_'));
337: $this->setValue(Nette\ArrayTools::get($this->getForm()->getHttpData(), $path));
338: }
339:
340:
341:
342: 343: 344: 345: 346:
347: public function setDisabled($value = TRUE)
348: {
349: $this->disabled = (bool) $value;
350: return $this;
351: }
352:
353:
354:
355: 356: 357: 358:
359: public function isDisabled()
360: {
361: return $this->disabled;
362: }
363:
364:
365:
366:
367:
368:
369:
370: 371: 372: 373:
374: public function getControl()
375: {
376: $this->setOption('rendered', TRUE);
377: $control = clone $this->control;
378: $control->name = $this->getHtmlName();
379: $control->disabled = $this->disabled;
380: $control->id = $this->getHtmlId();
381: $rules = self::exportRules($this->rules);
382: $rules = substr(json_encode($rules), 1, -1);
383: $rules = preg_replace('#"([a-z0-9]+)":#i', '$1:', $rules);
384: $rules = preg_replace('#(?<!\\\\)"([^\\\\\',]*)"#i', "'$1'", $rules);
385: $control->data['nette-rules'] = $rules ? $rules : NULL;
386: return $control;
387: }
388:
389:
390:
391: 392: 393: 394: 395:
396: public function getLabel($caption = NULL)
397: {
398: $label = clone $this->label;
399: $label->for = $this->getHtmlId();
400: if ($caption !== NULL) {
401: $label->setText($this->translate($caption));
402:
403: } elseif ($this->caption instanceof Html) {
404: $label->add($this->caption);
405:
406: } else {
407: $label->setText($this->translate($this->caption));
408: }
409: return $label;
410: }
411:
412:
413:
414: 415: 416: 417:
418: final public function getControlPrototype()
419: {
420: return $this->control;
421: }
422:
423:
424:
425: 426: 427: 428:
429: final public function getLabelPrototype()
430: {
431: return $this->label;
432: }
433:
434:
435:
436: 437: 438: 439: 440: 441:
442: public function setRendered($value = TRUE)
443: {
444: $this->setOption('rendered', $value);
445: return $this;
446: }
447:
448:
449:
450: 451: 452: 453: 454:
455: public function isRendered()
456: {
457: return !empty($this->options['rendered']);
458: }
459:
460:
461:
462:
463:
464:
465:
466: 467: 468: 469: 470: 471: 472:
473: public function addRule($operation, $message = NULL, $arg = NULL)
474: {
475: $this->rules->addRule($operation, $message, $arg);
476: return $this;
477: }
478:
479:
480:
481: 482: 483: 484: 485: 486:
487: public function addCondition($operation, $value = NULL)
488: {
489: return $this->rules->addCondition($operation, $value);
490: }
491:
492:
493:
494: 495: 496: 497: 498: 499: 500:
501: public function addConditionOn(IFormControl $control, $operation, $value = NULL)
502: {
503: return $this->rules->addConditionOn($control, $operation, $value);
504: }
505:
506:
507:
508: 509: 510:
511: final public function getRules()
512: {
513: return $this->rules;
514: }
515:
516:
517:
518: 519: 520: 521: 522: 523:
524: final public function setRequired($message = NULL)
525: {
526: $this->rules->addRule(Form::FILLED, $message);
527: return $this;
528: }
529:
530:
531:
532: 533: 534: 535: 536:
537: final public function isRequired()
538: {
539: return !empty($this->options['required']);
540: }
541:
542:
543:
544: 545: 546: 547: 548:
549: public function notifyRule(Rule $rule)
550: {
551: if (is_string($rule->operation) && strcasecmp($rule->operation, ':filled') === 0) {
552: $this->setOption('required', TRUE);
553: }
554: }
555:
556:
557:
558: 559: 560:
561: private static function exportRules($rules)
562: {
563: $payload = array();
564: foreach ($rules as $rule) {
565: if (!is_string($rule->operation)) {
566: continue;
567:
568: } elseif ($rule->type === Rule::VALIDATOR) {
569: $item = array('op' => ($rule->isNegative ? '~' : '') . $rule->operation, 'msg' => $rules->formatMessage($rule, FALSE));
570:
571: } elseif ($rule->type === Rule::CONDITION) {
572: $item = array('op' => ($rule->isNegative ? '~' : '') . $rule->operation, 'rules' => self::exportRules($rule->subRules), 'control' => $rule->control->getHtmlName());
573: if ($rule->subRules->getToggles()) {
574: $item['toggle'] = $rule->subRules->getToggles();
575: }
576: }
577:
578: if (is_array($rule->arg)) {
579: foreach ($rule->arg as $key => $value) {
580: $item['arg'][$key] = $value instanceof IFormControl ? (object) array('control' => $value->getHtmlName()) : $value;
581: }
582: } elseif ($rule->arg !== NULL) {
583: $item['arg'] = $rule->arg instanceof IFormControl ? (object) array('control' => $rule->arg->getHtmlName()) : $rule->arg;
584: }
585:
586: $payload[] = $item;
587: }
588: return $payload;
589: }
590:
591:
592:
593:
594:
595:
596:
597: 598: 599: 600: 601: 602:
603: public static function validateEqual(IFormControl $control, $arg)
604: {
605: $value = $control->getValue();
606: foreach ((is_array($value) ? $value : array($value)) as $val) {
607: foreach ((is_array($arg) ? $arg : array($arg)) as $item) {
608: if ((string) $val === (string) ($item instanceof IFormControl ? $item->value : $item)) {
609: return TRUE;
610: }
611: }
612: }
613: return FALSE;
614: }
615:
616:
617:
618: 619: 620: 621: 622:
623: public static function validateFilled(IFormControl $control)
624: {
625: return (string) $control->getValue() !== ''; 626: }
627:
628:
629:
630: 631: 632: 633: 634:
635: public static function validateValid(IFormControl $control)
636: {
637: return $control->rules->validate(TRUE);
638: }
639:
640:
641:
642: 643: 644: 645: 646:
647: public function addError($message)
648: {
649: if (!in_array($message, $this->errors, TRUE)) {
650: $this->errors[] = $message;
651: }
652: $this->getForm()->addError($message);
653: }
654:
655:
656:
657: 658: 659: 660:
661: public function getErrors()
662: {
663: return $this->errors;
664: }
665:
666:
667:
668: 669: 670:
671: public function hasErrors()
672: {
673: return (bool) $this->errors;
674: }
675:
676:
677:
678: 679: 680:
681: public function cleanErrors()
682: {
683: $this->errors = array();
684: }
685:
686: }
687: