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