PageRenderTime 28ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/libs/Nette/Forms/Rendering/DefaultFormRenderer.php

https://github.com/premiumcombination/nts
PHP | 514 lines | 303 code | 96 blank | 115 comment | 56 complexity | 7b2b9e86a72b2eea8ce9d4a46de50e5a MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of the Nette Framework (http://nette.org)
  4. *
  5. * Copyright (c) 2004, 2011 David Grudl (http://davidgrudl.com)
  6. *
  7. * For the full copyright and license information, please view
  8. * the file license.txt that was distributed with this source code.
  9. */
  10. namespace Nette\Forms\Rendering;
  11. use Nette,
  12. Nette\Utils\Html;
  13. /**
  14. * Converts a Form into the HTML output.
  15. *
  16. * @author David Grudl
  17. */
  18. class DefaultFormRenderer extends Nette\Object implements Nette\Forms\IFormRenderer
  19. {
  20. /**
  21. * /--- form.container
  22. *
  23. * /--- if (form.errors) error.container
  24. * .... error.item [.class]
  25. * \---
  26. *
  27. * /--- hidden.container
  28. * .... HIDDEN CONTROLS
  29. * \---
  30. *
  31. * /--- group.container
  32. * .... group.label
  33. * .... group.description
  34. *
  35. * /--- controls.container
  36. *
  37. * /--- pair.container [.required .optional .odd]
  38. *
  39. * /--- label.container
  40. * .... LABEL
  41. * .... label.suffix
  42. * .... label.requiredsuffix
  43. * \---
  44. *
  45. * /--- control.container [.odd]
  46. * .... CONTROL [.required .text .password .file .submit .button]
  47. * .... control.requiredsuffix
  48. * .... control.description
  49. * .... if (control.errors) error.container
  50. * \---
  51. * \---
  52. * \---
  53. * \---
  54. * \--
  55. *
  56. * @var array of HTML tags */
  57. public $wrappers = array(
  58. 'form' => array(
  59. 'container' => NULL,
  60. 'errors' => TRUE,
  61. ),
  62. 'error' => array(
  63. 'container' => 'ul class=error',
  64. 'item' => 'li',
  65. ),
  66. 'group' => array(
  67. 'container' => 'fieldset',
  68. 'label' => 'legend',
  69. 'description' => 'p',
  70. ),
  71. 'controls' => array(
  72. 'container' => 'table',
  73. ),
  74. 'pair' => array(
  75. 'container' => 'tr',
  76. '.required' => 'required',
  77. '.optional' => NULL,
  78. '.odd' => NULL,
  79. ),
  80. 'control' => array(
  81. 'container' => 'td',
  82. '.odd' => NULL,
  83. 'errors' => FALSE,
  84. 'description' => 'small',
  85. 'requiredsuffix' => '',
  86. '.required' => 'required',
  87. '.text' => 'text',
  88. '.password' => 'text',
  89. '.file' => 'text',
  90. '.submit' => 'button',
  91. '.image' => 'imagebutton',
  92. '.button' => 'button',
  93. ),
  94. 'label' => array(
  95. 'container' => 'th',
  96. 'suffix' => NULL,
  97. 'requiredsuffix' => '',
  98. ),
  99. 'hidden' => array(
  100. 'container' => 'div',
  101. ),
  102. );
  103. /** @var Nette\Forms\Form */
  104. protected $form;
  105. /** @var int */
  106. protected $counter;
  107. /**
  108. * Provides complete form rendering.
  109. * @param Nette\Forms\Form
  110. * @param string 'begin', 'errors', 'body', 'end' or empty to render all
  111. * @return string
  112. */
  113. public function render(Nette\Forms\Form $form, $mode = NULL)
  114. {
  115. if ($this->form !== $form) {
  116. $this->form = $form;
  117. $this->init();
  118. }
  119. $s = '';
  120. if (!$mode || $mode === 'begin') {
  121. $s .= $this->renderBegin();
  122. }
  123. if ((!$mode && $this->getValue('form errors')) || $mode === 'errors') {
  124. $s .= $this->renderErrors();
  125. }
  126. if (!$mode || $mode === 'body') {
  127. $s .= $this->renderBody();
  128. }
  129. if (!$mode || $mode === 'end') {
  130. $s .= $this->renderEnd();
  131. }
  132. return $s;
  133. }
  134. /** @deprecated */
  135. public function setClientScript()
  136. {
  137. trigger_error(__METHOD__ . '() is deprecated; use unobstructive JavaScript instead.', E_USER_WARNING);
  138. return $this;
  139. }
  140. /**
  141. * Initializes form.
  142. * @return void
  143. */
  144. protected function init()
  145. {
  146. // TODO: only for back compatiblity - remove?
  147. $wrapper = & $this->wrappers['control'];
  148. foreach ($this->form->getControls() as $control) {
  149. if ($control->isRequired() && isset($wrapper['.required'])) {
  150. $control->getLabelPrototype()->class($wrapper['.required'], TRUE);
  151. }
  152. $el = $control->getControlPrototype();
  153. if ($el->getName() === 'input' && isset($wrapper['.' . $el->type])) {
  154. $el->class($wrapper['.' . $el->type], TRUE);
  155. }
  156. }
  157. }
  158. /**
  159. * Renders form begin.
  160. * @return string
  161. */
  162. public function renderBegin()
  163. {
  164. $this->counter = 0;
  165. foreach ($this->form->getControls() as $control) {
  166. $control->setOption('rendered', FALSE);
  167. }
  168. if (strcasecmp($this->form->getMethod(), 'get') === 0) {
  169. $el = clone $this->form->getElementPrototype();
  170. $url = explode('?', (string) $el->action, 2);
  171. $el->action = $url[0];
  172. $s = '';
  173. if (isset($url[1])) {
  174. foreach (preg_split('#[;&]#', $url[1]) as $param) {
  175. $parts = explode('=', $param, 2);
  176. $name = urldecode($parts[0]);
  177. if (!isset($this->form[$name])) {
  178. $s .= Html::el('input', array('type' => 'hidden', 'name' => $name, 'value' => urldecode($parts[1])));
  179. }
  180. }
  181. $s = "\n\t" . $this->getWrapper('hidden container')->setHtml($s);
  182. }
  183. return $el->startTag() . $s;
  184. } else {
  185. return $this->form->getElementPrototype()->startTag();
  186. }
  187. }
  188. /**
  189. * Renders form end.
  190. * @return string
  191. */
  192. public function renderEnd()
  193. {
  194. $s = '';
  195. foreach ($this->form->getControls() as $control) {
  196. if ($control instanceof Nette\Forms\Controls\HiddenField && !$control->getOption('rendered')) {
  197. $s .= (string) $control->getControl();
  198. }
  199. }
  200. if (iterator_count($this->form->getComponents(TRUE, 'Nette\Forms\Controls\TextInput')) < 2) {
  201. $s .= '<!--[if IE]><input type=IEbug disabled style="display:none"><![endif]-->';
  202. }
  203. if ($s) {
  204. $s = $this->getWrapper('hidden container')->setHtml($s) . "\n";
  205. }
  206. return $s . $this->form->getElementPrototype()->endTag() . "\n";
  207. }
  208. /**
  209. * Renders validation errors (per form or per control).
  210. * @param Nette\Forms\IControl
  211. * @return string
  212. */
  213. public function renderErrors(Nette\Forms\IControl $control = NULL)
  214. {
  215. $errors = $control === NULL ? $this->form->getErrors() : $control->getErrors();
  216. if (count($errors)) {
  217. $ul = $this->getWrapper('error container');
  218. $li = $this->getWrapper('error item');
  219. foreach ($errors as $error) {
  220. $item = clone $li;
  221. if ($error instanceof Html) {
  222. $item->add($error);
  223. } else {
  224. $item->setText($error);
  225. }
  226. $ul->add($item);
  227. }
  228. return "\n" . $ul->render(0);
  229. }
  230. }
  231. /**
  232. * Renders form body.
  233. * @return string
  234. */
  235. public function renderBody()
  236. {
  237. $s = $remains = '';
  238. $defaultContainer = $this->getWrapper('group container');
  239. $translator = $this->form->getTranslator();
  240. foreach ($this->form->getGroups() as $group) {
  241. if (!$group->getControls() || !$group->getOption('visual')) {
  242. continue;
  243. }
  244. $container = $group->getOption('container', $defaultContainer);
  245. $container = $container instanceof Html ? clone $container : Html::el($container);
  246. $s .= "\n" . $container->startTag();
  247. $text = $group->getOption('label');
  248. if ($text instanceof Html) {
  249. $s .= $text;
  250. } elseif (is_string($text)) {
  251. if ($translator !== NULL) {
  252. $text = $translator->translate($text);
  253. }
  254. $s .= "\n" . $this->getWrapper('group label')->setText($text) . "\n";
  255. }
  256. $text = $group->getOption('description');
  257. if ($text instanceof Html) {
  258. $s .= $text;
  259. } elseif (is_string($text)) {
  260. if ($translator !== NULL) {
  261. $text = $translator->translate($text);
  262. }
  263. $s .= $this->getWrapper('group description')->setText($text) . "\n";
  264. }
  265. $s .= $this->renderControls($group);
  266. $remains = $container->endTag() . "\n" . $remains;
  267. if (!$group->getOption('embedNext')) {
  268. $s .= $remains;
  269. $remains = '';
  270. }
  271. }
  272. $s .= $remains . $this->renderControls($this->form);
  273. $container = $this->getWrapper('form container');
  274. $container->setHtml($s);
  275. return $container->render(0);
  276. }
  277. /**
  278. * Renders group of controls.
  279. * @param Nette\Forms\Container|FormGroup
  280. * @return string
  281. */
  282. public function renderControls($parent)
  283. {
  284. if (!($parent instanceof Nette\Forms\Container || $parent instanceof Nette\Forms\ControlGroup)) {
  285. throw new Nette\InvalidArgumentException("Argument must be FormContainer or FormGroup instance.");
  286. }
  287. $container = $this->getWrapper('controls container');
  288. $buttons = NULL;
  289. foreach ($parent->getControls() as $control) {
  290. if ($control->getOption('rendered') || $control instanceof Nette\Forms\Controls\HiddenField || $control->getForm(FALSE) !== $this->form) {
  291. // skip
  292. } elseif ($control instanceof Nette\Forms\Controls\Button) {
  293. $buttons[] = $control;
  294. } else {
  295. if ($buttons) {
  296. $container->add($this->renderPairMulti($buttons));
  297. $buttons = NULL;
  298. }
  299. $container->add($this->renderPair($control));
  300. }
  301. }
  302. if ($buttons) {
  303. $container->add($this->renderPairMulti($buttons));
  304. }
  305. $s = '';
  306. if (count($container)) {
  307. $s .= "\n" . $container . "\n";
  308. }
  309. return $s;
  310. }
  311. /**
  312. * Renders single visual row.
  313. * @param Nette\Forms\IControl
  314. * @return string
  315. */
  316. public function renderPair(Nette\Forms\IControl $control)
  317. {
  318. $pair = $this->getWrapper('pair container');
  319. $pair->add($this->renderLabel($control));
  320. $pair->add($this->renderControl($control));
  321. $pair->class($this->getValue($control->isRequired() ? 'pair .required' : 'pair .optional'), TRUE);
  322. $pair->class($control->getOption('class'), TRUE);
  323. if (++$this->counter % 2) {
  324. $pair->class($this->getValue('pair .odd'), TRUE);
  325. }
  326. $pair->id = $control->getOption('id');
  327. return $pair->render(0);
  328. }
  329. /**
  330. * Renders single visual row of multiple controls.
  331. * @param array of IFormControl
  332. * @return string
  333. */
  334. public function renderPairMulti(array $controls)
  335. {
  336. $s = array();
  337. foreach ($controls as $control) {
  338. if (!$control instanceof Nette\Forms\IControl) {
  339. throw new Nette\InvalidArgumentException("Argument must be array of IFormControl instances.");
  340. }
  341. $s[] = (string) $control->getControl();
  342. }
  343. $pair = $this->getWrapper('pair container');
  344. $pair->add($this->renderLabel($control));
  345. $pair->add($this->getWrapper('control container')->setHtml(implode(" ", $s)));
  346. return $pair->render(0);
  347. }
  348. /**
  349. * Renders 'label' part of visual row of controls.
  350. * @param Nette\Forms\IControl
  351. * @return string
  352. */
  353. public function renderLabel(Nette\Forms\IControl $control)
  354. {
  355. $head = $this->getWrapper('label container');
  356. if ($control instanceof Nette\Forms\Controls\Checkbox || $control instanceof Nette\Forms\Controls\Button) {
  357. return $head->setHtml(($head->getName() === 'td' || $head->getName() === 'th') ? '&nbsp;' : '');
  358. } else {
  359. $label = $control->getLabel();
  360. $suffix = $this->getValue('label suffix') . ($control->isRequired() ? $this->getValue('label requiredsuffix') : '');
  361. if ($label instanceof Html) {
  362. $label->setHtml($label->getHtml() . $suffix);
  363. $suffix = '';
  364. }
  365. return $head->setHtml((string) $label . $suffix);
  366. }
  367. }
  368. /**
  369. * Renders 'control' part of visual row of controls.
  370. * @param Nette\Forms\IControl
  371. * @return string
  372. */
  373. public function renderControl(Nette\Forms\IControl $control)
  374. {
  375. $body = $this->getWrapper('control container');
  376. if ($this->counter % 2) {
  377. $body->class($this->getValue('control .odd'), TRUE);
  378. }
  379. $description = $control->getOption('description');
  380. if ($description instanceof Html) {
  381. $description = ' ' . $control->getOption('description');
  382. } elseif (is_string($description)) {
  383. $description = ' ' . $this->getWrapper('control description')->setText($control->translate($description));
  384. } else {
  385. $description = '';
  386. }
  387. if ($control->isRequired()) {
  388. $description = $this->getValue('control requiredsuffix') . $description;
  389. }
  390. if ($this->getValue('control errors')) {
  391. $description .= $this->renderErrors($control);
  392. }
  393. if ($control instanceof Nette\Forms\Controls\Checkbox || $control instanceof Nette\Forms\Controls\Button) {
  394. return $body->setHtml((string) $control->getControl() . (string) $control->getLabel() . $description);
  395. } else {
  396. return $body->setHtml((string) $control->getControl() . $description);
  397. }
  398. }
  399. /**
  400. * @param string
  401. * @return Nette\Utils\Html
  402. */
  403. protected function getWrapper($name)
  404. {
  405. $data = $this->getValue($name);
  406. return $data instanceof Html ? clone $data : Html::el($data);
  407. }
  408. /**
  409. * @param string
  410. * @return string
  411. */
  412. protected function getValue($name)
  413. {
  414. $name = explode(' ', $name);
  415. $data = & $this->wrappers[$name[0]][$name[1]];
  416. return $data;
  417. }
  418. }