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
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Utils
  • none
  • Tracy
    • Bridges
      • Nette

Classes

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