1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Forms\Rendering;
9:
10: use Nette;
11: use Nette\Utils\Html;
12:
13:
14: 15: 16:
17: class DefaultFormRenderer extends Nette\Object implements Nette\Forms\IFormRenderer
18: {
19: 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: 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:
114: protected $form;
115:
116:
117: protected $counter;
118:
119:
120: 121: 122: 123: 124: 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: 154: 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: 186: 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: 209: 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: 237: 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: 302: 303: 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:
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: 345: 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: 365: 366: 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: 402: 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) {
414: $label .= $suffix;
415: }
416: return $this->getWrapper('label container')->setHtml($label);
417: }
418:
419:
420: 421: 422: 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: 457: 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: 468: 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: