2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
   5  * A static renderer for HTML_QuickForm, makes an array of form content
 
   6  * useful for a Smarty template
 
  10  * LICENSE: This source file is subject to version 3.01 of the PHP license
 
  11  * that is available through the world-wide-web at the following URI:
 
  12  * http://www.php.net/license/3_01.txt If you did not receive a copy of
 
  13  * the PHP License and are unable to obtain it through the web, please
 
  14  * send a note to license@php.net so we can mail you a copy immediately.
 
  17  * @package     HTML_QuickForm
 
  18  * @author      Alexey Borzov <avb@php.net>
 
  19  * @author      Bertrand Mansion <bmansion@mamasam.com>
 
  20  * @author      Thomas Schulz <ths@4bconsult.de>
 
  21  * @copyright   2001-2011 The PHP Group
 
  22  * @license     http://www.php.net/license/3_01.txt PHP License 3.01
 
  24  * @link        http://pear.php.net/package/HTML_QuickForm
 
  28  * A concrete renderer for HTML_QuickForm, makes an array of form contents
 
  30 require_once 'HTML/QuickForm/Renderer/Array.php';
 
  33  * A static renderer for HTML_QuickForm, makes an array of form content
 
  34  * useful for a Smarty template
 
  36  * Based on old HTML_QuickForm::toArray() code and ITStatic renderer.
 
  38  * The form array structure is the following:
 
  41  *  [frozen]       => whether the complete form is frozen'
 
  42  *  [javascript]   => javascript for client-side validation
 
  43  *  [attributes]   => attributes for <form> tag
 
  44  *  [hidden]       => html of all hidden elements
 
  45  *  [requirednote] => note about the required elements
 
  48  *          [1st_element_name] => Error for the 1st element
 
  50  *          [nth_element_name] => Error for the nth element
 
  55  *          [1st_header_name] => Header text for the 1st header
 
  57  *          [nth_header_name] => Header text for the nth header
 
  60  *  [1st_element_name] => Array for the 1st element
 
  62  *  [nth_element_name] => Array for the nth element
 
  65  * where an element array has the form:
 
  68  *          [name]      => element name
 
  69  *          [value]     => element value,
 
  70  *          [type]      => type of the element
 
  71  *          [frozen]    => whether element is frozen
 
  72  *          [label]     => label for the element
 
  73  *          [required]  => whether element is required
 
  74  * // if element is not a group:
 
  75  *          [html]      => HTML for the element
 
  76  * // if element is a group:
 
  77  *          [separator] => separator for group elements
 
  78  *          [1st_gitem_name] => Array for the 1st element in group
 
  80  *          [nth_gitem_name] => Array for the nth element in group
 
  86  * @package     HTML_QuickForm
 
  87  * @author      Alexey Borzov <avb@php.net>
 
  88  * @author      Bertrand Mansion <bmansion@mamasam.com>
 
  89  * @author      Thomas Schulz <ths@4bconsult.de>
 
  90  * @version     Release: 3.2.16
 
  93 class HTML_QuickForm_Renderer_ArraySmarty extends HTML_QuickForm_Renderer_Array
 
  99     * The Smarty template engine instance
 
 105     * Current element index
 
 108     var $_elementIdx = 0;
 
 111     * The current element index inside a group
 
 114     var $_groupElementIdx = 0;
 
 117     * How to handle the required tag for required fields
 
 119     * @see      setRequiredTemplate()
 
 124     * How to handle error messages in form validation
 
 126     * @see      setErrorTemplate()
 
 134     * @param  Smarty  reference to the Smarty template engine instance
 
 135     * @param  bool    true: render an array of labels to many labels, $key 0 to 'label' and the oterh to "label_$key"
 
 136     * @param  bool    true: collect all hidden elements into string; false: process them as usual form elements
 
 139     function HTML_QuickForm_Renderer_ArraySmarty(&$tpl, $staticLabels = false, $collectHidden = true)
 
 141         $this->HTML_QuickForm_Renderer_Array($collectHidden, $staticLabels);
 
 146     * Called when visiting a header element
 
 148     * @param    HTML_QuickForm_header   header element being visited
 
 152     function renderHeader(&$header)
 
 154         if ($name = $header->getName()) {
 
 155             $this->_ary['header'][$name] = $header->toHtml();
 
 157             $this->_ary['header'][$this->_sectionCount] = $header->toHtml();
 
 159         $this->_currentSection = $this->_sectionCount++;
 
 160     } // end func renderHeader
 
 163     * Called when visiting a group, before processing any group elements
 
 165     * @param    HTML_QuickForm_group    group being visited
 
 166     * @param    bool                    Whether a group is required
 
 167     * @param    string                  An error message associated with a group
 
 171     function startGroup(&$group, $required, $error)
 
 173         parent::startGroup($group, $required, $error);
 
 174         $this->_groupElementIdx = 1;
 
 175     } // end func startGroup
 
 178     * Creates an array representing an element containing
 
 179     * the key for storing this
 
 182     * @param  HTML_QuickForm_element    form element being visited
 
 183     * @param  bool                      Whether an element is required
 
 184     * @param  string                    Error associated with the element
 
 187     function _elementToArray(&$element, $required, $error)
 
 189         $ret = parent::_elementToArray($element, $required, $error);
 
 191         if ('group' == $ret['type']) {
 
 192             $ret['html'] = $element->toHtml();
 
 193             // we don't need the elements, see the array structure
 
 194             unset($ret['elements']);
 
 196         if (($required || $error) && !empty($this->_required)){
 
 197             $this->_renderRequired($ret['label'], $ret['html'], $required, $error);
 
 199         if ($error && !empty($this->_error)) {
 
 200             $this->_renderError($ret['label'], $ret['html'], $error);
 
 201             $ret['error'] = $error;
 
 203         // create keys for elements grouped by native group or name
 
 204         if (strstr($ret['name'], '[') or $this->_currentGroup) {
 
 205             // Fix for bug #8123: escape backslashes and quotes to prevent errors 
 
 206             // in eval(). The code below seems to handle the case where element
 
 207             // name has unbalanced square brackets. Dunno whether we really
 
 208             // need this after the fix for #8123, but I'm wary of making big
 
 209             // changes to this code.  
 
 210             preg_match('/([^]]*)\\[([^]]*)\\]/', $ret['name'], $matches);
 
 211             if (isset($matches[1])) {
 
 212                 $sKeysSub = substr_replace($ret['name'], '', 0, strlen($matches[1]));
 
 213                 $sKeysSub = str_replace(
 
 214                     array('\\',   '\'',   '['  ,   ']', '[\'\']'),
 
 215                     array('\\\\', '\\\'', '[\'', '\']', '[]'    ),
 
 218                 $sKeys = '[\'' . str_replace(array('\\', '\''), array('\\\\', '\\\''), $matches[1]) . '\']' . $sKeysSub;
 
 220                 $sKeys = '[\'' . str_replace(array('\\', '\''), array('\\\\', '\\\''), $ret['name']) . '\']';
 
 222             // special handling for elements in native groups
 
 223             if ($this->_currentGroup) {
 
 224                 // skip unnamed group items unless radios: no name -> no static access
 
 225                 // identification: have the same key string as the parent group
 
 226                 if ($this->_currentGroup['keys'] == $sKeys and 'radio' != $ret['type']) {
 
 229                 // reduce string of keys by remove leading group keys
 
 230                 if (0 === strpos($sKeys, $this->_currentGroup['keys'])) {
 
 231                     $sKeys = substr_replace($sKeys, '', 0, strlen($this->_currentGroup['keys']));
 
 234         // element without a name
 
 235         } elseif ($ret['name'] == '') {
 
 236             $sKeys = '[\'element_' . $this->_elementIdx . '\']';
 
 239             $sKeys = '[\'' . str_replace(array('\\', '\''), array('\\\\', '\\\''), $ret['name']) . '\']';
 
 241         // for radios: add extra key from value
 
 242         if ('radio' == $ret['type'] and substr($sKeys, -2) != '[]') {
 
 243             $sKeys .= '[\'' . str_replace(array('\\', '\''), array('\\\\', '\\\''), $ret['value']) . '\']';
 
 245         $this->_elementIdx++;
 
 246         $ret['keys'] = $sKeys;
 
 248     } // end func _elementToArray
 
 251     * Stores an array representation of an element in the form array
 
 254     * @param array  Array representation of an element
 
 257     function _storeArray($elAry)
 
 260             $sKeys = $elAry['keys'];
 
 261             unset($elAry['keys']);
 
 262             // where should we put this element...
 
 263             if (is_array($this->_currentGroup) && ('group' != $elAry['type'])) {
 
 264                 $toEval = '$this->_currentGroup' . $sKeys . ' = $elAry;';
 
 266                 $toEval = '$this->_ary' . $sKeys . ' = $elAry;';
 
 274     * Called when an element is required
 
 276     * This method will add the required tag to the element label and/or the element html
 
 277     * such as defined with the method setRequiredTemplate.
 
 279     * @param    string      The element label
 
 280     * @param    string      The element html rendering
 
 281     * @param    boolean     The element required
 
 282     * @param    string      The element error
 
 283     * @see      setRequiredTemplate()
 
 287     function _renderRequired(&$label, &$html, &$required, &$error)
 
 289         $this->_tpl->assign(array(
 
 292             'required' => $required,
 
 295         if (!empty($label) && strpos($this->_required, $this->_tpl->left_delimiter . '$label') !== false) {
 
 296             $label = $this->_tplFetch($this->_required);
 
 298         if (!empty($html) && strpos($this->_required, $this->_tpl->left_delimiter . '$html') !== false) {
 
 299             $html = $this->_tplFetch($this->_required);
 
 301         $this->_tpl->clear_assign(array('label', 'html', 'required'));
 
 302     } // end func _renderRequired
 
 305     * Called when an element has a validation error
 
 307     * This method will add the error message to the element label or the element html
 
 308     * such as defined with the method setErrorTemplate. If the error placeholder is not found
 
 309     * in the template, the error will be displayed in the form error block.
 
 311     * @param    string      The element label
 
 312     * @param    string      The element html rendering
 
 313     * @param    string      The element error
 
 314     * @see      setErrorTemplate()
 
 318     function _renderError(&$label, &$html, &$error)
 
 320         $this->_tpl->assign(array('label' => '', 'html' => '', 'error' => $error));
 
 321         $error = $this->_tplFetch($this->_error);
 
 322         $this->_tpl->assign(array('label' => $label, 'html'  => $html));
 
 324         if (!empty($label) && strpos($this->_error, $this->_tpl->left_delimiter . '$label') !== false) {
 
 325             $label = $this->_tplFetch($this->_error);
 
 326         } elseif (!empty($html) && strpos($this->_error, $this->_tpl->left_delimiter . '$html') !== false) {
 
 327             $html = $this->_tplFetch($this->_error);
 
 329         $this->_tpl->clear_assign(array('label', 'html', 'error'));
 
 330     } // end func _renderError
 
 333     * Process an template sourced in a string with Smarty
 
 335     * Smarty has no core function to render     a template given as a string.
 
 336     * So we use the smarty eval plugin function to do this.
 
 338     * @param    string      The template source
 
 342     function _tplFetch($tplSource)
 
 344         if (!function_exists('smarty_function_eval')) {
 
 345             require SMARTY_DIR . '/plugins/function.eval.php';
 
 347         return smarty_function_eval(array('var' => $tplSource), $this->_tpl);
 
 348     }// end func _tplFetch
 
 351     * Sets the way required elements are rendered
 
 353     * You can use {$label} or {$html} placeholders to let the renderer know where
 
 354     * where the element label or the element html are positionned according to the
 
 355     * required tag. They will be replaced accordingly with the right value.     You
 
 356     * can use the full smarty syntax here, especially a custom modifier for I18N.
 
 358     * {if $required}<span style="color: red;">*</span>{/if}{$label|translate}
 
 359     * will put a red star in front of the label if the element is required and
 
 360     * translate the label.
 
 363     * @param    string      The required element template
 
 367     function setRequiredTemplate($template)
 
 369         $this->_required = $template;
 
 370     } // end func setRequiredTemplate
 
 373     * Sets the way elements with validation errors are rendered
 
 375     * You can use {$label} or {$html} placeholders to let the renderer know where
 
 376     * where the element label or the element html are positionned according to the
 
 377     * error message. They will be replaced accordingly with the right value.
 
 378     * The error message will replace the {$error} placeholder.
 
 380     * {if $error}<span style="color: red;">{$error}</span>{/if}<br />{$html}
 
 381     * will put the error message in red on top of the element html.
 
 383     * If you want all error messages to be output in the main error block, use
 
 384     * the {$form.errors} part of the rendered array that collects all raw error
 
 387     * If you want to place all error messages manually, do not specify {$html}
 
 390     * Groups can have special layouts. With this kind of groups, you have to
 
 391     * place the formated error message manually. In this case, use {$form.group.error}
 
 392     * where you want the formated error message to appear in the form.
 
 394     * @param    string      The element error template
 
 398     function setErrorTemplate($template)
 
 400         $this->_error = $template;
 
 401     } // end func setErrorTemplate