PageRenderTime 50ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 0ms

/system/library/PEAR/HTML/QuickForm2/Renderer/Default.php

https://bitbucket.org/spekkionu/passworddb
PHP | 628 lines | 305 code | 40 blank | 283 comment | 39 complexity | 3b0aecabeadc5e5d61f33ac55965fadb MD5 | raw file
Possible License(s): BSD-2-Clause
  1. <?php
  2. /**
  3. * Default renderer for HTML_QuickForm2
  4. *
  5. * PHP version 5
  6. *
  7. * LICENSE:
  8. *
  9. * Copyright (c) 2006-2012, Alexey Borzov <avb@php.net>,
  10. * Bertrand Mansion <golgote@mamasam.com>
  11. * All rights reserved.
  12. *
  13. * Redistribution and use in source and binary forms, with or without
  14. * modification, are permitted provided that the following conditions
  15. * are met:
  16. *
  17. * * Redistributions of source code must retain the above copyright
  18. * notice, this list of conditions and the following disclaimer.
  19. * * Redistributions in binary form must reproduce the above copyright
  20. * notice, this list of conditions and the following disclaimer in the
  21. * documentation and/or other materials provided with the distribution.
  22. * * The names of the authors may not be used to endorse or promote products
  23. * derived from this software without specific prior written permission.
  24. *
  25. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  26. * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  27. * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  28. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  29. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  30. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  31. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  32. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  33. * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  34. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  35. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  36. *
  37. * @category HTML
  38. * @package HTML_QuickForm2
  39. * @author Alexey Borzov <avb@php.net>
  40. * @author Bertrand Mansion <golgote@mamasam.com>
  41. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  42. * @version SVN: $Id: Default.php 325202 2012-04-15 10:22:17Z avb $
  43. * @link http://pear.php.net/package/HTML_QuickForm2
  44. */
  45. /**
  46. * Abstract base class for QuickForm2 renderers
  47. */
  48. require_once 'HTML/QuickForm2/Renderer.php';
  49. /**
  50. * Default renderer for QuickForm2
  51. *
  52. * Mostly a direct port of Default renderer from QuickForm 3.x package.
  53. *
  54. * While almost everything in this class is defined as public, its properties
  55. * and those methods that are not published (i.e. not in array returned by
  56. * exportMethods()) will be available to renderer plugins only.
  57. *
  58. * The following methods are published:
  59. * - {@link setTemplateForClass()}
  60. * - {@link setTemplateForId()}
  61. * - {@link setErrorTemplate()}
  62. * - {@link setElementTemplateForGroupClass()}
  63. * - {@link setElementTemplateForGroupId()}
  64. *
  65. * @category HTML
  66. * @package HTML_QuickForm2
  67. * @author Alexey Borzov <avb@php.net>
  68. * @author Bertrand Mansion <golgote@mamasam.com>
  69. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  70. * @version Release: 2.0.0
  71. * @link http://pear.php.net/package/HTML_QuickForm2
  72. */
  73. class HTML_QuickForm2_Renderer_Default extends HTML_QuickForm2_Renderer
  74. {
  75. /**
  76. * Whether the form contains required elements
  77. * @var bool
  78. */
  79. public $hasRequired = false;
  80. /**
  81. * HTML generated for the form
  82. * @var array
  83. */
  84. public $html = array(array());
  85. /**
  86. * HTML for hidden elements if 'group_hiddens' option is on
  87. * @var string
  88. */
  89. public $hiddenHtml = '';
  90. /**
  91. * Array of validation errors if 'group_errors' option is on
  92. * @var array
  93. */
  94. public $errors = array();
  95. /**
  96. * Default templates for elements of the given class
  97. * @var array
  98. */
  99. public $templatesForClass = array(
  100. 'html_quickform2_element_inputhidden' => '<div style="display: none;">{element}</div>',
  101. 'html_quickform2' => '<div class="quickform">{errors}<form{attributes}><div>{hidden}{content}</div></form><qf:reqnote><div class="reqnote">{reqnote}</div></qf:reqnote></div>',
  102. 'html_quickform2_container_fieldset' => '<fieldset{attributes}><qf:label><legend id="{id}-legend">{label}</legend></qf:label>{content}</fieldset>',
  103. 'special:error' => array(
  104. 'prefix' => '<div class="errors"><qf:message><p>{message}</p></qf:message><ul><li>',
  105. 'separator' => '</li><li>',
  106. 'suffix' => '</li></ul><qf:message><p>{message}</p></qf:message></div>'
  107. ),
  108. 'html_quickform2_element' => '<div class="row"><p class="label"><qf:required><span class="required">*</span></qf:required><qf:label><label for="{id}">{label}</label></qf:label></p><div class="element<qf:error> error</qf:error>"><qf:error><span class="error">{error}<br /></span></qf:error>{element}</div></div>',
  109. 'html_quickform2_container_group' => '<div class="row {class}"><p class="label"><qf:required><span class="required">*</span></qf:required><qf:label><label>{label}</label></qf:label></p><div class="element group<qf:error> error</qf:error>" id="{id}"><qf:error><span class="error">{error}<br /></span></qf:error>{content}</div></div>',
  110. 'html_quickform2_container_repeat' => '<div class="row repeat" id="{id}"><qf:label><p>{label}</p></qf:label>{content}</div>'
  111. );
  112. /**
  113. * Custom templates for elements with the given IDs
  114. * @var array
  115. */
  116. public $templatesForId = array();
  117. /**
  118. * Default templates for elements in groups of the given classes
  119. *
  120. * Array has the form ('group class' => ('element class' => 'template', ...), ...)
  121. *
  122. * @var array
  123. */
  124. public $elementTemplatesForGroupClass = array(
  125. 'html_quickform2_container' => array(
  126. 'html_quickform2_element' => '{element}',
  127. 'html_quickform2_container_fieldset' => '<fieldset{attributes}><qf:label><legend id="{id}-legend">{label}</legend></qf:label>{content}</fieldset>'
  128. )
  129. );
  130. /**
  131. * Custom templates for grouped elements in the given group IDs
  132. *
  133. * Array has the form ('group id' => ('element class' => 'template', ...), ...)
  134. *
  135. * @var array
  136. */
  137. public $elementTemplatesForGroupId = array();
  138. /**
  139. * Array containing IDs of the groups being rendered
  140. * @var array
  141. */
  142. public $groupId = array();
  143. protected function exportMethods()
  144. {
  145. return array(
  146. 'setTemplateForClass',
  147. 'setTemplateForId',
  148. 'setErrorTemplate',
  149. 'setElementTemplateForGroupClass',
  150. 'setElementTemplateForGroupId'
  151. );
  152. }
  153. /**
  154. * Sets template for form elements that are instances of the given class
  155. *
  156. * When searching for a template to use, renderer will check for templates
  157. * set for element's class and its parent classes, until found. Thus a more
  158. * specific template will override a more generic one.
  159. *
  160. * @param string $className Class name
  161. * @param mixed $template Template to use for elements of that class
  162. *
  163. * @return HTML_QuickForm2_Renderer_Default
  164. */
  165. public function setTemplateForClass($className, $template)
  166. {
  167. $this->templatesForClass[strtolower($className)] = $template;
  168. return $this;
  169. }
  170. /**
  171. * Sets template for form element with the given id
  172. *
  173. * If a template is set for an element via this method, it will be used.
  174. * In the other case a generic template set by {@link setTemplateForClass()}
  175. * or {@link setElementTemplateForGroupClass()} will be used.
  176. *
  177. * @param string $id Element's id
  178. * @param mixed $template Template to use for rendering of that element
  179. *
  180. * @return HTML_QuickForm2_Renderer_Default
  181. */
  182. public function setTemplateForId($id, $template)
  183. {
  184. $this->templatesForId[$id] = $template;
  185. return $this;
  186. }
  187. /**
  188. * Sets template for rendering validation errors
  189. *
  190. * This template will be used if 'group_errors' option is set to true.
  191. * The template array should contain 'prefix', 'suffix' and 'separator'
  192. * keys.
  193. *
  194. * @param array $template Template for validation errors
  195. *
  196. * @return HTML_QuickForm2_Renderer_Default
  197. */
  198. public function setErrorTemplate(array $template)
  199. {
  200. return $this->setTemplateForClass('special:error', $template);
  201. }
  202. /**
  203. * Sets grouped elements templates using group class
  204. *
  205. * Templates set via {@link setTemplateForClass()} will not be used for
  206. * grouped form elements. When searching for a template to use, the renderer
  207. * will first consider template set for a specific group id, then the
  208. * group templates set by group class.
  209. *
  210. * @param string $groupClass Group class name
  211. * @param string $elementClass Element class name
  212. * @param mixed $template Template
  213. *
  214. * @return HTML_QuickForm2_Renderer_Default
  215. */
  216. public function setElementTemplateForGroupClass($groupClass, $elementClass, $template)
  217. {
  218. $this->elementTemplatesForGroupClass[strtolower($groupClass)][strtolower($elementClass)] = $template;
  219. return $this;
  220. }
  221. /**
  222. * Sets grouped elements templates using group id
  223. *
  224. * Templates set via {@link setTemplateForClass()} will not be used for
  225. * grouped form elements. When searching for a template to use, the renderer
  226. * will first consider template set for a specific group id, then the
  227. * group templates set by group class.
  228. *
  229. * @param string $groupId Group id
  230. * @param string $elementClass Element class name
  231. * @param mixed $template Template
  232. *
  233. * @return HTML_QuickForm2_Renderer_Default
  234. */
  235. public function setElementTemplateForGroupId($groupId, $elementClass, $template)
  236. {
  237. $this->elementTemplatesForGroupId[$groupId][strtolower($elementClass)] = $template;
  238. return $this;
  239. }
  240. /**
  241. * Resets the accumulated data
  242. *
  243. * This method is called automatically by startForm() method, but should
  244. * be called manually before calling other rendering methods separately.
  245. *
  246. * @return HTML_QuickForm2_Renderer_Default
  247. */
  248. public function reset()
  249. {
  250. $this->html = array(array());
  251. $this->hiddenHtml = '';
  252. $this->errors = array();
  253. $this->hasRequired = false;
  254. $this->groupId = array();
  255. return $this;
  256. }
  257. /**
  258. * Returns generated HTML
  259. *
  260. * @return string
  261. */
  262. public function __toString()
  263. {
  264. return (isset($this->html[0][0])? $this->html[0][0]: '') .
  265. $this->hiddenHtml;
  266. }
  267. /**
  268. * Renders a generic element
  269. *
  270. * @param HTML_QuickForm2_Node $element Element being rendered
  271. */
  272. public function renderElement(HTML_QuickForm2_Node $element)
  273. {
  274. $elTpl = $this->prepareTemplate($this->findTemplate($element), $element);
  275. $this->html[count($this->html) - 1][] = str_replace(
  276. array('{element}', '{id}'), array($element, $element->getId()), $elTpl
  277. );
  278. }
  279. /**
  280. * Renders a hidden element
  281. *
  282. * @param HTML_QuickForm2_Node $element Hidden element being rendered
  283. */
  284. public function renderHidden(HTML_QuickForm2_Node $element)
  285. {
  286. if ($this->options['group_hiddens']) {
  287. $this->hiddenHtml .= $element->__toString();
  288. } else {
  289. $this->html[count($this->html) - 1][] = str_replace(
  290. '{element}', $element, $this->findTemplate($element)
  291. );
  292. }
  293. }
  294. /**
  295. * Starts rendering a generic container, called before processing contained elements
  296. *
  297. * @param HTML_QuickForm2_Node $container Container being rendered
  298. */
  299. public function startContainer(HTML_QuickForm2_Node $container)
  300. {
  301. $this->html[] = array();
  302. $this->groupId[] = false;
  303. }
  304. /**
  305. * Finishes rendering a generic container, called after processing contained elements
  306. *
  307. * @param HTML_QuickForm2_Node $container Container being rendered
  308. */
  309. public function finishContainer(HTML_QuickForm2_Node $container)
  310. {
  311. array_pop($this->groupId);
  312. $cTpl = str_replace(
  313. array('{attributes}', '{id}'),
  314. array($container->getAttributes(true), $container->getId()),
  315. $this->prepareTemplate($this->findTemplate($container, '{content}'), $container)
  316. );
  317. $cHtml = array_pop($this->html);
  318. $break = HTML_Common2::getOption('linebreak');
  319. $indent = str_repeat(HTML_Common2::getOption('indent'), count($this->html));
  320. $this->html[count($this->html) - 1][] = str_replace(
  321. '{content}', $break . $indent . implode($break . $indent, $cHtml), $cTpl
  322. );
  323. }
  324. /**
  325. * Starts rendering a group, called before processing grouped elements
  326. *
  327. * @param HTML_QuickForm2_Node $group Group being rendered
  328. */
  329. public function startGroup(HTML_QuickForm2_Node $group)
  330. {
  331. $this->html[] = array();
  332. $this->groupId[] = $group->getId();
  333. }
  334. /**
  335. * Finishes rendering a group, called after processing grouped elements
  336. *
  337. * @param HTML_QuickForm2_Node $group Group being rendered
  338. */
  339. public function finishGroup(HTML_QuickForm2_Node $group)
  340. {
  341. $gTpl = str_replace(
  342. array('{attributes}', '{id}', '{class}'),
  343. array($group->getAttributes(true), array_pop($this->groupId),
  344. $group->getAttribute('class')),
  345. $this->prepareTemplate($this->findTemplate($group, '{content}'), $group)
  346. );
  347. $separator = $group->getSeparator();
  348. $elements = array_pop($this->html);
  349. if (!is_array($separator)) {
  350. $content = implode((string)$separator, $elements);
  351. } else {
  352. $content = '';
  353. $cSeparator = count($separator);
  354. for ($i = 0, $count = count($elements); $i < $count; $i++) {
  355. $content .= (0 == $i? '': $separator[($i - 1) % $cSeparator]) .
  356. $elements[$i];
  357. }
  358. }
  359. $this->html[count($this->html) - 1][] = str_replace('{content}', $content, $gTpl);
  360. }
  361. /**
  362. * Starts rendering a form, called before processing contained elements
  363. *
  364. * @param HTML_QuickForm2_Node $form Form being rendered
  365. */
  366. public function startForm(HTML_QuickForm2_Node $form)
  367. {
  368. $this->reset();
  369. }
  370. /**
  371. * Finishes rendering a form, called after processing contained elements
  372. *
  373. * @param HTML_QuickForm2_Node $form Form being rendered
  374. */
  375. public function finishForm(HTML_QuickForm2_Node $form)
  376. {
  377. $formTpl = str_replace(
  378. array('{attributes}', '{hidden}', '{errors}'),
  379. array($form->getAttributes(true), $this->hiddenHtml,
  380. $this->outputGroupedErrors()),
  381. $this->findTemplate($form, '{content}')
  382. );
  383. $this->hiddenHtml = '';
  384. // required note
  385. if (!$this->hasRequired || $form->toggleFrozen()
  386. || empty($this->options['required_note'])
  387. ) {
  388. $formTpl = preg_replace('!<qf:reqnote>.*</qf:reqnote>!isU', '', $formTpl);
  389. } else {
  390. $formTpl = str_replace(
  391. array('<qf:reqnote>', '</qf:reqnote>', '{reqnote}'),
  392. array('', '', $this->options['required_note']),
  393. $formTpl
  394. );
  395. }
  396. $break = HTML_Common2::getOption('linebreak');
  397. $script = $this->getJavascriptBuilder()->getFormJavascript($form->getId());
  398. $this->html[0] = array(
  399. str_replace('{content}', $break . implode($break, $this->html[0]), $formTpl) .
  400. (empty($script)? '': $break . $script)
  401. );
  402. }
  403. /**
  404. * Creates a error list if 'group_errors' option is true
  405. *
  406. * @return string HTML with a list of all validation errors
  407. */
  408. public function outputGroupedErrors()
  409. {
  410. if (empty($this->errors)) {
  411. return '';
  412. }
  413. if (!empty($this->options['errors_prefix'])) {
  414. $errorHtml = str_replace(
  415. array('<qf:message>', '</qf:message>', '{message}'),
  416. array('', '', $this->options['errors_prefix']),
  417. $this->templatesForClass['special:error']['prefix']
  418. );
  419. } else {
  420. $errorHtml = preg_replace(
  421. '!<qf:message>.*</qf:message>!isU', '',
  422. $this->templatesForClass['special:error']['prefix']
  423. );
  424. }
  425. $errorHtml .= implode(
  426. $this->templatesForClass['special:error']['separator'],
  427. $this->errors
  428. );
  429. if (!empty($this->options['errors_suffix'])) {
  430. $errorHtml .= str_replace(
  431. array('<qf:message>', '</qf:message>', '{message}'),
  432. array('', '', $this->options['errors_suffix']),
  433. $this->templatesForClass['special:error']['suffix']
  434. );
  435. } else {
  436. $errorHtml .= preg_replace(
  437. '!<qf:message>.*</qf:message>!isU', '',
  438. $this->templatesForClass['special:error']['suffix']
  439. );
  440. }
  441. return $errorHtml;
  442. }
  443. /**
  444. * Finds a proper template for the element
  445. *
  446. * Templates are scanned in a predefined order. First, if a template was
  447. * set for a specific element by id, it is returned, no matter if the
  448. * element belongs to a group. If the element does not belong to a group,
  449. * we try to match a template using the element class.
  450. * But, if the element belongs to a group, templates are first looked up
  451. * using the containing group id, then using the containing group class.
  452. * When no template is found, the provided default template is returned.
  453. *
  454. * @param HTML_QuickForm2_Node $element Element being rendered
  455. * @param string $default Default template to use if not found
  456. *
  457. * @return string Template
  458. */
  459. public function findTemplate(HTML_QuickForm2_Node $element, $default = '{element}')
  460. {
  461. if (!empty($this->templatesForId[$element->getId()])) {
  462. return $this->templatesForId[$element->getId()];
  463. }
  464. $class = strtolower(get_class($element));
  465. $groupId = end($this->groupId);
  466. $elementClasses = array();
  467. do {
  468. if (empty($groupId) && !empty($this->templatesForClass[$class])) {
  469. return $this->templatesForClass[$class];
  470. }
  471. $elementClasses[$class] = true;
  472. } while ($class = strtolower(get_parent_class($class)));
  473. if (!empty($groupId)) {
  474. if (!empty($this->elementTemplatesForGroupId[$groupId])) {
  475. while (list($elClass) = each($elementClasses)) {
  476. if (!empty($this->elementTemplatesForGroupId[$groupId][$elClass])) {
  477. return $this->elementTemplatesForGroupId[$groupId][$elClass];
  478. }
  479. }
  480. }
  481. $group = $element->getContainer();
  482. $grClass = strtolower(get_class($group));
  483. do {
  484. if (!empty($this->elementTemplatesForGroupClass[$grClass])) {
  485. reset($elementClasses);
  486. while (list($elClass) = each($elementClasses)) {
  487. if (!empty($this->elementTemplatesForGroupClass[$grClass][$elClass])) {
  488. return $this->elementTemplatesForGroupClass[$grClass][$elClass];
  489. }
  490. }
  491. }
  492. } while ($grClass = strtolower(get_parent_class($grClass)));
  493. }
  494. return $default;
  495. }
  496. /**
  497. * Processes the element's template, adding label(s), required note and error message
  498. *
  499. * @param string $elTpl Element template
  500. * @param HTML_QuickForm2_Node $element Element being rendered
  501. *
  502. * @return string Template with some substitutions done
  503. */
  504. public function prepareTemplate($elTpl, HTML_QuickForm2_Node $element)
  505. {
  506. // if element is required
  507. $elTpl = $this->markRequired($elTpl, $element->isRequired());
  508. $elTpl = $this->outputError($elTpl, $element->getError());
  509. return $this->outputLabel($elTpl, $element->getLabel());
  510. }
  511. /**
  512. * Marks element required or removes "required" block
  513. *
  514. * @param string $elTpl Element template
  515. * @param bool $required Whether element is required
  516. *
  517. * @return string Template with processed "required" block
  518. */
  519. public function markRequired($elTpl, $required)
  520. {
  521. if ($required) {
  522. $this->hasRequired = true;
  523. $elTpl = str_replace(
  524. array('<qf:required>', '</qf:required>'), array('', ''), $elTpl
  525. );
  526. } else {
  527. $elTpl = preg_replace('!<qf:required>.*</qf:required>!isU', '', $elTpl);
  528. }
  529. return $elTpl;
  530. }
  531. /**
  532. * Outputs element error, removes empty error blocks
  533. *
  534. * @param string $elTpl Element template
  535. * @param string $error Validation error for the element
  536. *
  537. * @return string Template with error substitutions done
  538. */
  539. public function outputError($elTpl, $error)
  540. {
  541. if ($error && !$this->options['group_errors']) {
  542. $elTpl = str_replace(
  543. array('<qf:error>', '</qf:error>', '{error}'),
  544. array('', '', $error), $elTpl
  545. );
  546. } else {
  547. if ($error && $this->options['group_errors']) {
  548. $this->errors[] = $error;
  549. }
  550. $elTpl = preg_replace('!<qf:error>.*</qf:error>!isU', '', $elTpl);
  551. }
  552. return $elTpl;
  553. }
  554. /**
  555. * Outputs element's label(s), removes empty label blocks
  556. *
  557. * @param string $elTpl Element template
  558. * @param string|array $label Element label(s)
  559. *
  560. * @return string Template with label substitutions done
  561. */
  562. public function outputLabel($elTpl, $label)
  563. {
  564. $mainLabel = is_array($label)? array_shift($label): $label;
  565. $elTpl = str_replace('{label}', $mainLabel, $elTpl);
  566. if (false !== strpos($elTpl, '<qf:label>')) {
  567. if ($mainLabel) {
  568. $elTpl = str_replace(array('<qf:label>', '</qf:label>'), array('', ''), $elTpl);
  569. } else {
  570. $elTpl = preg_replace('!<qf:label>.*</qf:label>!isU', '', $elTpl);
  571. }
  572. }
  573. if (is_array($label)) {
  574. foreach ($label as $key => $text) {
  575. $key = is_int($key)? $key + 2: $key;
  576. $elTpl = str_replace(
  577. array('<qf:label_' . $key . '>', '</qf:label_' . $key . '>', '{label_' . $key . '}'),
  578. array('', '', $text), $elTpl
  579. );
  580. }
  581. }
  582. if (strpos($elTpl, '{label_')) {
  583. $elTpl = preg_replace('!<qf:label_([^>]+)>.*</qf:label_\1>!isU', '', $elTpl);
  584. }
  585. return $elTpl;
  586. }
  587. }
  588. ?>