1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Forms;
9:
10: use Nette;
11:
12:
13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29:
30: class Form extends Container
31: {
32:
33: const EQUAL = ':equal',
34: IS_IN = ':equal',
35: NOT_EQUAL = ':notEqual',
36: FILLED = ':filled',
37: BLANK = ':blank',
38: REQUIRED = self::FILLED,
39: VALID = ':valid';
40:
41:
42: const PROTECTION = 'Nette\Forms\Controls\HiddenField::validateEqual';
43:
44:
45: const SUBMITTED = ':submitted';
46:
47:
48: const MIN_LENGTH = ':minLength',
49: MAX_LENGTH = ':maxLength',
50: LENGTH = ':length',
51: EMAIL = ':email',
52: URL = ':url',
53: REGEXP = ':regexp',
54: PATTERN = ':pattern',
55: INTEGER = ':integer',
56: NUMERIC = ':integer',
57: FLOAT = ':float',
58: RANGE = ':range';
59:
60:
61: const COUNT = ':length';
62:
63:
64: const MAX_FILE_SIZE = ':fileSize',
65: MIME_TYPE = ':mimeType',
66: IMAGE = ':image';
67:
68:
69: const GET = 'get',
70: POST = 'post';
71:
72:
73: const TRACKER_ID = '_form_';
74:
75:
76: const PROTECTOR_ID = '_token_';
77:
78:
79: public $onSuccess;
80:
81:
82: public $onError;
83:
84:
85: public $onSubmit;
86:
87:
88: public $onInvalidSubmit;
89:
90:
91: private $submittedBy;
92:
93:
94: private $httpData;
95:
96:
97: private $element;
98:
99:
100: private $renderer;
101:
102:
103: private $translator;
104:
105:
106: private $groups = array();
107:
108:
109: private $errors = array();
110:
111:
112: public $httpRequest;
113:
114:
115: 116: 117: 118:
119: public function __construct($name = NULL)
120: {
121: $this->element = Nette\Utils\Html::el('form');
122: $this->element->action = '';
123: $this->element->method = self::POST;
124: $this->element->id = $name === NULL ? NULL : 'frm-' . $name;
125:
126: $this->monitor(__CLASS__);
127: if ($name !== NULL) {
128: $tracker = new Controls\HiddenField($name);
129: $tracker->unmonitor(__CLASS__);
130: $this[self::TRACKER_ID] = $tracker;
131: }
132: parent::__construct(NULL, $name);
133: }
134:
135:
136: 137: 138: 139: 140: 141:
142: protected function attached($obj)
143: {
144: if ($obj instanceof self) {
145: throw new Nette\InvalidStateException('Nested forms are forbidden.');
146: }
147: }
148:
149:
150: 151: 152: 153:
154: public function getForm($need = TRUE)
155: {
156: return $this;
157: }
158:
159:
160: 161: 162: 163: 164:
165: public function setAction($url)
166: {
167: $this->element->action = $url;
168: return $this;
169: }
170:
171:
172: 173: 174: 175:
176: public function getAction()
177: {
178: return $this->element->action;
179: }
180:
181:
182: 183: 184: 185: 186:
187: public function setMethod($method)
188: {
189: if ($this->httpData !== NULL) {
190: throw new Nette\InvalidStateException(__METHOD__ . '() must be called until the form is empty.');
191: }
192: $this->element->method = strtolower($method);
193: return $this;
194: }
195:
196:
197: 198: 199: 200:
201: public function getMethod()
202: {
203: return $this->element->method;
204: }
205:
206:
207: 208: 209: 210: 211: 212:
213: public function addProtection($message = NULL, $timeout = NULL)
214: {
215: $session = $this->getSession()->getSection('Nette.Forms.Form/CSRF');
216: $key = "key$timeout";
217: if (isset($session->$key)) {
218: $token = $session->$key;
219: } else {
220: $session->$key = $token = Nette\Utils\Strings::random();
221: }
222: $session->setExpiration($timeout, $key);
223: $this[self::PROTECTOR_ID] = new Controls\HiddenField($token);
224: $this[self::PROTECTOR_ID]->addRule(self::PROTECTION, $message, $token);
225: }
226:
227:
228: 229: 230: 231: 232: 233:
234: public function addGroup($caption = NULL, $setAsCurrent = TRUE)
235: {
236: $group = new ControlGroup;
237: $group->setOption('label', $caption);
238: $group->setOption('visual', TRUE);
239:
240: if ($setAsCurrent) {
241: $this->setCurrentGroup($group);
242: }
243:
244: if (isset($this->groups[$caption])) {
245: return $this->groups[] = $group;
246: } else {
247: return $this->groups[$caption] = $group;
248: }
249: }
250:
251:
252: 253: 254: 255: 256:
257: public function removeGroup($name)
258: {
259: if (is_string($name) && isset($this->groups[$name])) {
260: $group = $this->groups[$name];
261:
262: } elseif ($name instanceof ControlGroup && in_array($name, $this->groups, TRUE)) {
263: $group = $name;
264: $name = array_search($group, $this->groups, TRUE);
265:
266: } else {
267: throw new Nette\InvalidArgumentException("Group not found in form '$this->name'");
268: }
269:
270: foreach ($group->getControls() as $control) {
271: $control->getParent()->removeComponent($control);
272: }
273:
274: unset($this->groups[$name]);
275: }
276:
277:
278: 279: 280: 281:
282: public function getGroups()
283: {
284: return $this->groups;
285: }
286:
287:
288: 289: 290: 291: 292:
293: public function getGroup($name)
294: {
295: return isset($this->groups[$name]) ? $this->groups[$name] : NULL;
296: }
297:
298:
299:
300:
301:
302: 303: 304: 305:
306: public function setTranslator(Nette\Localization\ITranslator $translator = NULL)
307: {
308: $this->translator = $translator;
309: return $this;
310: }
311:
312:
313: 314: 315: 316:
317: public function getTranslator()
318: {
319: return $this->translator;
320: }
321:
322:
323:
324:
325:
326: 327: 328: 329:
330: public function isAnchored()
331: {
332: return TRUE;
333: }
334:
335:
336: 337: 338: 339:
340: public function isSubmitted()
341: {
342: if ($this->submittedBy === NULL) {
343: $this->getHttpData();
344: }
345: return $this->submittedBy;
346: }
347:
348:
349: 350: 351: 352:
353: public function isSuccess()
354: {
355: return $this->isSubmitted() && $this->isValid();
356: }
357:
358:
359: 360: 361: 362:
363: public function setSubmittedBy(ISubmitterControl $by = NULL)
364: {
365: $this->submittedBy = $by === NULL ? FALSE : $by;
366: return $this;
367: }
368:
369:
370: 371: 372: 373:
374: public function getHttpData()
375: {
376: if ($this->httpData === NULL) {
377: if (!$this->isAnchored()) {
378: throw new Nette\InvalidStateException('Form is not anchored and therefore can not determine whether it was submitted.');
379: }
380: $data = $this->receiveHttpData();
381: $this->httpData = (array) $data;
382: $this->submittedBy = is_array($data);
383: }
384: return $this->httpData;
385: }
386:
387:
388: 389: 390: 391:
392: public function fireEvents()
393: {
394: if (!$this->isSubmitted()) {
395: return;
396:
397: } elseif ($this->submittedBy instanceof ISubmitterControl) {
398: if (!$this->submittedBy->getValidationScope() || $this->isValid()) {
399: $this->submittedBy->click();
400: $valid = TRUE;
401: } else {
402: $this->submittedBy->onInvalidClick($this->submittedBy);
403: }
404: }
405:
406: if (isset($valid) || $this->isValid()) {
407: $this->onSuccess($this);
408: } else {
409: $this->onError($this);
410: if ($this->onInvalidSubmit) {
411: trigger_error(__CLASS__ . '->onInvalidSubmit is deprecated; use onError instead.', E_USER_WARNING);
412: $this->onInvalidSubmit($this);
413: }
414: }
415:
416: if ($this->onSuccess) {
417: $this->onSubmit($this);
418: } elseif ($this->onSubmit) {
419: trigger_error(__CLASS__ . '->onSubmit changed its behavior; use onSuccess instead.', E_USER_WARNING);
420: if (isset($valid) || $this->isValid()) {
421: $this->onSubmit($this);
422: }
423: }
424: }
425:
426:
427: 428: 429: 430:
431: protected function receiveHttpData()
432: {
433: $httpRequest = $this->getHttpRequest();
434: if (strcasecmp($this->getMethod(), $httpRequest->getMethod())) {
435: return;
436: }
437:
438: if ($httpRequest->isMethod('post')) {
439: $data = Nette\Utils\Arrays::mergeTree($httpRequest->getPost(), $httpRequest->getFiles());
440: } else {
441: $data = $httpRequest->getQuery();
442: if (!$data) {
443: return;
444: }
445: }
446:
447: if ($tracker = $this->getComponent(self::TRACKER_ID, FALSE)) {
448: if (!isset($data[self::TRACKER_ID]) || $data[self::TRACKER_ID] !== $tracker->getValue()) {
449: return;
450: }
451: }
452:
453: return $data;
454: }
455:
456:
457:
458:
459:
460: 461: 462: 463:
464: public function getValues($asArray = FALSE)
465: {
466: $values = parent::getValues($asArray);
467: unset($values[self::TRACKER_ID], $values[self::PROTECTOR_ID]);
468: return $values;
469: }
470:
471:
472:
473:
474:
475: 476: 477: 478: 479:
480: public function addError($message)
481: {
482: $this->valid = FALSE;
483: if ($message !== NULL && !in_array($message, $this->errors, TRUE)) {
484: $this->errors[] = $message;
485: }
486: }
487:
488:
489: 490: 491: 492:
493: public function getErrors()
494: {
495: return $this->errors;
496: }
497:
498:
499: 500: 501:
502: public function hasErrors()
503: {
504: return (bool) $this->getErrors();
505: }
506:
507:
508: 509: 510:
511: public function cleanErrors()
512: {
513: $this->errors = array();
514: $this->valid = NULL;
515: }
516:
517:
518:
519:
520:
521: 522: 523: 524:
525: public function getElementPrototype()
526: {
527: return $this->element;
528: }
529:
530:
531: 532: 533: 534:
535: public function setRenderer(IFormRenderer $renderer)
536: {
537: $this->renderer = $renderer;
538: return $this;
539: }
540:
541:
542: 543: 544: 545:
546: public function getRenderer()
547: {
548: if ($this->renderer === NULL) {
549: $this->renderer = new Rendering\DefaultFormRenderer;
550: }
551: return $this->renderer;
552: }
553:
554:
555: 556: 557: 558:
559: public function render()
560: {
561: $args = func_get_args();
562: array_unshift($args, $this);
563: echo call_user_func_array(array($this->getRenderer(), 'render'), $args);
564: }
565:
566:
567: 568: 569: 570: 571:
572: public function __toString()
573: {
574: try {
575: return $this->getRenderer()->render($this);
576:
577: } catch (\Exception $e) {
578: if (func_get_args() && func_get_arg(0)) {
579: throw $e;
580: } else {
581: trigger_error("Exception in " . __METHOD__ . "(): {$e->getMessage()} in {$e->getFile()}:{$e->getLine()}", E_USER_ERROR);
582: }
583: }
584: }
585:
586:
587:
588:
589:
590: 591: 592:
593: protected function getHttpRequest()
594: {
595: if (!$this->httpRequest) {
596: $factory = new Nette\Http\RequestFactory;
597: $this->httpRequest = $factory->setEncoding('UTF-8')->createHttpRequest();
598: }
599: return $this->httpRequest;
600: }
601:
602:
603: 604: 605:
606: protected function getSession()
607: {
608: if (!$this->httpRequest) {
609: $this->httpRequest = Nette\Environment::getHttpRequest();
610: }
611: return Nette\Environment::getSession();
612: }
613:
614: }
615: