, * Bertrand Mansion * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: Default.php 299706 2010-05-24 18:32:37Z avb $ * @link http://pear.php.net/package/HTML_QuickForm2 */ /** * Abstract base class for QuickForm2 renderers */ // require_once 'HTML/QuickForm2/Renderer.php'; /** * Default renderer for QuickForm2 * * Mostly a direct port of Default renderer from QuickForm 3.x package. * * While almost everything in this class is defined as public, its properties * and those methods that are not published (i.e. not in array returned by * exportMethods()) will be available to renderer plugins only. * * The following methods are published: * - {@link reset()} * - {@link setTemplateForClass()} * - {@link setTemplateForId()} * - {@link setErrorTemplate()} * - {@link setElementTemplateForGroupClass()} * - {@link setElementTemplateForGroupId()} * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @version Release: @package_version@ */ class HTML_QuickForm2_Renderer_Default extends HTML_QuickForm2_Renderer { /** * Whether the form contains required elements * @var bool */ public $hasRequired = false; /** * HTML generated for the form * @var array */ public $html = array(array()); /** * HTML for hidden elements if 'group_hiddens' option is on * @var string */ public $hiddenHtml = ''; /** * Array of validation errors if 'group_errors' option is on * @var array */ public $errors = array(); /** * Default templates for elements of the given class * @var array */ public $templatesForClass = array( 'html_quickform2_element_inputhidden' => '
{element}
', 'html_quickform2' => '
{errors}{hidden}{content}
{reqnote}
', 'html_quickform2_container_fieldset' => '{label}{content}', 'special:error' => array( 'prefix' => '

{message}

  • ', 'separator' => '
  • ', 'suffix' => '

{message}

