Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationDI
      • ApplicationLatte
      • ApplicationTracy
      • CacheDI
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsDI
      • FormsLatte
      • Framework
      • HttpDI
      • HttpTracy
      • MailDI
      • ReflectionDI
      • SecurityDI
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Conventions
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Templating
    • Utils
  • NetteModule
  • none
  • Tracy
    • Bridges
      • Nette

Classes

  • DefaultFormRenderer
  • Overview
  • Namespace
  • 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:  */
  7: 
  8: namespace Nette\Forms\Rendering;
  9: 
 10: use Nette,
 11:     Nette\Utils\Html;
 12: 
 13: 
 14: /**
 15:  * Converts a Form into the HTML output.
 16:  *
 17:  * @author     David Grudl
 18:  */
 19: class DefaultFormRenderer extends Nette\Object implements Nette\Forms\IFormRenderer
 20: {
 21:     /**
 22:      *  /--- form.container
 23:      *
 24:      *    /--- error.container
 25:      *      .... error.item [.class]
 26:      *    \---
 27:      *
 28:      *    /--- hidden.container
 29:      *      .... HIDDEN CONTROLS
 30:      *    \---
 31:      *
 32:      *    /--- group.container
 33:      *      .... group.label
 34:      *      .... group.description
 35:      *
 36:      *      /--- controls.container
 37:      *
 38:      *        /--- pair.container [.required .optional .odd]
 39:      *
 40:      *          /--- label.container
 41:      *            .... LABEL
 42:      *            .... label.suffix
 43:      *            .... label.requiredsuffix
 44:      *          \---
 45:      *
 46:      *          /--- control.container [.odd]
 47:      *            .... CONTROL [.required .text .password .file .submit .button]
 48:      *            .... control.requiredsuffix
 49:      *            .... control.description
 50:      *            .... control.errorcontainer + control.erroritem
 51:      *          \---
 52:      *        \---
 53:      *      \---
 54:      *    \---
 55:      *  \--
 56:      *
 57:      * @var array of HTML tags */
 58:     public $wrappers = array(
 59:         'form' => array(
 60:             'container' => NULL,
 61:         ),
 62: 
 63:         'error' => array(
 64:             'container' => 'ul class=error',
 65:             'item' => 'li',
 66:         ),
 67: 
 68:         'group' => array(
 69:             'container' => 'fieldset',
 70:             'label' => 'legend',
 71:             'description' => 'p',
 72:         ),
 73: 
 74:         'controls' => array(
 75:             'container' => 'table',
 76:         ),
 77: 
 78:         'pair' => array(
 79:             'container' => 'tr',
 80:             '.required' => 'required',
 81:             '.optional' => NULL,
 82:             '.odd' => NULL,
 83:             '.error' => NULL,
 84:         ),
 85: 
 86:         'control' => array(
 87:             'container' => 'td',
 88:             '.odd' => NULL,
 89: 
 90:             'description' => 'small',
 91:             'requiredsuffix' => '',
 92:             'errorcontainer' => 'span class=error',
 93:             'erroritem' => '',
 94: 
 95:             '.required' => 'required',
 96:             '.text' => 'text',
 97:             '.password' => 'text',
 98:             '.file' => 'text',
 99:             '.submit' => 'button',
100:             '.image' => 'imagebutton',
101:             '.button' => 'button',
102:         ),
103: 
104:         'label' => array(
105:             'container' => 'th',
106:             'suffix' => NULL,
107:             'requiredsuffix' => '',
108:         ),
109: 
110:         'hidden' => array(
111:             'container' => 'div',
112:         ),
113:     );
114: 
115:     /** @var Nette\Forms\Form */
116:     protected $form;
117: 
118:     /** @var int */
119:     protected $counter;
120: 
121: 
122:     /**
123:      * Provides complete form rendering.
124:      * @param  Nette\Forms\Form
125:      * @param  string 'begin', 'errors', 'ownerrors', 'body', 'end' or empty to render all
126:      * @return string
127:      */
128:     public function render(Nette\Forms\Form $form, $mode = NULL)
129:     {
130:         if ($this->form !== $form) {
131:             $this->form = $form;
132:         }
133: 
134:         $s = '';
135:         if (!$mode || $mode === 'begin') {
136:             $s .= $this->renderBegin();
137:         }
138:         if (!$mode || strtolower($mode) === 'ownerrors') {
139:             $s .= $this->renderErrors();
140: 
141:         } elseif ($mode === 'errors') {
142:             $s .= $this->renderErrors(NULL, FALSE);
143:         }
144:         if (!$mode || $mode === 'body') {
145:             $s .= $this->renderBody();
146:         }
147:         if (!$mode || $mode === 'end') {
148:             $s .= $this->renderEnd();
149:         }
150:         return $s;
151:     }
152: 
153: 
154:     /**
155:      * Renders form begin.
156:      * @return string
157:      */
158:     public function renderBegin()
159:     {
160:         $this->counter = 0;
161: 
162:         foreach ($this->form->getControls() as $control) {
163:             $control->setOption('rendered', FALSE);
164:         }
165: 
166:         if (strcasecmp($this->form->getMethod(), 'get') === 0) {
167:             $el = clone $this->form->getElementPrototype();
168:             $query = parse_url($el->action, PHP_URL_QUERY);
169:             $el->action = str_replace("?$query", '', $el->action);
170:             $s = '';
171:             foreach (preg_split('#[;&]#', $query, NULL, PREG_SPLIT_NO_EMPTY) as $param) {
172:                 $parts = explode('=', $param, 2);
173:                 $name = urldecode($parts[0]);
174:                 if (!isset($this->form[$name])) {
175:                     $s .= Html::el('input', array('type' => 'hidden', 'name' => $name, 'value' => urldecode($parts[1])));
176:                 }
177:             }
178:             return $el->startTag() . ($s ? "\n\t" . $this->getWrapper('hidden container')->setHtml($s) : '');
179: 
180:         } else {
181:             return $this->form->getElementPrototype()->startTag();
182:         }
183:     }
184: 
185: 
186:     /**
187:      * Renders form end.
188:      * @return string
189:      */
190:     public function renderEnd()
191:     {
192:         $s = '';
193:         foreach ($this->form->getControls() as $control) {
194:             if ($control instanceof Nette\Forms\Controls\HiddenField && !$control->getOption('rendered')) {
195:                 $s .= $control->getControl();
196:             }
197:         }
198:         if (iterator_count($this->form->getComponents(TRUE, 'Nette\Forms\Controls\TextInput')) < 2) {
199:             $s .= '<!--[if IE]><input type=IEbug disabled style="display:none"><![endif]-->';
200:         }
201:         if ($s) {
202:             $s = $this->getWrapper('hidden container')->setHtml($s) . "\n";
203:         }
204: 
205:         return $s . $this->form->getElementPrototype()->endTag() . "\n";
206:     }
207: 
208: 
209:     /**
210:      * Renders validation errors (per form or per control).
211:      * @return string
212:      */
213:     public function renderErrors(Nette\Forms\IControl $control = NULL, $own = TRUE)
214:     {
215:         $errors = $control
216:             ? $control->getErrors()
217:             : ($own ? $this->form->getOwnErrors() : $this->form->getErrors());
218:         if (!$errors) {
219:             return;
220:         }
221:         $container = $this->getWrapper($control ? 'control errorcontainer' : 'error container');
222:         $item = $this->getWrapper($control ? 'control erroritem' : 'error item');
223: 
224:         foreach ($errors as $error) {
225:             $item = clone $item;
226:             if ($error instanceof Html) {
227:                 $item->add($error);
228:             } else {
229:                 $item->setText($error);
230:             }
231:             $container->add($item);
232:         }
233:         return "\n" . $container->render($control ? 1 : 0);
234:     }
235: 
236: 
237:     /**
238:      * Renders form body.
239:      * @return string
240:      */
241:     public function renderBody()
242:     {
243:         $s = $remains = '';
244: 
245:         $defaultContainer = $this->getWrapper('group container');
246:         $translator = $this->form->getTranslator();
247: 
248:         foreach ($this->form->getGroups() as $group) {
249:             if (!$group->getControls() || !$group->getOption('visual')) {
250:                 continue;
251:             }
252: 
253:             $container = $group->getOption('container', $defaultContainer);
254:             $container = $container instanceof Html ? clone $container : Html::el($container);
255: 
256:             $id = $group->getOption('id');
257:             if ($id) {
258:                 $container->id = $id;
259:             }
260: 
261:             $s .= "\n" . $container->startTag();
262: 
263:             $text = $group->getOption('label');
264:             if ($text instanceof Html) {
265:                 $s .= $this->getWrapper('group label')->add($text);
266: 
267:             } elseif (is_string($text)) {
268:                 if ($translator !== NULL) {
269:                     $text = $translator->translate($text);
270:                 }
271:                 $s .= "\n" . $this->getWrapper('group label')->setText($text) . "\n";
272:             }
273: 
274:             $text = $group->getOption('description');
275:             if ($text instanceof Html) {
276:                 $s .= $text;
277: 
278:             } elseif (is_string($text)) {
279:                 if ($translator !== NULL) {
280:                     $text = $translator->translate($text);
281:                 }
282:                 $s .= $this->getWrapper('group description')->setText($text) . "\n";
283:             }
284: 
285:             $s .= $this->renderControls($group);
286: 
287:             $remains = $container->endTag() . "\n" . $remains;
288:             if (!$group->getOption('embedNext')) {
289:                 $s .= $remains;
290:                 $remains = '';
291:             }
292:         }
293: 
294:         $s .= $remains . $this->renderControls($this->form);
295: 
296:         $container = $this->getWrapper('form container');
297:         $container->setHtml($s);
298:         return $container->render(0);
299:     }
300: 
301: 
302:     /**
303:      * Renders group of controls.
304:      * @param  Nette\Forms\Container|Nette\Forms\ControlGroup
305:      * @return string
306:      */
307:     public function renderControls($parent)
308:     {
309:         if (!($parent instanceof Nette\Forms\Container || $parent instanceof Nette\Forms\ControlGroup)) {
310:             throw new Nette\InvalidArgumentException('Argument must be Nette\Forms\Container or Nette\Forms\ControlGroup instance.');
311:         }
312: 
313:         $container = $this->getWrapper('controls container');
314: 
315:         $buttons = NULL;
316:         foreach ($parent->getControls() as $control) {
317:             if ($control->getOption('rendered') || $control instanceof Nette\Forms\Controls\HiddenField || $control->getForm(FALSE) !== $this->form) {
318:                 // skip
319: 
320:             } elseif ($control instanceof Nette\Forms\Controls\Button) {
321:                 $buttons[] = $control;
322: 
323:             } else {
324:                 if ($buttons) {
325:                     $container->add($this->renderPairMulti($buttons));
326:                     $buttons = NULL;
327:                 }
328:                 $container->add($this->renderPair($control));
329:             }
330:         }
331: 
332:         if ($buttons) {
333:             $container->add($this->renderPairMulti($buttons));
334:         }
335: 
336:         $s = '';
337:         if (count($container)) {
338:             $s .= "\n" . $container . "\n";
339:         }
340: 
341:         return $s;
342:     }
343: 
344: 
345:     /**
346:      * Renders single visual row.
347:      * @return string
348:      */
349:     public function renderPair(Nette\Forms\IControl $control)
350:     {
351:         $pair = $this->getWrapper('pair container');
352:         $pair->add($this->renderLabel($control));
353:         $pair->add($this->renderControl($control));
354:         $pair->class($this->getValue($control->isRequired() ? 'pair .required' : 'pair .optional'), TRUE);
355:         $pair->class($control->hasErrors() ? $this->getValue('pair .error') : NULL, TRUE);
356:         $pair->class($control->getOption('class'), TRUE);
357:         if (++$this->counter % 2) {
358:             $pair->class($this->getValue('pair .odd'), TRUE);
359:         }
360:         $pair->id = $control->getOption('id');
361:         return $pair->render(0);
362:     }
363: 
364: 
365:     /**
366:      * Renders single visual row of multiple controls.
367:      * @param  Nette\Forms\IControl[]
368:      * @return string
369:      */
370:     public function renderPairMulti(array $controls)
371:     {
372:         $s = array();
373:         foreach ($controls as $control) {
374:             if (!$control instanceof Nette\Forms\IControl) {
375:                 throw new Nette\InvalidArgumentException('Argument must be array of Nette\Forms\IControl instances.');
376:             }
377:             $description = $control->getOption('description');
378:             if ($description instanceof Html) {
379:                 $description = ' ' . $control->getOption('description');
380: 
381:             } elseif (is_string($description)) {
382:                 $description = ' ' . $this->getWrapper('control description')->setText($control->translate($description));
383: 
384:             } else {
385:                 $description = '';
386:             }
387: 
388:             $control->setOption('rendered', TRUE);
389:             $el = $control->getControl();
390:             if ($el instanceof Html && $el->getName() === 'input') {
391:                 $el->class($this->getValue("control .$el->type"), TRUE);
392:             }
393:             $s[] = $el . $description;
394:         }
395:         $pair = $this->getWrapper('pair container');
396:         $pair->add($this->renderLabel($control));
397:         $pair->add($this->getWrapper('control container')->setHtml(implode(' ', $s)));
398:         return $pair->render(0);
399:     }
400: 
401: 
402:     /**
403:      * Renders 'label' part of visual row of controls.
404:      * @return string
405:      */
406:     public function renderLabel(Nette\Forms\IControl $control)
407:     {
408:         $suffix = $this->getValue('label suffix') . ($control->isRequired() ? $this->getValue('label requiredsuffix') : '');
409:         $label = $control->getLabel();
410:         if ($label instanceof Html) {
411:             $label->add($suffix);
412:             if ($control->isRequired()) {
413:                 $label->class($this->getValue('control .required'), TRUE);
414:             }
415:         } elseif ($label != NULL) { // @intentionally ==
416:             $label .= $suffix;
417:         }
418:         return $this->getWrapper('label container')->setHtml($label);
419:     }
420: 
421: 
422:     /**
423:      * Renders 'control' part of visual row of controls.
424:      * @return string
425:      */
426:     public function renderControl(Nette\Forms\IControl $control)
427:     {
428:         $body = $this->getWrapper('control container');
429:         if ($this->counter % 2) {
430:             $body->class($this->getValue('control .odd'), TRUE);
431:         }
432: 
433:         $description = $control->getOption('description');
434:         if ($description instanceof Html) {
435:             $description = ' ' . $description;
436: 
437:         } elseif (is_string($description)) {
438:             $description = ' ' . $this->getWrapper('control description')->setText($control->translate($description));
439: 
440:         } else {
441:             $description = '';
442:         }
443: 
444:         if ($control->isRequired()) {
445:             $description = $this->getValue('control requiredsuffix') . $description;
446:         }
447: 
448:         $control->setOption('rendered', TRUE);
449:         $el = $control->getControl();
450:         if ($el instanceof Html && $el->getName() === 'input') {
451:             $el->class($this->getValue("control .$el->type"), TRUE);
452:         }
453:         return $body->setHtml($el . $description . $this->renderErrors($control));
454:     }
455: 
456: 
457:     /**
458:      * @param  string
459:      * @return Html
460:      */
461:     protected function getWrapper($name)
462:     {
463:         $data = $this->getValue($name);
464:         return $data instanceof Html ? clone $data : Html::el($data);
465:     }
466: 
467: 
468:     /**
469:      * @param  string
470:      * @return string
471:      */
472:     protected function getValue($name)
473:     {
474:         $name = explode(' ', $name);
475:         $data = & $this->wrappers[$name[0]][$name[1]];
476:         return $data;
477:     }
478: 
479: }
480: 
Nette 2.3.1 API API documentation generated by ApiGen 2.8.0