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

Classes

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