' ), 'html_quickform2_element' => '
{error}
{element}
', 'html_quickform2_container_group' => '
{error}
{content}
' ); /** * Custom templates for elements with the given IDs * @var array */ public $templatesForId = array(); /** * Default templates for elements in groups of the given classes * * Array has the form ('group class' => ('element class' => 'template', ...), ...) * * @var array */ public $elementTemplatesForGroupClass = array( 'html_quickform2_container' => array( 'html_quickform2_element' => '{element}', 'html_quickform2_container_fieldset' => '{label}{content}' ) ); /** * Custom templates for grouped elements in the given group IDs * * Array has the form ('group id' => ('element class' => 'template', ...), ...) * * @var array */ public $elementTemplatesForGroupId = array(); /** * Array containing IDs of the groups being rendered * @var array */ public $groupId = array(); public function exportMethods() { return array( 'reset', 'setTemplateForClass', 'setTemplateForId', 'setErrorTemplate', 'setGroupedTemplateForClass', 'setElementTemplateForGroupClass', 'setElementTemplateForGroupId' ); } /** * Sets template for form elements that are instances of the given class * * When searching for a template to use, renderer will check for templates * set for element's class and its parent classes, until found. Thus a more * specific template will override a more generic one. * * @param string Class name * @param mixed Template to use for elements of that class * @return HTML_QuickForm2_Renderer_Default */ public function setTemplateForClass($className, $template) { $this->templatesForClass[strtolower($className)] = $template; return $this; } /** * Sets template for form element with the given id * * If a template is set for an element via this method, it will be used. * In the other case a generic template set by {@link setTemplateForClass()} * or {@link setGroupedTemplateForClass()} will be used. * * @param string Element's id * @param mixed Template to use for rendering of that element * @return HTML_QuickForm2_Renderer_Default */ public function setTemplateForId($id, $template) { $this->templatesForId[$id] = $template; return $this; } /** * Sets template for rendering validation errors * * This template will be used if 'group_errors' option is set to true. * The template array should contain 'prefix', 'suffix' and 'separator' * keys. * * @param array Template for validation errors * @return HTML_QuickForm2_Renderer_Default */ public function setErrorTemplate(array $template) { return $this->setTemplateForClass('special:error', $template); } /** * Sets grouped elements templates using group class * * Templates set via {@link setTemplateForClass()} will not be used for * grouped form elements. When searching for a template to use, the renderer * will first consider template set for a specific group id, then the * group templates set by group class. * * @param string Group class name * @param string Element class name * @param mixed Template * @return HTML_QuickForm2_Renderer_Default */ public function setElementTemplateForGroupClass($groupClass, $elementClass, $template) { $this->elementTemplatesForGroupClass[strtolower($groupClass)][strtolower($elementClass)] = $template; return $this; } /** * Sets grouped elements templates using group id * * Templates set via {@link setTemplateForClass()} will not be used for * grouped form elements. When searching for a template to use, the renderer * will first consider template set for a specific group id, then the * group templates set by group class. * * @param string Group id * @param string Element class name * @param mixed Template * @return HTML_QuickForm2_Renderer_Default */ public function setElementTemplateForGroupId($groupId, $elementClass, $template) { $this->elementTemplatesForGroupId[$groupId][strtolower($elementClass)] = $template; return $this; } /** * Resets the accumulated data * * This method is called automatically by startForm() method, but should * be called manually before calling other rendering methods separately. * * @return HTML_QuickForm2_Renderer_Default */ public function reset() { $this->html = array(array()); $this->hiddenHtml = ''; $this->errors = array(); $this->hasRequired = false; $this->groupId = array(); return $this; } /** * Returns generated HTML * * @return string */ public function __toString() { return (isset($this->html[0][0])? $this->html[0][0]: '') . $this->hiddenHtml; } /** * Renders a generic element * * @param HTML_QuickForm2_Node Element being rendered */ public function renderElement(HTML_QuickForm2_Node $element) { $elTpl = $this->prepareTemplate($this->findTemplate($element), $element); $this->html[count($this->html) - 1][] = str_replace(array('{element}', '{id}'), array($element, $element->getId()), $elTpl); } /** * Renders a hidden element * * @param HTML_QuickForm2_Node Hidden element being rendered */ public function renderHidden(HTML_QuickForm2_Node $element) { if ($this->options['group_hiddens']) { $this->hiddenHtml .= $element->__toString(); } else { $this->html[count($this->html) - 1][] = str_replace('{element}', $element, $this->findTemplate($element)); } } /** * Starts rendering a generic container, called before processing contained elements * * @param HTML_QuickForm2_Node Container being rendered */ public function startContainer(HTML_QuickForm2_Node $container) { $this->html[] = array(); $this->groupId[] = false; } /** * Finishes rendering a generic container, called after processing contained elements * * @param HTML_QuickForm2_Node Container being rendered */ public function finishContainer(HTML_QuickForm2_Node $container) { array_pop($this->groupId); $cTpl = str_replace( array('{attributes}', '{id}'), array($container->getAttributes(true), $container->getId()), $this->prepareTemplate($this->findTemplate($container, '{content}'), $container) ); $cHtml = array_pop($this->html); $break = HTML_Common2::getOption('linebreak'); $indent = str_repeat(HTML_Common2::getOption('indent'), count($this->html)); $this->html[count($this->html) - 1][] = str_replace( '{content}', $break . $indent . implode($break . $indent, $cHtml), $cTpl ); } /** * Starts rendering a group, called before processing grouped elements * * @param HTML_QuickForm2_Node Group being rendered */ public function startGroup(HTML_QuickForm2_Node $group) { $this->html[] = array(); $this->groupId[] = $group->getId(); } /** * Finishes rendering a group, called after processing grouped elements * * @param HTML_QuickForm2_Node Group being rendered */ public function finishGroup(HTML_QuickForm2_Node $group) { $gTpl = str_replace( array('{attributes}', '{id}'), array($group->getAttributes(true), array_pop($this->groupId)), $this->prepareTemplate($this->findTemplate($group, '{content}'), $group) ); $separator = $group->getSeparator(); $elements = array_pop($this->html); if (!is_array($separator)) { $content = implode((string)$separator, $elements); } else { $content = ''; $cSeparator = count($separator); for ($i = 0, $count = count($elements); $i < $count; $i++) { $content .= (0 == $i? '': $separator[($i - 1) % $cSeparator]) . $elements[$i]; } } $this->html[count($this->html) - 1][] = str_replace('{content}', $content, $gTpl); } /** * Starts rendering a form, called before processing contained elements * * @param HTML_QuickForm2_Node Form being rendered */ public function startForm(HTML_QuickForm2_Node $form) { $this->reset(); } /** * Finishes rendering a form, called after processing contained elements * * @param HTML_QuickForm2_Node Form being rendered */ public function finishForm(HTML_QuickForm2_Node $form) { $formTpl = str_replace( array('{attributes}', '{hidden}', '{errors}'), array($form->getAttributes(true), $this->hiddenHtml, $this->outputGroupedErrors()), $this->findTemplate($form, '{content}') ); $this->hiddenHtml = ''; // required note if (!$this->hasRequired || $form->toggleFrozen() || empty($this->options['required_note'])) { $formTpl = preg_replace('!.*!isU', '', $formTpl); } else { $formTpl = str_replace( array('', '', '{reqnote}'), array('', '', $this->options['required_note']), $formTpl ); } $break = HTML_Common2::getOption('linebreak'); $script = $this->getJavascriptBuilder()->__toString(); $this->html[0] = array((empty($script)? '': $script . $break) . str_replace( '{content}', $break . implode($break, $this->html[0]), $formTpl )); } /** * Creates a error list if 'group_errors' option is true * * @return string HTML with a list of all validation errors */ public function outputGroupedErrors() { if (empty($this->errors)) { return ''; } if (!empty($this->options['errors_prefix'])) { $errorHtml = str_replace(array('', '', '{message}'), array('', '', $this->options['errors_prefix']), $this->templatesForClass['special:error']['prefix']); } else { $errorHtml = preg_replace('!.*!isU', '', $this->templatesForClass['special:error']['prefix']); } $errorHtml .= implode($this->templatesForClass['special:error']['separator'], $this->errors); if (!empty($this->options['errors_suffix'])) { $errorHtml .= str_replace(array('', '', '{message}'), array('', '', $this->options['errors_suffix']), $this->templatesForClass['special:error']['suffix']); } else { $errorHtml .= preg_replace('!.*!isU', '', $this->templatesForClass['special:error']['suffix']); } return $errorHtml; } /** * Finds a proper template for the element * * Templates are scanned in a predefined order. First, if a template was * set for a specific element by id, it is returned, no matter if the * element belongs to a group. If the element does not belong to a group, * we try to match a template using the element class. * But, if the element belongs to a group, templates are first looked up * using the containing group id, then using the containing group class. * When no template is found, the provided default template is returned. * * @param HTML_QuickForm2_Node Element being rendered * @param string Default template to use if not found * @return string Template */ public function findTemplate(HTML_QuickForm2_Node $element, $default = '{element}') { if (!empty($this->templatesForId[$element->getId()])) { return $this->templatesForId[$element->getId()]; } $class = strtolower(get_class($element)); $groupId = end($this->groupId); $elementClasses = array(); do { if (empty($groupId) && !empty($this->templatesForClass[$class])) { return $this->templatesForClass[$class]; } $elementClasses[$class] = true; } while ($class = strtolower(get_parent_class($class))); if (!empty($groupId)) { if (!empty($this->elementTemplatesForGroupId[$groupId])) { while (list($elClass) = each($elementClasses)) { if (!empty($this->elementTemplatesForGroupId[$groupId][$elClass])) { return $this->elementTemplatesForGroupId[$groupId][$elClass]; } } } $group = $element->getContainer(); $grClass = strtolower(get_class($group)); do { if (!empty($this->elementTemplatesForGroupClass[$grClass])) { reset($elementClasses); while (list($elClass) = each($elementClasses)) { if (!empty($this->elementTemplatesForGroupClass[$grClass][$elClass])) { return $this->elementTemplatesForGroupClass[$grClass][$elClass]; } } } } while ($grClass = strtolower(get_parent_class($grClass))); } return $default; } /** * Processes the element's template, adding label(s), required note and error message * * @param string Element template * @param HTML_QuickForm2_Node Element being rendered * @return string Template with some substitutions done */ public function prepareTemplate($elTpl, HTML_QuickForm2_Node $element) { // if element is required $elTpl = $this->markRequired($elTpl, $element->isRequired()); $elTpl = $this->outputError($elTpl, $element->getError()); return $this->outputLabel($elTpl, $element->getLabel()); } /** * Marks element required or removes "required" block * * @param string Element template * @param bool Whether element is required * @return string Template with processed "required" block */ public function markRequired($elTpl, $required) { if ($required) { $this->hasRequired = true; $elTpl = str_replace(array('', ''), array('', ''), $elTpl); } else { $elTpl = preg_replace('!.*!isU', '', $elTpl); } return $elTpl; } /** * Outputs element error, removes empty error blocks * * @param string Element template * @param string Validation error for the element * @return string Template with error substitutions done */ public function outputError($elTpl, $error) { if ($error && !$this->options['group_errors']) { $elTpl = str_replace(array('', '', '{error}'), array('', '', $error), $elTpl); } else { if ($error && $this->options['group_errors']) { $this->errors[] = $error; } $elTpl = preg_replace('!.*!isU', '', $elTpl); } return $elTpl; } /** * Outputs element's label(s), removes empty label blocks * * @param string Element template * @param mixed Element label(s) * @return string Template with label substitutions done */ public function outputLabel($elTpl, $label) { $mainLabel = is_array($label)? array_shift($label): $label; $elTpl = str_replace('{label}', $mainLabel, $elTpl); if (false !== strpos($elTpl, '')) { if ($mainLabel) { $elTpl = str_replace(array('', ''), array('', ''), $elTpl); } else { $elTpl = preg_replace('!.*!isU', '', $elTpl); } } if (is_array($label)) { foreach($label as $key => $text) { $key = is_int($key)? $key + 2: $key; $elTpl = str_replace(array('', '', '{label_' . $key . '}'), array('', '', $text), $elTpl); } } if (strpos($elTpl, '{label_')) { $elTpl = preg_replace('!]+)>.*!isU', '', $elTpl); } return $elTpl; } } ?>