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