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