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