Packages

  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Config
      • Adapters
      • Extensions
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Diagnostics
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
      • PhpGenerator
  • NetteModule
  • none

Classes

Interfaces

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