<?php
/**
* Moc10 Library
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.TXT.
* It is also available through the world-wide-web at this URL:
* http://www.moc10phplibrary.com/LICENSE.TXT
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to hide@address.com so we can send you a copy immediately.
*
* @category Moc10
* @package Moc10_Form
* @author Nick Sagona, III <hide@address.com>
* @copyright Copyright (c) 2009-2011 Moc 10 Media, LLC. (http://www.moc10media.com)
* @license http://www.moc10phplibrary.com/LICENSE.TXT New BSD License
*/
/**
* Moc10_Form
*
* @category Moc10
* @package Moc10_Form
* @author Nick Sagona, III <hide@address.com>
* @copyright Copyright (c) 2009-2011 Moc 10 Media, LLC. (http://www.moc10media.com)
* @license http://www.moc10phplibrary.com/LICENSE.TXT New BSD License
* @version 1.9.7
*/
class Moc10_Form extends Moc10_Dom
{
/**
* Form element node
* @var Moc10_Dom_Child
*/
protected $_form = null;
/**
* Form action
* @var string
*/
protected $_action = null;
/**
* Form method
* @var string
*/
protected $_method = null;
/**
* Form template for HTML formatting.
* @var string
*/
protected $_template = null;
/**
* Form init values for quick setup
* @var array
*/
protected $_initValues = array();
/**
* Constructor
*
* Instantiate the form object
*
* @param string $action
* @param string $method
* @param string $indent
* @return void
*/
public function __construct($action, $method, $indent = null)
{
// Set the form's action and method.
$this->_action = $action;
$this->_method = $method;
// Create the parent DOM element and the form child element.
parent::__construct(null, 'utf-8', null, $indent);
$this->_form = new Moc10_Dom_Child('form', null, null, false, $indent);
$this->_form->setAttributes(array('action' => $this->_action, 'method' => $this->_method));
$this->addChildren($this->_form);
}
/**
* Set the init values of the form object.
*
* @param array $values
* @throws Exception
* @return void
*/
public function setInitValues($values)
{
$lang = new Moc10_Language();
if (!is_array($values)) {
throw new Exception($lang->__('The parameter passed must be an array.'));
} if (isset($values[0]) && !is_array($values[0])) {
throw new Exception($lang->__('The array parameter passed must contain an array.'));
} else {
$this->_initValues = $values;
}
}
/**
* Process the init values of the form object and create the form elements.
*
* @return void
*/
public function processInitValues()
{
if (isset($this->_initValues[0])) {
foreach ($this->_initValues as $field) {
if (is_array($field) && isset($field['type']) && isset($field['name'])) {
$type = $field['type'];
$name = $field['name'];
$value = (isset($field['value'])) ? $field['value'] : null;
$marked = (isset($field['marked'])) ? $field['marked'] : null;
$label = (isset($field['label'])) ? $field['label'] : null;
$required = (isset($field['required'])) ? $field['required'] : null;
$attributes = (isset($field['attributes'])) ? $field['attributes'] : null;
$validators = (isset($field['validators'])) ? $field['validators'] : null;
// Initialize the form element.
switch ($type) {
case 'checkbox':
$elem = new Moc10_Form_Element_Checkbox($name, $value, $marked);
break;
case 'radio':
$elem = new Moc10_Form_Element_Radio($name, $value, $marked);
break;
case 'select':
$elem = new Moc10_Form_Element_Select($name, $value, $marked);
break;
case 'textarea':
$elem = new Moc10_Form_Element_Textarea($name, $value, $marked);
break;
default:
$elem = new Moc10_Form_Element($type, $name, $value, $marked);
}
// Set the label.
if (!is_null($label)) {
$elem->setLabel($label);
}
// Set if required.
if (!is_null($required)) {
$elem->setRequired($required);
}
// Set any attributes.
if (!is_null($attributes)) {
if (is_array($attributes)) {
if ((count($attributes) == 2) && !is_array($attributes[0]) && !is_array($attributes[1])) {
$elem->setAttributes($attributes[0], $attributes[1]);
} else {
foreach ($attributes as $att) {
if (isset($att[0]) && isset($att[1])) {
$elem->setAttributes($att[0], $att[1]);
}
}
}
}
}
// Set any validators.
if (!is_null($validators)) {
if (is_array($validators)) {
foreach ($validators as $val) {
if (is_array($val)) {
$cond = (isset($val[1])) ? $val[1] : true;
$value = (isset($val[2])) ? $val[2] : null;
$msg = (isset($val[3])) ? $val[3] : null;
$elem->addValidator($val[0], $cond, $value, $msg);
} else {
$elem->addValidator($val);
}
}
} else {
$elem->addValidator($validators);
}
}
$this->addElements($elem);
}
}
}
}
/**
* Set $_POST values to $initValues.
*
* @param array $post
* @param boolean $filter
* @return void
*/
public function setPostValues($post, $filter = false)
{
// Set $_POST values into the _initValues property.
if (isset($this->_initValues[0])) {
foreach ($this->_initValues as $key => $field) {
if (isset($field['name']) && isset($post[$field['name']])) {
if (($field['type'] == 'select') || ($field['type'] == 'checkbox') || ($field['type'] == 'radio')) {
$this->_initValues[$key]['marked'] = $post[$field['name']];
} else {
$this->_initValues[$key]['value'] = ($filter) ? (string)Moc10_String::setString($post[$field['name']])->striptags() : $post[$field['name']];
}
}
}
// Else, if elements have already been created, set $_POST values into those elements.
} else {
$elements = $this->_form->getChildren();
if (isset($elements[0])) {
foreach ($elements as $key => $field) {
$attributes = $field->getAttributes();
if (isset($attributes['name']) && isset($post[$attributes['name']])) {
// If a select element.
if ($field instanceof Moc10_Form_Element_Select) {
$elements[$key]->setMarked($post[$attributes['name']]);
$elements[$key]->removeChildren();
foreach ($elements[$key]->value as $k => $v) {
$opt = new Moc10_Dom_Child('option');
$opt->setAttributes('value', $k);
if ($v == $elements[$key]->marked) {
$opt->setAttributes('selected', 'selected');
}
$opt->setValue($v);
$elements[$key]->addChildren($opt);
}
// If a textarea element.
} else if ($field instanceof Moc10_Form_Element_Textarea) {
$val = ($filter) ? (string)Moc10_String::setString($post[$attributes['name']])->striptags() : $post[$attributes['name']];
$elements[$key]->value = $val;
$elements[$key]->setValue($val);
// If an input element.
} else {
$val = ($filter) ? (string)Moc10_String::setString($post[$attributes['name']])->striptags() : $post[$attributes['name']];
$elements[$key]->value = $val;
$elements[$key]->setAttributes('value', $val);
}
// If a checkbox element.
} else if ($field instanceof Moc10_Form_Element_Checkbox) {
$children = $field->getChildren();
$atts = $children[0]->getAttributes();
$name = str_replace('[]', '', $atts['name']);
if (isset($post[$name])) {
$elements[$key]->setMarked($post[$name]);
$elements[$key]->removeChildren();
$i = null;
foreach ($elements[$key]->value as $k => $v) {
$chk = new Moc10_Dom_Child('input');
$chk->setAttributes(array('type' => 'checkbox', 'class' => 'checkBox', 'name' => ($name . '[]'), 'id' => ($name . $i), 'value' => $k));
if (in_array($v, $elements[$key]->marked)) {
$chk->setAttributes('checked', 'checked');
}
$span = new Moc10_Dom_Child('span');
$span->setAttributes('class', 'checkPad');
$span->setValue($v);
$elements[$key]->addChildren(array($chk, $span));
$i++;
}
}
// If a radio element.
} else if ($field instanceof Moc10_Form_Element_Radio) {
$children = $field->getChildren();
$atts = $children[0]->getAttributes();
$name = $atts['name'];
if (isset($post[$name])) {
$elements[$key]->setMarked($post[$name]);
$elements[$key]->removeChildren();
$i = null;
foreach ($elements[$key]->value as $k => $v) {
$rad = new Moc10_Dom_Child('input');
$rad->setAttributes(array('type' => 'radio', 'class' => 'radioBtn', 'name' => $name, 'id' => ($name . $i), 'value' => $k));
if ($v == $elements[$key]->marked) {
$rad->setAttributes('checked', 'checked');
}
$span = new Moc10_Dom_Child('span');
$span->setAttributes('class', 'radioPad');
$span->setValue($v);
$elements[$key]->addChildren(array($rad, $span));
$i++;
}
}
}
}
$this->_form->removeChildren();
$this->_form->addChildren($elements);
}
}
}
/**
* Set a form template for the render method to utilize.
*
* @param string $tmpl
* @return void
*/
public function setTemplate($tmpl)
{
$this->_template = $tmpl;
}
/**
* Get the form template for the render method to utilize.
*
* @return string
*/
public function getTemplate()
{
return $this->_template;
}
/**
* Set an attribute or attributes for the form object.
*
* @param array|string $a
* @param string $v
* @return void
*/
public function setAttributes($a, $v = null)
{
$this->_form->setAttributes($a, $v);
}
/**
* Get the attributes of the form object.
*
* @return array
*/
public function getAttributes()
{
return $this->_form->getAttributes();
}
/**
* Add a form element or elements to the form object.
*
* @param array|string $e
* @return void
*/
public function addElements($e)
{
$this->_form->addChildren($e);
}
/**
* Get the elements of the form object.
*
* @return array
*/
public function getElements()
{
return $this->_form->getChildren();
}
/**
* Determine whether or not the form object is valid and return the result.
*
* @return boolean
*/
public function isValid()
{
$noerrors = true;
$children = $this->_form->getChildren();
// Check each element for validators, validate them and return the result.
foreach ($children as $child) {
if ($child->validate() == false) {
$noerrors = false;
}
}
return $noerrors;
}
/**
* Render the form object using the defined template. The template should use a simple search and replace
* format that contains [{element}] and/or [{element_label}] for the placeholders that will be swapped out.
* Required fields' labels have class="required" and error messages have class="error" for styling purposes.
*
* @param boolean $ret
* @throws Exception
* @return void
*/
public function render($ret = false)
{
// Check to make sure form elements exist.
if (count($this->_form->getChildren()) == 0) {
$lang = new Moc10_Language();
throw new Exception($lang->__('Error: There are no form elements declared for this form object.'));
// Else, if the template is not set, default to the basic output.
} else if (is_null($this->_template)) {
if ($ret) {
return (string)$this;
} else {
print($this);
}
// Else, start building the form's HTML output based on the template.
} else {
// Initialize properties and variabels.
$this->_output = '';
$children = $this->_form->getChildren();
// Loop through the child elements of the form.
foreach ($children as $child) {
// Get the element name.
if ($child->getName() == 'fieldset') {
$chdrn = $child->getChildren();
$attribs = $chdrn[0]->getAttributes();
} else {
$attribs = $child->getAttributes();
}
$name = (isset($attribs['name'])) ? $attribs['name'] : '';
$name = str_replace('[]', '', $name);
// Set the element's label, if applicable.
if (!is_null($child->label)) {
// Format the label name.
$label = new Moc10_Dom_Child('label', $child->label);
if ($child->required) {
$label->setAttributes(array('for' => $name, 'class' => 'required'));
} else {
$label->setAttributes('for', $name);
}
// Swap the element's label placeholder with the rendered label element.
$labelSearch = '[{' . $name . '_label}]';
$labelReplace = $label->render(true);
$this->_template = str_replace($labelSearch, substr($labelReplace, 0, -1), $this->_template);
}
// Calculate the element's indentation.
$indent = '';
$indent = substr($this->_template, 0, strpos($this->_template, ('[{' . $name . '}]')));
$indent = substr($indent, (strrpos($indent, "\n") + 1));
$matches = array();
preg_match_all('/[^\s]/', $indent, $matches);
if (isset($matches[0])) {
foreach ($matches[0] as $str) {
$indent = str_replace($str, ' ', $indent);
}
}
// Set each child element's indentation.
$childChildren = $child->getChildren();
$child->removeChildren();
foreach ($childChildren as $cChild) {
$cChild->setIndent(($indent . ' '));
$child->addChildren($cChild);
}
// Swap the element's placeholder with the rendered element.
$elementSearch = '[{' . $name . '}]';
$elementReplace = $child->render(true, 0, $indent);
$elementReplace = substr($elementReplace, 0, -1);
$elementReplace = str_replace('</select>', $indent . '</select>', $elementReplace);
$elementReplace = str_replace('</fieldset>', $indent . '</fieldset>', $elementReplace);
$this->_template = str_replace($elementSearch, $elementReplace, $this->_template);
}
// Set the rendered form content and remove the children.
$this->_form->setValue("\n" . $this->_template . "\n" . $this->_form->getIndent());
$this->_form->removeChildren();
// Return or print the form output.
if ($ret) {
return $this->_form->render(true);
} else {
print($this->_form->render(true));
}
}
}
/**
* Output the form object in a basic HTML format. Each form element is formatted to a 1:1 label to element
* ratio, using <dl>, <dt> and <dd> tags. Required fields' labels have class="required" and error messages
* have class="error" for styling purposes.
*
* @return string
*/
public function __toString()
{
// Initialize propeties.
$this->_output = '';
$children = $this->_form->getChildren();
$this->_form->removeChildren();
// Create DL element.
$dl = new Moc10_Dom_Child('dl', null, null, false, ($this->_form->getIndent() . ' '));
// Loop through the children and create and attach the appropriate DT and DT elements, with labels where applicable.
foreach ($children as $child) {
// If the element label is set, render the appropriate DT and DD elements.
if (!is_null($child->label)) {
// Create the DT and DD elements.
$dt = new Moc10_Dom_Child('dt', null, null, false, ($this->_form->getIndent() . ' '));
$dd = new Moc10_Dom_Child('dd', null, null, false, ($this->_form->getIndent() . ' '));
// Format the label name.
$lbl_name = ($child->getName() == 'fieldset') ? '1' : '';
$label = new Moc10_Dom_Child('label', $child->label, null, false, ($this->_form->getIndent() . ' '));
if ($child->getName() == 'fieldset') {
$chdrn = $child->getChildren();
$attribs = $chdrn[0]->getAttributes();
} else {
$attribs = $child->getAttributes();
}
$name = (isset($attribs['name'])) ? $attribs['name'] : '';
$name = str_replace('[]', '', $name);
if ($child->required) {
$label->setAttributes(array('for' => ($name . $lbl_name), 'class' => 'required'));
} else {
$label->setAttributes('for', ($name . $lbl_name));
}
// Add the appropriate children to the appropriate elements.
$dt->addChildren($label);
$child->setIndent(($this->_form->getIndent() . ' '));
$childChildren = $child->getChildren();
$child->removeChildren();
foreach ($childChildren as $cChild) {
$cChild->setIndent(($this->_form->getIndent() . ' '));
$child->addChildren($cChild);
}
$dd->addChildren($child);
$dl->addChildren(array($dt, $dd));
// Else, render only a DD element.
} else {
$dd = new Moc10_Dom_Child('dd', null, null, false, ($this->_form->getIndent() . ' '));
$child->setIndent(($this->_form->getIndent() . ' '));
$dd->addChildren($child);
$dl->addChildren($dd);
}
}
// Add the DL element and its children to the form element.
$this->_form->addChildren($dl);
$this->_output = $this->_form->render(true);
// Print the output.
return $this->_output;
}
}