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 void 
  177. 176:      */
  178. 177:     public function setClientScript($clientScript NULL)
  179. 178:     {
  180. 179:         $this->clientScript = $clientScript;
  181. 180:     }
  182. 181:  
  183. 182:  
  184. 183:  
  185. 184:     /**
  186. 185:      * Returns JavaScript handler.
  187. 186:      * @return mixed 
  188. 187:      */
  189. 188:     public function getClientScript()
  190. 189:     {
  191. 190:         if ($this->clientScript === TRUE{
  192. 191:             $this->clientScript = new InstantClientScript($this->form);
  193. 192:         }
  194. 193:         return $this->clientScript;
  195. 194:     }
  196. 195:  
  197. 196:  
  198. 197:  
  199. 198:     /**
  200. 199:      * Initializes form.
  201. 200:      * @return void 
  202. 201:      */
  203. 202:     protected function init()
  204. 203:     {
  205. 204:         $clientScript $this->getClientScript();
  206. 205:         if ($clientScript !== NULL{
  207. 206:             $clientScript->enable();
  208. 207:         }
  209. 208:  
  210. 209:         // TODO: only for back compatiblity - remove?
  211. 210:         $wrapper $this->wrappers['control'];
  212. 211:         foreach ($this->form->getControls(as $control{
  213. 212:             if ($control->getOption('required'&& isset($wrapper['.required'])) {
  214. 213:                 $control->getLabelPrototype()->class($wrapper['.required']TRUE);
  215. 214:             }
  216. 215:  
  217. 216:             $el $control->getControlPrototype();
  218. 217:             if ($el->getName(=== 'input' && isset($wrapper['.' $el->type])) {
  219. 218:                 $el->class($wrapper['.' $el->type]TRUE);
  220. 219:             }
  221. 220:         }
  222. 221:     }
  223. 222:  
  224. 223:  
  225. 224:  
  226. 225:     /**
  227. 226:      * Renders form begin.
  228. 227:      * @return string 
  229. 228:      */
  230. 229:     public function renderBegin()
  231. 230:     {
  232. 231:         $this->counter = 0;
  233. 232:  
  234. 233:         foreach ($this->form->getControls(as $control{
  235. 234:             $control->setOption('rendered'FALSE);
  236. 235:         }
  237. 236:  
  238. 237:         if (strcasecmp($this->form->getMethod()'get'=== 0{
  239. 238:             $el clone $this->form->getElementPrototype();
  240. 239:             $uri explode('?'(string) $el->action2);
  241. 240:             $el->action $uri[0];
  242. 241:             $s '';
  243. 242:             if (isset($uri[1])) {
  244. 243:                 foreach (explode('&'$uri[1]as $param{
  245. 244:                     $parts explode('='$param2);
  246. 245:                     $s .= Html::el('input'array('type' => 'hidden''name' => urldecode($parts[0])'value' => urldecode($parts[1])));
  247. 246:                 }
  248. 247:                 $s "\n\t" $this->getWrapper('hidden container')->setHtml($s);
  249. 248:             }
  250. 249:             return $el->startTag($s;
  251. 250:  
  252. 251:  
  253. 252:         else {
  254. 253:             return $this->form->getElementPrototype()->startTag();
  255. 254:         }
  256. 255:     }
  257. 256:  
  258. 257:  
  259. 258:  
  260. 259:     /**
  261. 260:      * Renders form end.
  262. 261:      * @return string 
  263. 262:      */
  264. 263:     public function renderEnd()
  265. 264:     {
  266. 265:         $s '';
  267. 266:         foreach ($this->form->getControls(as $control{
  268. 267:             if ($control instanceof HiddenField && !$control->getOption('rendered')) {
  269. 268:                 $s .= (string) $control->getControl();
  270. 269:             }
  271. 270:         }
  272. 271:         if ($s{
  273. 272:             $s $this->getWrapper('hidden container')->setHtml($s"\n";
  274. 273:         }
  275. 274:  
  276. 275:         $s .= $this->form->getElementPrototype()->endTag("\n";
  277. 276:  
  278. 277:         $clientScript $this->getClientScript();
  279. 278:         if ($clientScript !== NULL{
  280. 279:             $s .= $clientScript->renderClientScript("\n";
  281. 280:         }
  282. 281:  
  283. 282:         return $s;
  284. 283:     }
  285. 284:  
  286. 285:  
  287. 286:  
  288. 287:     /**
  289. 288:      * Renders validation errors (per form or per control).
  290. 289:      * @param  IFormControl 
  291. 290:      * @return string 
  292. 291:      */
  293. 292:     public function renderErrors(IFormControl $control NULL)
  294. 293:     {
  295. 294:         $errors $control === NULL $this->form->getErrors($control->getErrors();
  296. 295:         if (count($errors)) {
  297. 296:             $ul $this->getWrapper('error container');
  298. 297:             $li $this->getWrapper('error item');
  299. 298:  
  300. 299:             foreach ($errors as $error{
  301. 300:                 $item clone $li;
  302. 301:                 if ($error instanceof Html{
  303. 302:                     $item->add($error);
  304. 303:                 else {
  305. 304:                     $item->setText($error);
  306. 305:                 }
  307. 306:                 $ul->add($item);
  308. 307:             }
  309. 308:             return "\n" $ul->render(0);
  310. 309:         }
  311. 310:     }
  312. 311:  
  313. 312:  
  314. 313:  
  315. 314:     /**
  316. 315:      * Renders form body.
  317. 316:      * @return string 
  318. 317:      */
  319. 318:     public function renderBody()
  320. 319:     {
  321. 320:         $s $remains '';
  322. 321:  
  323. 322:         $defaultContainer $this->getWrapper('group container');
  324. 323:         $translator $this->form->getTranslator();
  325. 324:  
  326. 325:         foreach ($this->form->getGroups(as $group{
  327. 326:             if (!$group->getControls(|| !$group->getOption('visual')) continue;
  328. 327:  
  329. 328:             $container $group->getOption('container'$defaultContainer);
  330. 329:             $container $container instanceof Html clone $container Html::el($container);
  331. 330:  
  332. 331:             $s .= "\n" $container->startTag();
  333. 332:  
  334. 333:             $text $group->getOption('label');
  335. 334:             if ($text instanceof Html{
  336. 335:                 $s .= $text;
  337. 336:  
  338. 337:             elseif (is_string($text)) {
  339. 338:                 if ($translator !== NULL{
  340. 339:                     $text $translator->translate($text);
  341. 340:                 }
  342. 341:                 $s .= "\n" $this->getWrapper('group label')->setText($text"\n";
  343. 342:             }
  344. 343:  
  345. 344:             $text $group->getOption('description');
  346. 345:             if ($text instanceof Html{
  347. 346:                 $s .= $text;
  348. 347:  
  349. 348:             elseif (is_string($text)) {
  350. 349:                 if ($translator !== NULL{
  351. 350:                     $text $translator->translate($text);
  352. 351:                 }
  353. 352:                 $s .= $this->getWrapper('group description')->setText($text"\n";
  354. 353:             }
  355. 354:  
  356. 355:             $s .= $this->renderControls($group);
  357. 356:  
  358. 357:             $remains $container->endTag("\n" $remains;
  359. 358:             if (!$group->getOption('embedNext')) {
  360. 359:                 $s .= $remains;
  361. 360:                 $remains '';
  362. 361:             }
  363. 362:         }
  364. 363:  
  365. 364:         $s .= $remains $this->renderControls($this->form);
  366. 365:  
  367. 366:         $container $this->getWrapper('form container');
  368. 367:         $container->setHtml($s);
  369. 368:         return $container->render(0);
  370. 369:     }
  371. 370:  
  372. 371:  
  373. 372:  
  374. 373:     /**
  375. 374:      * Renders group of controls.
  376. 375:      * @param  FormContainer|FormGroup
  377. 376:      * @return string 
  378. 377:      */
  379. 378:     public function renderControls($parent)
  380. 379:     {
  381. 380:         if (!($parent instanceof FormContainer || $parent instanceof FormGroup)) {
  382. 381:             throw new InvalidArgumentException("Argument must be FormContainer or FormGroup instance.");
  383. 382:         }
  384. 383:  
  385. 384:         $container $this->getWrapper('controls container');
  386. 385:  
  387. 386:         $buttons NULL;
  388. 387:         foreach ($parent->getControls(as $control{
  389. 388:             if ($control->getOption('rendered'|| $control instanceof HiddenField || $control->getForm(FALSE!== $this->form{
  390. 389:                 // skip
  391. 390:  
  392. 391:             elseif ($control instanceof Button{
  393. 392:                 $buttons[$control;
  394. 393:  
  395. 394:             else {
  396. 395:                 if ($buttons{
  397. 396:                     $container->add($this->renderPairMulti($buttons));
  398. 397:                     $buttons NULL;
  399. 398:                 }
  400. 399:                 $container->add($this->renderPair($control));
  401. 400:             }
  402. 401:         }
  403. 402:  
  404. 403:         if ($buttons{
  405. 404:             $container->add($this->renderPairMulti($buttons));
  406. 405:         }
  407. 406:  
  408. 407:         $s '';
  409. 408:         if (count($container)) {
  410. 409:             $s .= "\n" $container "\n";
  411. 410:         }
  412. 411:  
  413. 412:         return $s;
  414. 413:     }
  415. 414:  
  416. 415:  
  417. 416:  
  418. 417:     /**
  419. 418:      * Renders single visual row.
  420. 419:      * @param  IFormControl 
  421. 420:      * @return string 
  422. 421:      */
  423. 422:     public function renderPair(IFormControl $control)
  424. 423:     {
  425. 424:         $pair $this->getWrapper('pair container');
  426. 425:         $pair->add($this->renderLabel($control));
  427. 426:         $pair->add($this->renderControl($control));
  428. 427:         $pair->class($this->getValue($control->getOption('required''pair .required' 'pair .optional')TRUE);
  429. 428:         $pair->class($control->getOption('class')TRUE);
  430. 429:         if (++$this->counter % 2$pair->class($this->getValue('pair .odd')TRUE);
  431. 430:         $pair->id $control->getOption('id');
  432. 431:         return $pair->render(0);
  433. 432:     }
  434. 433:  
  435. 434:  
  436. 435:  
  437. 436:     /**
  438. 437:      * Renders single visual row of multiple controls.
  439. 438:      * @param  array of IFormControl
  440. 439:      * @return string 
  441. 440:      */
  442. 441:     public function renderPairMulti(array $controls)
  443. 442:     {
  444. 443:         $s array();
  445. 444:         foreach ($controls as $control{
  446. 445:             if (!($control instanceof IFormControl)) {
  447. 446:                 throw new InvalidArgumentException("Argument must be array of IFormControl instances.");
  448. 447:             }
  449. 448:             $s[= (string) $control->getControl();
  450. 449:         }
  451. 450:         $pair $this->getWrapper('pair container');
  452. 451:         $pair->add($this->renderLabel($control));
  453. 452:         $pair->add($this->getWrapper('control container')->setHtml(implode(" "$s)));
  454. 453:         return $pair->render(0);
  455. 454:     }
  456. 455:  
  457. 456:  
  458. 457:  
  459. 458:     /**
  460. 459:      * Renders 'label' part of visual row of controls.
  461. 460:      * @param  IFormControl 
  462. 461:      * @return string 
  463. 462:      */
  464. 463:     public function renderLabel(IFormControl $control)
  465. 464:     {
  466. 465:         $head $this->getWrapper('label container');
  467. 466:  
  468. 467:         if ($control instanceof Checkbox || $control instanceof Button{
  469. 468:             return $head->setHtml(($head->getName(=== 'td' || $head->getName(=== 'th''&nbsp;' '');
  470. 469:  
  471. 470:         else {
  472. 471:             $label $control->getLabel();
  473. 472:             $suffix $this->getValue('label suffix'($control->getOption('required'$this->getValue('label requiredsuffix''');
  474. 473:             if ($label instanceof Html{
  475. 474:                 $label->setHtml($label->getHtml($suffix);
  476. 475:                 $suffix '';
  477. 476:             }
  478. 477:             return $head->setHtml((string) $label $suffix);
  479. 478:         }
  480. 479:     }
  481. 480:  
  482. 481:  
  483. 482:  
  484. 483:     /**
  485. 484:      * Renders 'control' part of visual row of controls.
  486. 485:      * @param  IFormControl 
  487. 486:      * @return string 
  488. 487:      */
  489. 488:     public function renderControl(IFormControl $control)
  490. 489:     {
  491. 490:         $body $this->getWrapper('control container');
  492. 491:         if ($this->counter 2$body->class($this->getValue('control .odd')TRUE);
  493. 492:  
  494. 493:         $description $control->getOption('description');
  495. 494:         if ($description instanceof Html{
  496. 495:             $description ' ' $control->getOption('description');
  497. 496:  
  498. 497:         elseif (is_string($description)) {
  499. 498:             $description ' ' $this->getWrapper('control description')->setText($description);
  500. 499:  
  501. 500:         else {
  502. 501:             $description '';
  503. 502:         }
  504. 503:  
  505. 504:         if ($control->getOption('required')) {
  506. 505:             $description $this->getValue('control requiredsuffix'$description;
  507. 506:         }
  508. 507:  
  509. 508:         if ($this->getValue('control errors')) {
  510. 509:             $description .= $this->renderErrors($control);
  511. 510:         }
  512. 511:  
  513. 512:         if ($control instanceof Checkbox || $control instanceof Button{
  514. 513:             return $body->setHtml((string) $control->getControl(. (string) $control->getLabel($description);
  515. 514:  
  516. 515:         else {
  517. 516:             return $body->setHtml((string) $control->getControl($description);
  518. 517:         }
  519. 518:     }
  520. 519:  
  521. 520:  
  522. 521:  
  523. 522:     /**
  524. 523:      * @param  string 
  525. 524:      * @return Html 
  526. 525:      */
  527. 526:     protected function getWrapper($name)
  528. 527:     {
  529. 528:         $data $this->getValue($name);
  530. 529:         return $data instanceof Html clone $data Html::el($data);
  531. 530:     }
  532. 531:  
  533. 532:  
  534. 533:  
  535. 534:     /**
  536. 535:      * @param  string 
  537. 536:      * @return string 
  538. 537:      */
  539. 538:     protected function getValue($name)
  540. 539:     {
  541. 540:         $name explode(' '$name);
  542. 541:         $data $this->wrappers[$name[0]][$name[1]];
  543. 542:         return $data;
  544. 543:     }
  545. 544: