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