Namespaces

  • 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
  • PHP

Classes

  • DefaultFormRenderer
  • Overview
  • Namespace
  • Class
  • Tree
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (http://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\Rendering;
 13: 
 14: use Nette,
 15:     Nette\Utils\Html;
 16: 
 17: 
 18: 
 19: /**
 20:  * Converts a Form into the HTML output.
 21:  *
 22:  * @author     David Grudl
 23:  */
 24: class DefaultFormRenderer extends Nette\Object implements Nette\Forms\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 Nette\Forms\Form */
120:     protected $form;
121: 
122:     /** @var int */
123:     protected $counter;
124: 
125: 
126: 
127:     /**
128:      * Provides complete form rendering.
129:      * @param  Nette\Forms\Form
130:      * @param  string 'begin', 'errors', 'body', 'end' or empty to render all
131:      * @return string
132:      */
133:     public function render(Nette\Forms\Form $form, $mode = NULL)
134:     {
135:         if ($this->form !== $form) {
136:             $this->form = $form;
137:             $this->init();
138:         }
139: 
140:         $s = '';
141:         if (!$mode || $mode === 'begin') {
142:             $s .= $this->renderBegin();
143:         }
144:         if ((!$mode && $this->getValue('form errors')) || $mode === 'errors') {
145:             $s .= $this->renderErrors();
146:         }
147:         if (!$mode || $mode === 'body') {
148:             $s .= $this->renderBody();
149:         }
150:         if (!$mode || $mode === 'end') {
151:             $s .= $this->renderEnd();
152:         }
153:         return $s;
154:     }
155: 
156: 
157: 
158:     /** @deprecated */
159:     public function setClientScript()
160:     {
161:         trigger_error(__METHOD__ . '() is deprecated; use unobstructive JavaScript instead.', E_USER_WARNING);
162:         return $this;
163:     }
164: 
165: 
166: 
167:     /**
168:      * Initializes form.
169:      * @return void
170:      */
171:     protected function init()
172:     {
173:         // TODO: only for back compatiblity - remove?
174:         $wrapper = & $this->wrappers['control'];
175:         foreach ($this->form->getControls() as $control) {
176:             if ($control->isRequired() && isset($wrapper['.required'])) {
177:                 $control->getLabelPrototype()->class($wrapper['.required'], TRUE);
178:             }
179: 
180:             $el = $control->getControlPrototype();
181:             if ($el->getName() === 'input' && isset($wrapper['.' . $el->type])) {
182:                 $el->class($wrapper['.' . $el->type], TRUE);
183:             }
184:         }
185:     }
186: 
187: 
188: 
189:     /**
190:      * Renders form begin.
191:      * @return string
192:      */
193:     public function renderBegin()
194:     {
195:         $this->counter = 0;
196: 
197:         foreach ($this->form->getControls() as $control) {
198:             $control->setOption('rendered', FALSE);
199:         }
200: 
201:         if (strcasecmp($this->form->getMethod(), 'get') === 0) {
202:             $el = clone $this->form->getElementPrototype();
203:             $url = explode('?', (string) $el->action, 2);
204:             $el->action = $url[0];
205:             $s = '';
206:             if (isset($url[1])) {
207:                 foreach (preg_split('#[;&]#', $url[1]) as $param) {
208:                     $parts = explode('=', $param, 2);
209:                     $name = urldecode($parts[0]);
210:                     if (!isset($this->form[$name])) {
211:                         $s .= Html::el('input', array('type' => 'hidden', 'name' => $name, 'value' => urldecode($parts[1])));
212:                     }
213:                 }
214:                 $s = "\n\t" . $this->getWrapper('hidden container')->setHtml($s);
215:             }
216:             return $el->startTag() . $s;
217: 
218: 
219:         } else {
220:             return $this->form->getElementPrototype()->startTag();
221:         }
222:     }
223: 
224: 
225: 
226:     /**
227:      * Renders form end.
228:      * @return string
229:      */
230:     public function renderEnd()
231:     {
232:         $s = '';
233:         foreach ($this->form->getControls() as $control) {
234:             if ($control instanceof Nette\Forms\Controls\HiddenField && !$control->getOption('rendered')) {
235:                 $s .= (string) $control->getControl();
236:             }
237:         }
238:         if (iterator_count($this->form->getComponents(TRUE, 'Nette\Forms\Controls\TextInput')) < 2) {
239:             $s .= '<!--[if IE]><input type=IEbug disabled style="display:none"><![endif]-->';
240:         }
241:         if ($s) {
242:             $s = $this->getWrapper('hidden container')->setHtml($s) . "\n";
243:         }
244: 
245:         return $s . $this->form->getElementPrototype()->endTag() . "\n";
246:     }
247: 
248: 
249: 
250:     /**
251:      * Renders validation errors (per form or per control).
252:      * @param  Nette\Forms\IControl
253:      * @return string
254:      */
255:     public function renderErrors(Nette\Forms\IControl $control = NULL)
256:     {
257:         $errors = $control === NULL ? $this->form->getErrors() : $control->getErrors();
258:         if (count($errors)) {
259:             $ul = $this->getWrapper('error container');
260:             $li = $this->getWrapper('error item');
261: 
262:             foreach ($errors as $error) {
263:                 $item = clone $li;
264:                 if ($error instanceof Html) {
265:                     $item->add($error);
266:                 } else {
267:                     $item->setText($error);
268:                 }
269:                 $ul->add($item);
270:             }
271:             return "\n" . $ul->render(0);
272:         }
273:     }
274: 
275: 
276: 
277:     /**
278:      * Renders form body.
279:      * @return string
280:      */
281:     public function renderBody()
282:     {
283:         $s = $remains = '';
284: 
285:         $defaultContainer = $this->getWrapper('group container');
286:         $translator = $this->form->getTranslator();
287: 
288:         foreach ($this->form->getGroups() as $group) {
289:             if (!$group->getControls() || !$group->getOption('visual')) {
290:                 continue;
291:             }
292: 
293:             $container = $group->getOption('container', $defaultContainer);
294:             $container = $container instanceof Html ? clone $container : Html::el($container);
295: 
296:             $s .= "\n" . $container->startTag();
297: 
298:             $text = $group->getOption('label');
299:             if ($text instanceof Html) {
300:                 $s .= $text;
301: 
302:             } elseif (is_string($text)) {
303:                 if ($translator !== NULL) {
304:                     $text = $translator->translate($text);
305:                 }
306:                 $s .= "\n" . $this->getWrapper('group label')->setText($text) . "\n";
307:             }
308: 
309:             $text = $group->getOption('description');
310:             if ($text instanceof Html) {
311:                 $s .= $text;
312: 
313:             } elseif (is_string($text)) {
314:                 if ($translator !== NULL) {
315:                     $text = $translator->translate($text);
316:                 }
317:                 $s .= $this->getWrapper('group description')->setText($text) . "\n";
318:             }
319: 
320:             $s .= $this->renderControls($group);
321: 
322:             $remains = $container->endTag() . "\n" . $remains;
323:             if (!$group->getOption('embedNext')) {
324:                 $s .= $remains;
325:                 $remains = '';
326:             }
327:         }
328: 
329:         $s .= $remains . $this->renderControls($this->form);
330: 
331:         $container = $this->getWrapper('form container');
332:         $container->setHtml($s);
333:         return $container->render(0);
334:     }
335: 
336: 
337: 
338:     /**
339:      * Renders group of controls.
340:      * @param  Nette\Forms\Container|FormGroup
341:      * @return string
342:      */
343:     public function renderControls($parent)
344:     {
345:         if (!($parent instanceof Nette\Forms\Container || $parent instanceof Nette\Forms\ControlGroup)) {
346:             throw new Nette\InvalidArgumentException("Argument must be FormContainer or FormGroup instance.");
347:         }
348: 
349:         $container = $this->getWrapper('controls container');
350: 
351:         $buttons = NULL;
352:         foreach ($parent->getControls() as $control) {
353:             if ($control->getOption('rendered') || $control instanceof Nette\Forms\Controls\HiddenField || $control->getForm(FALSE) !== $this->form) {
354:                 // skip
355: 
356:             } elseif ($control instanceof Nette\Forms\Controls\Button) {
357:                 $buttons[] = $control;
358: 
359:             } else {
360:                 if ($buttons) {
361:                     $container->add($this->renderPairMulti($buttons));
362:                     $buttons = NULL;
363:                 }
364:                 $container->add($this->renderPair($control));
365:             }
366:         }
367: 
368:         if ($buttons) {
369:             $container->add($this->renderPairMulti($buttons));
370:         }
371: 
372:         $s = '';
373:         if (count($container)) {
374:             $s .= "\n" . $container . "\n";
375:         }
376: 
377:         return $s;
378:     }
379: 
380: 
381: 
382:     /**
383:      * Renders single visual row.
384:      * @param  Nette\Forms\IControl
385:      * @return string
386:      */
387:     public function renderPair(Nette\Forms\IControl $control)
388:     {
389:         $pair = $this->getWrapper('pair container');
390:         $pair->add($this->renderLabel($control));
391:         $pair->add($this->renderControl($control));
392:         $pair->class($this->getValue($control->isRequired() ? 'pair .required' : 'pair .optional'), TRUE);
393:         $pair->class($control->getOption('class'), TRUE);
394:         if (++$this->counter % 2) {
395:             $pair->class($this->getValue('pair .odd'), TRUE);
396:         }
397:         $pair->id = $control->getOption('id');
398:         return $pair->render(0);
399:     }
400: 
401: 
402: 
403:     /**
404:      * Renders single visual row of multiple controls.
405:      * @param  IFormControl[]
406:      * @return string
407:      */
408:     public function renderPairMulti(array $controls)
409:     {
410:         $s = array();
411:         foreach ($controls as $control) {
412:             if (!$control instanceof Nette\Forms\IControl) {
413:                 throw new Nette\InvalidArgumentException("Argument must be array of IFormControl instances.");
414:             }
415:             $s[] = (string) $control->getControl();
416:         }
417:         $pair = $this->getWrapper('pair container');
418:         $pair->add($this->renderLabel($control));
419:         $pair->add($this->getWrapper('control container')->setHtml(implode(" ", $s)));
420:         return $pair->render(0);
421:     }
422: 
423: 
424: 
425:     /**
426:      * Renders 'label' part of visual row of controls.
427:      * @param  Nette\Forms\IControl
428:      * @return string
429:      */
430:     public function renderLabel(Nette\Forms\IControl $control)
431:     {
432:         $head = $this->getWrapper('label container');
433: 
434:         if ($control instanceof Nette\Forms\Controls\Checkbox || $control instanceof Nette\Forms\Controls\Button) {
435:             return $head->setHtml(($head->getName() === 'td' || $head->getName() === 'th') ? '&nbsp;' : '');
436: 
437:         } else {
438:             $label = $control->getLabel();
439:             $suffix = $this->getValue('label suffix') . ($control->isRequired() ? $this->getValue('label requiredsuffix') : '');
440:             if ($label instanceof Html) {
441:                 $label->setHtml($label->getHtml() . $suffix);
442:                 $suffix = '';
443:             }
444:             return $head->setHtml((string) $label . $suffix);
445:         }
446:     }
447: 
448: 
449: 
450:     /**
451:      * Renders 'control' part of visual row of controls.
452:      * @param  Nette\Forms\IControl
453:      * @return string
454:      */
455:     public function renderControl(Nette\Forms\IControl $control)
456:     {
457:         $body = $this->getWrapper('control container');
458:         if ($this->counter % 2) {
459:             $body->class($this->getValue('control .odd'), TRUE);
460:         }
461: 
462:         $description = $control->getOption('description');
463:         if ($description instanceof Html) {
464:             $description = ' ' . $control->getOption('description');
465: 
466:         } elseif (is_string($description)) {
467:             $description = ' ' . $this->getWrapper('control description')->setText($control->translate($description));
468: 
469:         } else {
470:             $description = '';
471:         }
472: 
473:         if ($control->isRequired()) {
474:             $description = $this->getValue('control requiredsuffix') . $description;
475:         }
476: 
477:         if ($this->getValue('control errors')) {
478:             $description .= $this->renderErrors($control);
479:         }
480: 
481:         if ($control instanceof Nette\Forms\Controls\Checkbox || $control instanceof Nette\Forms\Controls\Button) {
482:             return $body->setHtml((string) $control->getControl() . (string) $control->getLabel() . $description);
483: 
484:         } else {
485:             return $body->setHtml((string) $control->getControl() . $description);
486:         }
487:     }
488: 
489: 
490: 
491:     /**
492:      * @param  string
493:      * @return Nette\Utils\Html
494:      */
495:     protected function getWrapper($name)
496:     {
497:         $data = $this->getValue($name);
498:         return $data instanceof Html ? clone $data : Html::el($data);
499:     }
500: 
501: 
502: 
503:     /**
504:      * @param  string
505:      * @return string
506:      */
507:     protected function getValue($name)
508:     {
509:         $name = explode(' ', $name);
510:         $data = & $this->wrappers[$name[0]][$name[1]];
511:         return $data;
512:     }
513: 
514: }
515: 
Nette Framework 2.0.5 API API documentation generated by ApiGen 2.7.0