Source for file ConventionalRenderer.php

Documentation is available at ConventionalRenderer.php

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