Namespaces

  • Nette
    • Application
    • Caching
    • Collections
    • Config
    • Forms
    • IO
    • Loaders
    • Mail
    • Reflection
    • Security
    • Templates
    • Web
  • None
  • PHP

Classes

  • Button
  • Checkbox
  • ConventionalRenderer
  • FileUpload
  • Form
  • FormContainer
  • FormControl
  • FormGroup
  • HiddenField
  • ImageButton
  • InstantClientScript
  • MultiSelectBox
  • RadioList
  • Rule
  • Rules
  • SelectBox
  • SubmitButton
  • TextArea
  • TextBase
  • TextInput

Interfaces

  • IFormControl
  • IFormRenderer
  • INamingContainer
  • ISubmitterControl
  • Overview
  • Namespace
  • Class
  • Tree
  • Other releases
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://nette.org)
  5:  *
  6:  * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  7:  *
  8:  * For the full copyright and license information, please view
  9:  * the file license.txt that was distributed with this source code.
 10:  */
 11: 
 12: namespace Nette\Forms;
 13: 
 14: use Nette,
 15:     Nette\Web\Html;
 16: 
 17: 
 18: 
 19: /**
 20:  * Converts a Form into the HTML output.
 21:  *
 22:  * @author     David Grudl
 23:  */
 24: class ConventionalRenderer extends Nette\Object implements IFormRenderer
 25: {
 26:     /**
 27:      *  /--- form.container
 28:      *
 29:      *    /--- if (form.errors) error.container
 30:      *      .... error.item [.class]
 31:      *    \---
 32:      *
 33:      *    /--- hidden.container
 34:      *      .... HIDDEN CONTROLS
 35:      *    \---
 36:      *
 37:      *    /--- group.container
 38:      *      .... group.label
 39:      *      .... group.description
 40:      *
 41:      *      /--- controls.container
 42:      *
 43:      *        /--- pair.container [.required .optional .odd]
 44:      *
 45:      *          /--- label.container
 46:      *            .... LABEL
 47:      *            .... label.suffix
 48:      *            .... label.requiredsuffix
 49:      *          \---
 50:      *
 51:      *          /--- control.container [.odd]
 52:      *            .... CONTROL [.required .text .password .file .submit .button]
 53:      *            .... control.requiredsuffix
 54:      *            .... control.description
 55:      *            .... if (control.errors) error.container
 56:      *          \---
 57:      *        \---
 58:      *      \---
 59:      *    \---
 60:      *  \--
 61:      *
 62:      * @var array of HTML tags */
 63:     public $wrappers = array(
 64:         'form' => array(
 65:             'container' => NULL,
 66:             'errors' => TRUE,
 67:         ),
 68: 
 69:         'error' => array(
 70:             'container' => 'ul class=error',
 71:             'item' => 'li',
 72:         ),
 73: 
 74:         'group' => array(
 75:             'container' => 'fieldset',
 76:             'label' => 'legend',
 77:             'description' => 'p',
 78:         ),
 79: 
 80:         'controls' => array(
 81:             'container' => 'table',
 82:         ),
 83: 
 84:         'pair' => array(
 85:             'container' => 'tr',
 86:             '.required' => 'required',
 87:             '.optional' => NULL,
 88:             '.odd' => NULL,
 89:         ),
 90: 
 91:         'control' => array(
 92:             'container' => 'td',
 93:             '.odd' => NULL,
 94: 
 95:             'errors' => FALSE,
 96:             'description' => 'small',
 97:             'requiredsuffix' => '',
 98: 
 99:             '.required' => 'required',
100:             '.text' => 'text',
101:             '.password' => 'text',
102:             '.file' => 'text',
103:             '.submit' => 'button',
104:             '.image' => 'imagebutton',
105:             '.button' => 'button',
106:         ),
107: 
108:         'label' => array(
109:             'container' => 'th',
110:             'suffix' => NULL,
111:             'requiredsuffix' => '',
112:         ),
113: 
114:         'hidden' => array(
115:             'container' => 'div',
116:         ),
117:     );
118: 
119:     /** @var Form */
120:     protected $form;
121: 
122:     /** @var object */
123:     protected $clientScript = TRUE; // means autodetect
124: 
125:     /** @var int */
126:     protected $counter;
127: 
128: 
129: 
130:     /**
131:      * Provides complete form rendering.
132:      * @param  Form
133:      * @param  string 'begin', 'errors', 'body', 'end' or empty to render all
134:      * @return string
135:      */
136:     public function render(Form $form, $mode = NULL)
137:     {
138:         if ($this->form !== $form) {
139:             $this->form = $form;
140:             $this->init();
141:         }
142: 
143:         $s = '';
144:         if (!$mode || $mode === 'begin') {
145:             $s .= $this->renderBegin();
146:         }
147:         if ((!$mode && $this->getValue('form errors')) || $mode === 'errors') {
148:             $s .= $this->renderErrors();
149:         }
150:         if (!$mode || $mode === 'body') {
151:             $s .= $this->renderBody();
152:         }
153:         if (!$mode || $mode === 'end') {
154:             $s .= $this->renderEnd();
155:         }
156:         return $s;
157:     }
158: 
159: 
160: 
161:     /**
162:      * Sets JavaScript handler.
163:      * @param  object
164:      * @return ConventionalRenderer  provides a fluent interface
165:      */
166:     public function setClientScript($clientScript = NULL)
167:     {
168:         $this->clientScript = $clientScript;
169:         return $this;
170:     }
171: 
172: 
173: 
174:     /**
175:      * Returns JavaScript handler.
176:      * @return mixed
177:      */
178:     public function getClientScript()
179:     {
180:         if ($this->clientScript === TRUE) {
181:             $this->clientScript = new InstantClientScript($this->form);
182:         }
183:         return $this->clientScript;
184:     }
185: 
186: 
187: 
188:     /**
189:      * Initializes form.
190:      * @return void
191:      */
192:     protected function init()
193:     {
194:         $clientScript = $this->getClientScript();
195:         if ($clientScript !== NULL) {
196:             $clientScript->enable();
197:         }
198: 
199:         // TODO: only for back compatiblity - remove?
200:         $wrapper = & $this->wrappers['control'];
201:         foreach ($this->form->getControls() as $control) {
202:             if ($control->getOption('required') && isset($wrapper['.required'])) {
203:                 $control->getLabelPrototype()->class($wrapper['.required'], TRUE);
204:             }
205: 
206:             $el = $control->getControlPrototype();
207:             if ($el->getName() === 'input' && isset($wrapper['.' . $el->type])) {
208:                 $el->class($wrapper['.' . $el->type], TRUE);
209:             }
210:         }
211:     }
212: 
213: 
214: 
215:     /**
216:      * Renders form begin.
217:      * @return string
218:      */
219:     public function renderBegin()
220:     {
221:         $this->counter = 0;
222: 
223:         foreach ($this->form->getControls() as $control) {
224:             $control->setOption('rendered', FALSE);
225:         }
226: 
227:         if (strcasecmp($this->form->getMethod(), 'get') === 0) {
228:             $el = clone $this->form->getElementPrototype();
229:             $uri = explode('?', (string) $el->action, 2);
230:             $el->action = $uri[0];
231:             $s = '';
232:             if (isset($uri[1])) {
233:                 foreach (preg_split('#[;&]#', $uri[1]) as $param) {
234:                     $parts = explode('=', $param, 2);
235:                     $name = urldecode($parts[0]);
236:                     if (!isset($this->form[$name])) {
237:                         $s .= Html::el('input', array('type' => 'hidden', 'name' => $name, 'value' => urldecode($parts[1])));
238:                     }
239:                 }
240:                 $s = "\n\t" . $this->getWrapper('hidden container')->setHtml($s);
241:             }
242:             return $el->startTag() . $s;
243: 
244: 
245:         } else {
246:             return $this->form->getElementPrototype()->startTag();
247:         }
248:     }
249: 
250: 
251: 
252:     /**
253:      * Renders form end.
254:      * @return string
255:      */
256:     public function renderEnd()
257:     {
258:         $s = '';
259:         foreach ($this->form->getControls() as $control) {
260:             if ($control instanceof HiddenField && !$control->getOption('rendered')) {
261:                 $s .= (string) $control->getControl();
262:             }
263:         }
264:         if ($s) {
265:             $s = $this->getWrapper('hidden container')->setHtml($s) . "\n";
266:         }
267: 
268:         $s .= $this->form->getElementPrototype()->endTag() . "\n";
269: 
270:         $clientScript = $this->getClientScript();
271:         if ($clientScript !== NULL) {
272:             $s .= $clientScript->renderClientScript() . "\n";
273:         }
274: 
275:         return $s;
276:     }
277: 
278: 
279: 
280:     /**
281:      * Renders validation errors (per form or per control).
282:      * @param  IFormControl
283:      * @return string
284:      */
285:     public function renderErrors(IFormControl $control = NULL)
286:     {
287:         $errors = $control === NULL ? $this->form->getErrors() : $control->getErrors();
288:         if (count($errors)) {
289:             $ul = $this->getWrapper('error container');
290:             $li = $this->getWrapper('error item');
291: 
292:             foreach ($errors as $error) {
293:                 $item = clone $li;
294:                 if ($error instanceof Html) {
295:                     $item->add($error);
296:                 } else {
297:                     $item->setText($error);
298:                 }
299:                 $ul->add($item);
300:             }
301:             return "\n" . $ul->render(0);
302:         }
303:     }
304: 
305: 
306: 
307:     /**
308:      * Renders form body.
309:      * @return string
310:      */
311:     public function renderBody()
312:     {
313:         $s = $remains = '';
314: 
315:         $defaultContainer = $this->getWrapper('group container');
316:         $translator = $this->form->getTranslator();
317: 
318:         foreach ($this->form->getGroups() as $group) {
319:             if (!$group->getControls() || !$group->getOption('visual')) continue;
320: 
321:             $container = $group->getOption('container', $defaultContainer);
322:             $container = $container instanceof Html ? clone $container : Html::el($container);
323: 
324:             $s .= "\n" . $container->startTag();
325: 
326:             $text = $group->getOption('label');
327:             if ($text instanceof Html) {
328:                 $s .= $text;
329: 
330:             } elseif (is_string($text)) {
331:                 if ($translator !== NULL) {
332:                     $text = $translator->translate($text);
333:                 }
334:                 $s .= "\n" . $this->getWrapper('group label')->setText($text) . "\n";
335:             }
336: 
337:             $text = $group->getOption('description');
338:             if ($text instanceof Html) {
339:                 $s .= $text;
340: 
341:             } elseif (is_string($text)) {
342:                 if ($translator !== NULL) {
343:                     $text = $translator->translate($text);
344:                 }
345:                 $s .= $this->getWrapper('group description')->setText($text) . "\n";
346:             }
347: 
348:             $s .= $this->renderControls($group);
349: 
350:             $remains = $container->endTag() . "\n" . $remains;
351:             if (!$group->getOption('embedNext')) {
352:                 $s .= $remains;
353:                 $remains = '';
354:             }
355:         }
356: 
357:         $s .= $remains . $this->renderControls($this->form);
358: 
359:         $container = $this->getWrapper('form container');
360:         $container->setHtml($s);
361:         return $container->render(0);
362:     }
363: 
364: 
365: 
366:     /**
367:      * Renders group of controls.
368:      * @param  FormContainer|FormGroup
369:      * @return string
370:      */
371:     public function renderControls($parent)
372:     {
373:         if (!($parent instanceof FormContainer || $parent instanceof FormGroup)) {
374:             throw new \InvalidArgumentException("Argument must be FormContainer or FormGroup instance.");
375:         }
376: 
377:         $container = $this->getWrapper('controls container');
378: 
379:         $buttons = NULL;
380:         foreach ($parent->getControls() as $control) {
381:             if ($control->getOption('rendered') || $control instanceof HiddenField || $control->getForm(FALSE) !== $this->form) {
382:                 // skip
383: 
384:             } elseif ($control instanceof Button) {
385:                 $buttons[] = $control;
386: 
387:             } else {
388:                 if ($buttons) {
389:                     $container->add($this->renderPairMulti($buttons));
390:                     $buttons = NULL;
391:                 }
392:                 $container->add($this->renderPair($control));
393:             }
394:         }
395: 
396:         if ($buttons) {
397:             $container->add($this->renderPairMulti($buttons));
398:         }
399: 
400:         $s = '';
401:         if (count($container)) {
402:             $s .= "\n" . $container . "\n";
403:         }
404: 
405:         return $s;
406:     }
407: 
408: 
409: 
410:     /**
411:      * Renders single visual row.
412:      * @param  IFormControl
413:      * @return string
414:      */
415:     public function renderPair(IFormControl $control)
416:     {
417:         $pair = $this->getWrapper('pair container');
418:         $pair->add($this->renderLabel($control));
419:         $pair->add($this->renderControl($control));
420:         $pair->class($this->getValue($control->getOption('required') ? 'pair .required' : 'pair .optional'), TRUE);
421:         $pair->class($control->getOption('class'), TRUE);
422:         if (++$this->counter % 2) $pair->class($this->getValue('pair .odd'), TRUE);
423:         $pair->id = $control->getOption('id');
424:         return $pair->render(0);
425:     }
426: 
427: 
428: 
429:     /**
430:      * Renders single visual row of multiple controls.
431:      * @param  array of IFormControl
432:      * @return string
433:      */
434:     public function renderPairMulti(array $controls)
435:     {
436:         $s = array();
437:         foreach ($controls as $control) {
438:             if (!($control instanceof IFormControl)) {
439:                 throw new \InvalidArgumentException("Argument must be array of IFormControl instances.");
440:             }
441:             $s[] = (string) $control->getControl();
442:         }
443:         $pair = $this->getWrapper('pair container');
444:         $pair->add($this->renderLabel($control));
445:         $pair->add($this->getWrapper('control container')->setHtml(implode(" ", $s)));
446:         return $pair->render(0);
447:     }
448: 
449: 
450: 
451:     /**
452:      * Renders 'label' part of visual row of controls.
453:      * @param  IFormControl
454:      * @return string
455:      */
456:     public function renderLabel(IFormControl $control)
457:     {
458:         $head = $this->getWrapper('label container');
459: 
460:         if ($control instanceof Checkbox || $control instanceof Button) {
461:             return $head->setHtml(($head->getName() === 'td' || $head->getName() === 'th') ? '&nbsp;' : '');
462: 
463:         } else {
464:             $label = $control->getLabel();
465:             $suffix = $this->getValue('label suffix') . ($control->getOption('required') ? $this->getValue('label requiredsuffix') : '');
466:             if ($label instanceof Html) {
467:                 $label->setHtml($label->getHtml() . $suffix);
468:                 $suffix = '';
469:             }
470:             return $head->setHtml((string) $label . $suffix);
471:         }
472:     }
473: 
474: 
475: 
476:     /**
477:      * Renders 'control' part of visual row of controls.
478:      * @param  IFormControl
479:      * @return string
480:      */
481:     public function renderControl(IFormControl $control)
482:     {
483:         $body = $this->getWrapper('control container');
484:         if ($this->counter % 2) $body->class($this->getValue('control .odd'), TRUE);
485: 
486:         $description = $control->getOption('description');
487:         if ($description instanceof Html) {
488:             $description = ' ' . $control->getOption('description');
489: 
490:         } elseif (is_string($description)) {
491:             $description = ' ' . $this->getWrapper('control description')->setText($control->translate($description));
492: 
493:         } else {
494:             $description = '';
495:         }
496: 
497:         if ($control->getOption('required')) {
498:             $description = $this->getValue('control requiredsuffix') . $description;
499:         }
500: 
501:         if ($this->getValue('control errors')) {
502:             $description .= $this->renderErrors($control);
503:         }
504: 
505:         if ($control instanceof Checkbox || $control instanceof Button) {
506:             return $body->setHtml((string) $control->getControl() . (string) $control->getLabel() . $description);
507: 
508:         } else {
509:             return $body->setHtml((string) $control->getControl() . $description);
510:         }
511:     }
512: 
513: 
514: 
515:     /**
516:      * @param  string
517:      * @return Nette\Web\Html
518:      */
519:     protected function getWrapper($name)
520:     {
521:         $data = $this->getValue($name);
522:         return $data instanceof Html ? clone $data : Html::el($data);
523:     }
524: 
525: 
526: 
527:     /**
528:      * @param  string
529:      * @return string
530:      */
531:     protected function getValue($name)
532:     {
533:         $name = explode(' ', $name);
534:         $data = & $this->wrappers[$name[0]][$name[1]];
535:         return $data;
536:     }
537: 
538: }
539: 
Nette Framework 0.9.7 API documentation generated by ApiGen 2.3.0