1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13:
14:
15: 16: 17: 18: 19: 20:
21: class NConventionalRenderer extends NObject implements IFormRenderer
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: 59:
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:
117: protected $form;
118:
119:
120: protected $clientScript = TRUE;
121:
122:
123: protected $counter;
124:
125:
126:
127: 128: 129: 130: 131: 132:
133: public function render(NForm $form, $mode = NULL)
134: {
135: if ($this->form !== $form) {
136: $this->form = $form;
137: $this->init();
138: }
139:
140: $s = '';
141: if (!$mode || $mode === 'begin') {
142: $s .= $this->renderBegin();
143: }
144: if ((!$mode && $this->getValue('form errors')) || $mode === 'errors') {
145: $s .= $this->renderErrors();
146: }
147: if (!$mode || $mode === 'body') {
148: $s .= $this->renderBody();
149: }
150: if (!$mode || $mode === 'end') {
151: $s .= $this->renderEnd();
152: }
153: return $s;
154: }
155:
156:
157:
158: 159: 160: 161: 162:
163: public function setClientScript($clientScript = NULL)
164: {
165: $this->clientScript = $clientScript;
166: return $this;
167: }
168:
169:
170:
171: 172: 173: 174:
175: public function getClientScript()
176: {
177: if ($this->clientScript === TRUE) {
178: $this->clientScript = new NInstantClientScript($this->form);
179: }
180: return $this->clientScript;
181: }
182:
183:
184:
185: 186: 187: 188:
189: protected function init()
190: {
191: $clientScript = $this->getClientScript();
192: if ($clientScript !== NULL) {
193: $clientScript->enable();
194: }
195:
196:
197: $wrapper = & $this->wrappers['control'];
198: foreach ($this->form->getControls() as $control) {
199: if ($control->getOption('required') && isset($wrapper['.required'])) {
200: $control->getLabelPrototype()->class($wrapper['.required'], TRUE);
201: }
202:
203: $el = $control->getControlPrototype();
204: if ($el->getName() === 'input' && isset($wrapper['.' . $el->type])) {
205: $el->class($wrapper['.' . $el->type], TRUE);
206: }
207: }
208: }
209:
210:
211:
212: 213: 214: 215:
216: public function renderBegin()
217: {
218: $this->counter = 0;
219:
220: foreach ($this->form->getControls() as $control) {
221: $control->setOption('rendered', FALSE);
222: }
223:
224: if (strcasecmp($this->form->getMethod(), 'get') === 0) {
225: $el = clone $this->form->getElementPrototype();
226: $uri = explode('?', (string) $el->action, 2);
227: $el->action = $uri[0];
228: $s = '';
229: if (isset($uri[1])) {
230: foreach (preg_split('#[;&]#', $uri[1]) as $param) {
231: $parts = explode('=', $param, 2);
232: $name = urldecode($parts[0]);
233: if (!isset($this->form[$name])) {
234: $s .= NHtml::el('input', array('type' => 'hidden', 'name' => $name, 'value' => urldecode($parts[1])));
235: }
236: }
237: $s = "\n\t" . $this->getWrapper('hidden container')->setHtml($s);
238: }
239: return $el->startTag() . $s;
240:
241:
242: } else {
243: return $this->form->getElementPrototype()->startTag();
244: }
245: }
246:
247:
248:
249: 250: 251: 252:
253: public function renderEnd()
254: {
255: $s = '';
256: foreach ($this->form->getControls() as $control) {
257: if ($control instanceof NHiddenField && !$control->getOption('rendered')) {
258: $s .= (string) $control->getControl();
259: }
260: }
261: if ($s) {
262: $s = $this->getWrapper('hidden container')->setHtml($s) . "\n";
263: }
264:
265: $s .= $this->form->getElementPrototype()->endTag() . "\n";
266:
267: $clientScript = $this->getClientScript();
268: if ($clientScript !== NULL) {
269: $s .= $clientScript->renderClientScript() . "\n";
270: }
271:
272: return $s;
273: }
274:
275:
276:
277: 278: 279: 280: 281:
282: public function renderErrors(IFormControl $control = NULL)
283: {
284: $errors = $control === NULL ? $this->form->getErrors() : $control->getErrors();
285: if (count($errors)) {
286: $ul = $this->getWrapper('error container');
287: $li = $this->getWrapper('error item');
288:
289: foreach ($errors as $error) {
290: $item = clone $li;
291: if ($error instanceof NHtml) {
292: $item->add($error);
293: } else {
294: $item->setText($error);
295: }
296: $ul->add($item);
297: }
298: return "\n" . $ul->render(0);
299: }
300: }
301:
302:
303:
304: 305: 306: 307:
308: public function renderBody()
309: {
310: $s = $remains = '';
311:
312: $defaultContainer = $this->getWrapper('group container');
313: $translator = $this->form->getTranslator();
314:
315: foreach ($this->form->getGroups() as $group) {
316: if (!$group->getControls() || !$group->getOption('visual')) continue;
317:
318: $container = $group->getOption('container', $defaultContainer);
319: $container = $container instanceof NHtml ? clone $container : NHtml::el($container);
320:
321: $s .= "\n" . $container->startTag();
322:
323: $text = $group->getOption('label');
324: if ($text instanceof NHtml) {
325: $s .= $text;
326:
327: } elseif (is_string($text)) {
328: if ($translator !== NULL) {
329: $text = $translator->translate($text);
330: }
331: $s .= "\n" . $this->getWrapper('group label')->setText($text) . "\n";
332: }
333:
334: $text = $group->getOption('description');
335: if ($text instanceof NHtml) {
336: $s .= $text;
337:
338: } elseif (is_string($text)) {
339: if ($translator !== NULL) {
340: $text = $translator->translate($text);
341: }
342: $s .= $this->getWrapper('group description')->setText($text) . "\n";
343: }
344:
345: $s .= $this->renderControls($group);
346:
347: $remains = $container->endTag() . "\n" . $remains;
348: if (!$group->getOption('embedNext')) {
349: $s .= $remains;
350: $remains = '';
351: }
352: }
353:
354: $s .= $remains . $this->renderControls($this->form);
355:
356: $container = $this->getWrapper('form container');
357: $container->setHtml($s);
358: return $container->render(0);
359: }
360:
361:
362:
363: 364: 365: 366: 367:
368: public function renderControls($parent)
369: {
370: if (!($parent instanceof NFormContainer || $parent instanceof NFormGroup)) {
371: throw new InvalidArgumentException("Argument must be FormContainer or FormGroup instance.");
372: }
373:
374: $container = $this->getWrapper('controls container');
375:
376: $buttons = NULL;
377: foreach ($parent->getControls() as $control) {
378: if ($control->getOption('rendered') || $control instanceof NHiddenField || $control->getForm(FALSE) !== $this->form) {
379:
380:
381: } elseif ($control instanceof NButton) {
382: $buttons[] = $control;
383:
384: } else {
385: if ($buttons) {
386: $container->add($this->renderPairMulti($buttons));
387: $buttons = NULL;
388: }
389: $container->add($this->renderPair($control));
390: }
391: }
392:
393: if ($buttons) {
394: $container->add($this->renderPairMulti($buttons));
395: }
396:
397: $s = '';
398: if (count($container)) {
399: $s .= "\n" . $container . "\n";
400: }
401:
402: return $s;
403: }
404:
405:
406:
407: 408: 409: 410: 411:
412: public function renderPair(IFormControl $control)
413: {
414: $pair = $this->getWrapper('pair container');
415: $pair->add($this->renderLabel($control));
416: $pair->add($this->renderControl($control));
417: $pair->class($this->getValue($control->getOption('required') ? 'pair .required' : 'pair .optional'), TRUE);
418: $pair->class($control->getOption('class'), TRUE);
419: if (++$this->counter % 2) $pair->class($this->getValue('pair .odd'), TRUE);
420: $pair->id = $control->getOption('id');
421: return $pair->render(0);
422: }
423:
424:
425:
426: 427: 428: 429: 430:
431: public function renderPairMulti(array $controls)
432: {
433: $s = array();
434: foreach ($controls as $control) {
435: if (!($control instanceof IFormControl)) {
436: throw new InvalidArgumentException("Argument must be array of IFormControl instances.");
437: }
438: $s[] = (string) $control->getControl();
439: }
440: $pair = $this->getWrapper('pair container');
441: $pair->add($this->renderLabel($control));
442: $pair->add($this->getWrapper('control container')->setHtml(implode(" ", $s)));
443: return $pair->render(0);
444: }
445:
446:
447:
448: 449: 450: 451: 452:
453: public function renderLabel(IFormControl $control)
454: {
455: $head = $this->getWrapper('label container');
456:
457: if ($control instanceof NCheckbox || $control instanceof NButton) {
458: return $head->setHtml(($head->getName() === 'td' || $head->getName() === 'th') ? ' ' : '');
459:
460: } else {
461: $label = $control->getLabel();
462: $suffix = $this->getValue('label suffix') . ($control->getOption('required') ? $this->getValue('label requiredsuffix') : '');
463: if ($label instanceof NHtml) {
464: $label->setHtml($label->getHtml() . $suffix);
465: $suffix = '';
466: }
467: return $head->setHtml((string) $label . $suffix);
468: }
469: }
470:
471:
472:
473: 474: 475: 476: 477:
478: public function renderControl(IFormControl $control)
479: {
480: $body = $this->getWrapper('control container');
481: if ($this->counter % 2) $body->class($this->getValue('control .odd'), TRUE);
482:
483: $description = $control->getOption('description');
484: if ($description instanceof NHtml) {
485: $description = ' ' . $control->getOption('description');
486:
487: } elseif (is_string($description)) {
488: $description = ' ' . $this->getWrapper('control description')->setText($control->translate($description));
489:
490: } else {
491: $description = '';
492: }
493:
494: if ($control->getOption('required')) {
495: $description = $this->getValue('control requiredsuffix') . $description;
496: }
497:
498: if ($this->getValue('control errors')) {
499: $description .= $this->renderErrors($control);
500: }
501:
502: if ($control instanceof NCheckbox || $control instanceof NButton) {
503: return $body->setHtml((string) $control->getControl() . (string) $control->getLabel() . $description);
504:
505: } else {
506: return $body->setHtml((string) $control->getControl() . $description);
507: }
508: }
509:
510:
511:
512: 513: 514: 515:
516: protected function getWrapper($name)
517: {
518: $data = $this->getValue($name);
519: return $data instanceof NHtml ? clone $data : NHtml::el($data);
520: }
521:
522:
523:
524: 525: 526: 527:
528: protected function getValue($name)
529: {
530: $name = explode(' ', $name);
531: $data = & $this->wrappers[$name[0]][$name[1]];
532: return $data;
533: }
534:
535: }
536: