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