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