of Parliament Hill Computers
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version SCCS: @(#)Smarty.php 1.12 11/11/11 16:29:11
* @link http://pear.php.net/package/HTML_QuickForm2
*/
/**
* This generates an array, bring in the array renderer and extend it a bit:
*/
require_once 'HTML/QuickForm2/Renderer/Array.php';
/**
* A renderer for HTML_QuickForm2 building an array of form elements
*
* Based on Array renderer from HTML_QuickForm 3.x package
*
* The form array structure is the following:
*
* array(
* 'id' => form's "id" attribute (string),
* 'frozen' => whether the form is frozen (bool),
* 'attributes' => attributes for <form> tag (string),
* // if form contains required elements:
* 'required_note' => note about the required elements (string),
* 'requirednote' => note about the required elements (string),
* NB: no '_' in the middle
* In old_compat this is a span style="font-size:80%;"
* with the '*' also color:#ff0000;
* Not old_compat it is in a div class="reqnote"
* // if 'group_hiddens' option is true:
* 'hidden' => array with html of hidden elements (array) (if old_compat this is a string),
* // if 'group_errors' option is true:
* 'errors' => array(
* '1st element id' => 'Error for the 1st element',
* ...
* 'nth element id' => 'Error for the nth element'
* ),
* 'elements' => array(
* element_1,
* ...
* element_N
* )
* '1st_elements_name' => array for the 1st element,
* ... references into 'elements' above
* 'nth_elements_name' => array for the nth element,
* )
* );
*
* Where element_i is an array of the form's fields.
* The key for each element is the field's name (second argument to addElement())
* With the option key_id the 'id' is used, which by default is generated by QuickForm.
* If an individual field has the data element 'SmartyKey' (fourth argument to addElement()) that is used instead,
* (eg: multiple submit buttons with the same form name, you still need to render them separately).
*
*
* array(
* 'id' => element id (string),
* 'name' => element name (string),
* 'type' => type of the element (string),
* 'frozen' => whether element is frozen (bool),
* // if element has a label:
* 'label' => 'label for the element',
* // note that if 'static_labels' option is true and element's label is an
* // array then there will be several 'label_*' keys corresponding to
* // labels' array keys
* 'required' => whether element is required (bool),
* // if a validation error is present, otherwise this is not set
* 'error' => error associated with the element (string),
* // if some style was associated with an element:
* 'style' => 'some information about element style (e.g. for Smarty)',
*
* // if element is not a Container
* 'value' => element value (mixed),
* 'html' => HTML for the element (string),
*
* 'attribs' => attributes as an array, this includes what is passed to addElement()
* // if element is a Container
* 'attributes' => container attributes (string)
* // only for groups, if separator is set:
* 'separator' => separator for group elements (mixed),
* // Sub elements:
* // fieldset or group: the contained elements
* // In a group if the element has no name, they will be named 'Gp[]' - ie read as an array (named 'Gp' for the purpose of this comment)
* // radio: the possible alternatives keyed by the 'value' that would be returned
* // checkbox: the possible alternatives keyed by the index in the field name. The element names are like food[1] & drink[wine]
* 'elements' => array(
* element_1,
* ...
* element_N
* )
* 'set_value' => the 'value' from the attributes array (string). Types radio & checkbox only.
*
* If the type is 'radio' an element (type = 'radio') is created for each choice that the user has,
* keyed by the 'id' value. An 'element' will be created having an array elements keyed by
* the 'value' attribute.
* The 'id' above will be set to the value in 'name'.
* The 'type' of each element will be 'radio'
* );
*
*
* The following additional options are available:
*
* - 'old_compat' - generate something compatible with an old renderer
* - 'key_id' - the key to elements is the field's 'id' rather than 'name' (See also SmartyKey)
* - 'group_errors' - generate a top level array 'errors' with keys as per 'key_id' that contain error strings of the elements
*
*
* 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 toArray()}
* - {@link setStyleForId()}
*
* @category HTML
* @package HTML_QuickForm2
* @author Alain D D Williams
* @version Release: SCCS: @(#)Smarty.php 1.12 11/11/11 16:29:11
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @link http://pear.php.net/package/HTML_QuickForm2/Renderer/Smarty.php
*/
class HTML_QuickForm2_Renderer_Smarty extends HTML_QuickForm2_Renderer_Array
{
/**
* Constructor, adds new options
*/
protected function __construct()
{
parent::__construct();
$this->options += array(
'old_compat' => false,
'key_id' => false,
'group_errors' => false,
);
}
/**
* Creates an array with fields that are common to all elements
*
* @param HTML_QuickForm2_Node $element Element being rendered
*
* @return array
*/
public function buildCommonFields(HTML_QuickForm2_Node $element)
{
$keyn = $this->options['key_id'] ? 'id' : 'name';
$ary = array(
'id' => $element->getId(),
'frozen' => $element->toggleFrozen(),
'name' => $element->getName(),
);
// Key that we use for putting into arrays so that smarty can extract them.
// Note that the name may be empty.
$key_val = $ary[$keyn];
// User specified a key for the renderer array ?
$data = $element->getData();
if(isset($data['SmartyKey'])) {
$ary['SmartyKey'] = $key_val = $data['SmartyKey'];
}
if($key_val == '') {
$key_val = $ary['id'];
}
if ($labels = $element->getLabel()) {
if (!is_array($labels) || !$this->options['static_labels']) {
$ary['label'] = $labels;
} else {
foreach ($labels as $key => $label) {
$key = is_int($key)? $key + 1 : $key;
if (1 === $key) {
$ary['label'] = $label;
} else {
$ary['label_' . $key] = $label;
}
}
}
}
// Smarty: group_errors under 'name' or 'id' depending on key_id option:
if (($error = $element->getError()) && $this->options['group_errors']) {
$this->array['errors'][$key_val] = $error;
}
if ($error) { // Always generate here - if there is an error
$ary['error'] = $error;
}
if (isset($this->styles[$key_val])) {
$ary['style'] = $this->styles[$key_val];
}
if (!$element instanceof HTML_QuickForm2_Container) {
$ary['html'] = $element->__toString();
} else {
$ary['elements'] = array();
$ary['attributes'] = $element->getAttributes(true);
}
$ary['attribs'] = $element->getAttributes(false);
// Radio buttons record the attribute value
if($element->getType() == 'radio' || $element->getType() == 'checkbox') {
$attribs = $element->getAttributes(false);
if(isset($attribs['value'])) { // Perhaps throw error if not set
$ary['set_value'] = $attribs['value'];
}
}
return $ary;
}
/**
* Called to start
*
* @param HTML_QuickForm2_Node $form
*
* @return void
*/
public function startForm(HTML_QuickForm2_Node $form)
{
if($this->options['old_compat']) {
$this->options['group_hiddens'] = true;
}
parent::startForm($form);
}
/**
* Called at end
*
* @param HTML_QuickForm2_Node $form
*
* @return void
*/
public function finishForm(HTML_QuickForm2_Node $form)
{
parent::finishForm($form);
if ($this->hasRequired) {
// Create element 'requirednote' - note no '_'
if($this->options['old_compat']) {
// Old QuickForm had the requirednote styled & a different name:
$this->array['requirednote'] = preg_replace('|([^<]+)(.*)|',
'$1$2',
$this->options['required_note']);
} else {
$this->array['requirednote'] = ''. $this->options['required_note'] . '
';
}
}
// Create top level elements keyed by form field 'name' or 'id'
if(isset($this->array['elements']['0'])) {
$this->linkToLevelAbove($this->array, $this->array['elements']);
}
// For compat: it is expected that 'hidden' is a string, not an array:
if($this->options['old_compat'] && isset($this->array['hidden']) && is_array($this->array['hidden'])) {
$this->array['hidden'] = join(' ', $this->array['hidden']);
}
}
/**
* Look through the elements (numerically indexed) array, make fields
* members of the level above. This is so that they can be easily accessed by smarty templates.
* If we find a group, recurse down. Used for smarty only.
* Key is 'name' or 'id'.
*
* @param array &$top
* @param array $elements
* @param bool $inGroup
*
* @return void
*/
private function linkToLevelAbove(&$top, $elements, $inGroup = false)
{
$key = $this->options['key_id'] ? 'id' : 'name';
foreach($elements as &$elem) {
$top_key = $elem[$key];
// If in a group, convert something like inGrp[F4grp][F4_1] to F4_1
// Don't do if key_id as the value is a straight id.
if( !$this->options['key_id'] && $inGroup && $top_key != '') {
if(!(preg_match("/\[?([\w_]*)\]?$/i", $top_key, $match))) {
throw new HTML_QuickForm2_InvalidArgumentException(
"linkToLevelAbove can't obtain the name from '$top_key'");
}
// Allow group with empty field names - makes an array:
if($match[1] != '') { // Don't change for something like field[]
$top_key = $match[1];
}
}
// Override key for renderer array ?
if(isset($elem['SmartyKey'])) {
$top_key = $elem['SmartyKey'];
}
// Radio buttons: several elements with the same name, make an array
if(isset($elem['type']) && $elem['type'] == 'radio') {
if( ! isset($top[$top_key])) {
$top[$top_key] = array('id' => $top_key, 'type' => 'radio', 'elements' => array());
}
$top[$top_key][$elem['id']] = &$elem;
// So that it can be easily found put into 'elements' keyed by the value that it would have:
if(isset($elem['set_value'])) {
$top[$top_key]['elements'][$elem['set_value']] = &$elem;
}
} else if(isset($elem['type']) && $elem['type'] == 'checkbox' &&
preg_match('/^([^\[\]]+)\[([^\]]+)\]$/', $elem['name'], $fn)) {
// Need to get another name that is the fieldname without the array index
$name = $fn[1];
$index = $fn[2];
if( ! isset($top[$name])) {
$top[$name] = array('id' => $name, 'type' => 'checkbox', 'elements' => array());
}
$top[$name][$elem['id']] = &$elem;
// So that it can be easily found put into 'elements' keyed by the value that it would have:
if(isset($elem['set_value'])) {
$top[$name]['elements'][$index] = &$elem;
}
} else { // Normal field, just link into the level above.
if( ! isset($top[$top_key])) {
$top[$top_key] = &$elem; // Link into the level above
}
}
// If we have a group link its fields up to this level:
if(isset($elem['elements']['0'])) {
$this->linkToLevelAbove($elem, $elem['elements'], true);
}
// Link errors to the top level:
if(isset($elem['error']) && isset($this->array[$elem['error']])) {
$this->array['errors'][$top_key] = $this->array[$elem['error']];
}
}
}
/**#@-*/
}