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:
290: public function setValue($value)
291: {
292: $this->value = $value;
293: return $this;
294: }
295:
296:
297:
298: 299: 300: 301:
302: public function getValue()
303: {
304: return $this->value;
305: }
306:
307:
308:
309: 310: 311: 312:
313: public function isFilled()
314: {
315: return (string) $this->getValue() !== '';
316: }
317:
318:
319:
320: 321: 322: 323: 324:
325: public function setDefaultValue($value)
326: {
327: $form = $this->getForm(FALSE);
328: if (!$form || !$form->isAnchored() || !$form->isSubmitted()) {
329: $this->setValue($value);
330: }
331: return $this;
332: }
333:
334:
335:
336: 337: 338: 339:
340: public function loadHttpData()
341: {
342: $path = explode('[', strtr(str_replace(array('[]', ']'), '', $this->getHtmlName()), '.', '_'));
343: $this->setValue(Nette\Utils\Arrays::get($this->getForm()->getHttpData(), $path, NULL));
344: }
345:
346:
347:
348: 349: 350: 351: 352:
353: public function setDisabled($value = TRUE)
354: {
355: $this->disabled = (bool) $value;
356: return $this;
357: }
358:
359:
360:
361: 362: 363: 364:
365: public function isDisabled()
366: {
367: return $this->disabled;
368: }
369:
370:
371:
372:
373:
374:
375:
376: 377: 378: 379:
380: public function getControl()
381: {
382: $this->setOption('rendered', TRUE);
383:
384: $control = clone $this->control;
385: $control->name = $this->getHtmlName();
386: $control->disabled = $this->disabled;
387: $control->id = $this->getHtmlId();
388: $control->required = $this->isRequired();
389:
390: $rules = self::exportRules($this->rules);
391: $rules = substr(PHP_VERSION_ID >= 50400 ? json_encode($rules, JSON_UNESCAPED_UNICODE) : json_encode($rules), 1, -1);
392: $rules = preg_replace('#"([a-z0-9_]+)":#i', '$1:', $rules);
393: $rules = preg_replace('#(?<!\\\\)"(?!:[^a-z])([^\\\\\',]*)"#i', "'$1'", $rules);
394: $control->data('nette-rules', $rules ? $rules : NULL);
395:
396: return $control;
397: }
398:
399:
400:
401: 402: 403: 404: 405:
406: public function getLabel($caption = NULL)
407: {
408: $label = clone $this->label;
409: $label->for = $this->getHtmlId();
410: if ($caption !== NULL) {
411: $label->setText($this->translate($caption));
412:
413: } elseif ($this->caption instanceof Html) {
414: $label->add($this->caption);
415:
416: } else {
417: $label->setText($this->translate($this->caption));
418: }
419: return $label;
420: }
421:
422:
423:
424: 425: 426: 427:
428: final public function getControlPrototype()
429: {
430: return $this->control;
431: }
432:
433:
434:
435: 436: 437: 438:
439: final public function getLabelPrototype()
440: {
441: return $this->label;
442: }
443:
444:
445:
446:
447:
448:
449:
450: 451: 452: 453: 454: 455: 456:
457: public function addRule($operation, $message = NULL, $arg = NULL)
458: {
459: $this->rules->addRule($operation, $message, $arg);
460: return $this;
461: }
462:
463:
464:
465: 466: 467: 468: 469: 470:
471: public function addCondition($operation, $value = NULL)
472: {
473: return $this->rules->addCondition($operation, $value);
474: }
475:
476:
477:
478: 479: 480: 481: 482: 483: 484:
485: public function addConditionOn(IControl $control, $operation, $value = NULL)
486: {
487: return $this->rules->addConditionOn($control, $operation, $value);
488: }
489:
490:
491:
492: 493: 494:
495: final public function getRules()
496: {
497: return $this->rules;
498: }
499:
500:
501:
502: 503: 504: 505: 506:
507: final public function setRequired($message = NULL)
508: {
509: return $this->addRule(Form::FILLED, $message);
510: }
511:
512:
513:
514: 515: 516: 517:
518: final public function isRequired()
519: {
520: foreach ($this->rules as $rule) {
521: if ($rule->type === Rule::VALIDATOR && !$rule->isNegative && $rule->operation === Form::FILLED) {
522: return TRUE;
523: }
524: }
525: return FALSE;
526: }
527:
528:
529:
530: 531: 532:
533: protected static function exportRules($rules)
534: {
535: $payload = array();
536: foreach ($rules as $rule) {
537: if (!is_string($op = $rule->operation)) {
538: $op = new Nette\Callback($op);
539: if (!$op->isStatic()) {
540: continue;
541: }
542: }
543: if ($rule->type === Rule::VALIDATOR) {
544: $item = array('op' => ($rule->isNegative ? '~' : '') . $op, 'msg' => $rules->formatMessage($rule, FALSE));
545:
546: } elseif ($rule->type === Rule::CONDITION) {
547: $item = array(
548: 'op' => ($rule->isNegative ? '~' : '') . $op,
549: 'rules' => self::exportRules($rule->subRules),
550: 'control' => $rule->control->getHtmlName()
551: );
552: if ($rule->subRules->getToggles()) {
553: $item['toggle'] = $rule->subRules->getToggles();
554: }
555: }
556:
557: if (is_array($rule->arg)) {
558: foreach ($rule->arg as $key => $value) {
559: $item['arg'][$key] = $value instanceof IControl ? (object) array('control' => $value->getHtmlName()) : $value;
560: }
561: } elseif ($rule->arg !== NULL) {
562: $item['arg'] = $rule->arg instanceof IControl ? (object) array('control' => $rule->arg->getHtmlName()) : $rule->arg;
563: }
564:
565: $payload[] = $item;
566: }
567: return $payload;
568: }
569:
570:
571:
572:
573:
574:
575:
576: 577: 578: 579: 580: 581:
582: public static function validateEqual(IControl $control, $arg)
583: {
584: $value = $control->getValue();
585: foreach ((is_array($value) ? $value : array($value)) as $val) {
586: foreach ((is_array($arg) ? $arg : array($arg)) as $item) {
587: if ((string) $val === (string) ($item instanceof IControl ? $item->value : $item)) {
588: return TRUE;
589: }
590: }
591: }
592: return FALSE;
593: }
594:
595:
596:
597: 598: 599: 600: 601:
602: public static function validateFilled(IControl $control)
603: {
604: return $control->isFilled();
605: }
606:
607:
608:
609: 610: 611: 612:
613: public static function validateValid(IControl $control)
614: {
615: return $control->rules->validate(TRUE);
616: }
617:
618:
619:
620: 621: 622: 623: 624:
625: public function addError($message)
626: {
627: if (!in_array($message, $this->errors, TRUE)) {
628: $this->errors[] = $message;
629: }
630: $this->getForm()->addError($message);
631: }
632:
633:
634:
635: 636: 637: 638:
639: public function getErrors()
640: {
641: return $this->errors;
642: }
643:
644:
645:
646: 647: 648:
649: public function hasErrors()
650: {
651: return (bool) $this->errors;
652: }
653:
654:
655:
656: 657: 658:
659: public function cleanErrors()
660: {
661: $this->errors = array();
662: }
663:
664: }
665: