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