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