'integer', 'numeric' => 'numeric', 'is_natural' => 'natural', 'is_natural_no_zero' => 'positive_int', 'valid_email' => 'email', 'valid_ip' => 'ip', 'valid_base64' => 'base64', 'valid_date' => 'date', 'alpha_dash_dot' => 'alpha_dash_dot', 'alpha_slash_dot' => 'alpha_slash_dot', 'alpha' => 'alpha', 'alpha_numeric' => 'alpha_numeric', 'alpha_dash' => 'alpha_dash', 'required' => 'required' ); function __construct($options = array()) { foreach($options as $k => $v) { $this->{$k} = $v; } $this->CI =& get_instance(); $this->load = $this->CI->load; } // -------------------------------------------------------------------------- /** * Render a single field. Can be used to chain together multiple fields in a column. * * @param object $object The DataMapper Object to use. * @param string $field The field to render. * @param string $type The type of field to render. * @param array $options Various options to modify the output. * @return Rendered String. */ function render_field($object, $field, $type = NULL, $options = NULL) { $value = ''; if(array_key_exists($field, $object->has_one) || array_key_exists($field, $object->has_many)) { // Create a relationship field $one = array_key_exists($field, $object->has_one); // attempt to look up the current value(s) if( ! isset($options['value'])) { if($this->CI->input->post($field)) { $value = $this->CI->input->post($field); } else { // load the related object(s) $sel = $object->{$field}->select('id')->get(); if($one) { // only a single value is allowed $value = $sel->id; } else { // save what might be multiple values $value = array(); foreach($sel as $s) { $value[] = $s->id; } } } } else { // value was already set in the options $value = $options['value']; unset($options['value']); } // Attempt to get a list of possible values if( ! isset($options['list']) || is_object($options['list'])) { if( ! isset($options['list'])) { // look up all of the related values $c = get_class($object->{$field}); $total_items = new $c; // See if the custom method is defined if(method_exists($total_items, 'get_htmlform_list')) { // Get customized list $total_items->get_htmlform_list($object, $field); } else { // Get all items $total_items->get_iterated(); } } else { // process a passed-in DataMapper object $total_items = $options['list']; } $list = array(); foreach($total_items as $item) { // use the __toString value of the item for the label $list[$item->id] = (string)$item; } $options['list'] = $list; } // By if there can be multiple items, use a dropdown for large lists, // and a set of checkboxes for a small one. if($one || count($options['list']) > 6) { $default_type = 'dropdown'; if( ! $one && ! isset($options['size'])) { // limit to no more than 8 items high. $options['size'] = min(count($options['list']), 8); } } else { $default_type = 'checkbox'; } } else { // attempt to look up the current value(s) if( ! isset($options['value'])) { if($this->CI->input->post($field)) { $value = $this->CI->input->post($field); // clear default if set unset($options['default_value']); } else { if(isset($options['default_value'])) { $value = $options['default_value']; unset($options['default_value']); } else { // the field IS the value. $value = $object->{$field}; } } } else { // value was already set in the options $value = $options['value']; unset($options['value']); } // default to text $default_type = ($field == 'id') ? 'hidden' : 'text'; // determine default attributes $a = array(); // such as the size of the field. $max = $this->_get_validation_rule($object, $field, 'max_length'); if($max === FALSE) { $max = $this->_get_validation_rule($object, $field, 'exact_length'); } if($max !== FALSE) { $a['maxlength'] = $max; $a['size'] = min($max, 30); } $list = $this->_get_validation_info($object, $field, 'values', FALSE); if($list !== FALSE) { $a['list'] = $list; } $options = $options + $a; $extra_class = array(); // Add any of the known rules as classes (for JS processing) foreach($this->auto_rule_classes as $rule => $c) { if($this->_get_validation_rule($object, $field, $rule) !== FALSE) { $extra_class[] = $c; } } // add or set the class on the field. if( ! empty($extra_class)) { $extra_class = implode(' ', $extra_class); if(isset($options['class'])) { $options['class'] .= ' ' . $extra_class; } else { $options['class'] = $extra_class; } } } // determine the renderer type $type = $this->_get_type($object, $field, $type); if(empty($type)) { $type = $default_type; } // attempt to find the renderer function if(method_exists($this, '_input_' . $type)) { return $this->{'_input_' . $type}($object, $field, $value, $options); } else if(function_exists('input_' . $type)) { return call_user_func('input_' . $type, $object, $field, $value, $options); } else { log_message('error', 'FormMaker: Unable to find a renderer for '.$type); return 'FormMaker: UNABLE TO FIND A RENDERER FOR '.$type.''; } } // -------------------------------------------------------------------------- /** * Render a row with a single field. If $field does not exist on * $object->validation, then $field is output as-is. * * @param object $object The DataMapper Object to use. * @param string $field The field to render (or content) * @param string $type The type of field to render. * @param array $options Various options to modify the output. * @param string $row_template The template to use, or NULL to use the default. * @return Rendered String. */ function render_row($object, $field, $type = NULL, $options = array(), $row_template = NULL) { // try to determine type automatically $type = $this->_get_type($object, $field, $type); if( ! isset($object->validation[$field]) && (empty($type) || $type == 'section' || $type == 'none')) { // this could be a multiple-field row, or just some text. // if $type is 'section, it will be rendered using the section template. $error = ''; $label = ''; $content = $field; $id = NULL; } else { // use validation information to render the field. $content = $this->render_field($object, $field, $type, $options); if(empty($row_template)) { if($type == 'hidden' || $field == 'id') { $row_template = 'none'; } else { $row_template = $this->row_template; } } // determine if there is an existing error $error = isset($object->error->{$field}) ? $object->error->{$field} : ''; // determine if there is a pre-defined label $label = $this->_get_validation_info($object, $field, 'label', $field); // the field IS the id $id = $field; } $required = $this->_get_validation_rule($object, $field, 'required'); // Append these items. Values in $options have priority $view_data = $options + array( 'object' => $object, 'content' => $content, 'field' => $field, 'label' => $label, 'error' => $error, 'id' => $id, 'required' => $required ); if(is_null($row_template)) { if(empty($type)) { $row_template = 'none'; } else if($type == 'section') { $row_template = $this->section_template; } else { $row_template = $this->row_template; } } if($row_template == 'none') { return $content; } else { return $this->load->view($row_template, $view_data, TRUE); } } // -------------------------------------------------------------------------- /** * Renders an entire form. * * @param object $object The DataMapper Object to use. * @param string $fields An associative array that defines the form. * @param string $template The template to use. * @param string $row_template The template to use for rows. * @param array $template_options The template to use for rows. * @return Rendered String. */ function render_form($object, $fields, $url = '', $options = array(), $template = NULL, $row_template = NULL) { if(empty($url)) { // set url to current url $url =$this->CI->uri->uri_string(); } if(is_null($template)) { $template = $this->form_template; } $rows = ''; foreach($fields as $field => $field_options) { $rows .= $this->_render_row_from_form($object, $field, $field_options, $row_template); } $view_data = $options + array( 'object' => $object, 'fields' => $fields, 'url' => $url, 'rows' => $rows ); return $this->load->view($template, $view_data, TRUE); } // -------------------------------------------------------------------------- // Private Methods // -------------------------------------------------------------------------- // Converts information from render_form into a row of objects. function _render_row_from_form($object, $field, $options, $row_template, $row = TRUE) { if(is_int($field)) { // simple form, or HTML-content $field = $options; $options = NULL; } if(is_null($options)) { // always have an array for options $options = array(); } $type = ''; if( ! is_array($options)) { // if options is a single string, assume it is the type. $type = $options; $options = array(); } if(isset($options['type'])) { // type was set in options $type = $options['type']; unset($options['type']); } // see if a different row_template was in the options $rt = $row_template; if(isset($options['template'])) { $rt = $options['template']; unset($options['template']); } // Multiple fields, render them all as one. if(is_array($field)) { if(isset($field['row_options'])) { $options = $field['row_options']; unset($field['row_options']); } $ret = ''; $sep = ' '; if(isset($field['input_separator'])) { $sep = $field['input_separator']; unset($field['input_separator']); } foreach($field as $f => $fo) { // add each field to a list if( ! empty($ret)) { $ret .= $sep; } $ret .= $this->_render_row_from_form($object, $f, $fo, $row_template, FALSE); } // renders into a row or field below. $field = $ret; } if($row) { // if row is set, render the whole row. return $this->render_row($object, $field, $type, $options, $rt); } else { // render just the field. return $this->render_field($object, $field, $type, $options); } } // -------------------------------------------------------------------------- // Attempts to look up the field's type function _get_type($object, $field, $type) { if(empty($type)) { $type = $this->_get_validation_info($object, $field, 'type', NULL); } return $type; } // -------------------------------------------------------------------------- // Returns a field from the validation array function _get_validation_info($object, $field, $val, $default = '') { if(isset($object->validation[$field][$val])) { return $object->validation[$field][$val]; } return $default; } // -------------------------------------------------------------------------- // Returns the value (or TRUE) of the validation rule, or FALSE if it does not exist. function _get_validation_rule($object, $field, $rule) { $r = $this->_get_validation_info($object, $field, 'rules', FALSE); if($r !== FALSE) { if(isset($r[$rule])) { return $r[$rule]; } else if(in_array($rule, $r, TRUE)) { return TRUE; } } return FALSE; } // -------------------------------------------------------------------------- // Input Types // -------------------------------------------------------------------------- // Render a hidden input function _input_hidden($object, $id, $value, $options) { return $this->_render_simple_input('hidden', $id, $value, $options); } // render a single-line text input function _input_text($object, $id, $value, $options) { return $this->_render_simple_input('text', $id, $value, $options); } // render a password input function _input_password($object, $id, $value, $options) { if(isset($options['send_value'])) { unset($options['send_value']); } else { $value = ''; } return $this->_render_simple_input('password', $id, $value, $options); } // render a multiline text input function _input_textarea($object, $id, $value, $options) { if(isset($options['value'])) { $value = $options['value']; unset($options['value']); } $a = $options + array( 'name' => $id, 'id' => $id ); return $this->_render_node('textarea', $a, htmlspecialchars($value)); } // render a dropdown function _input_dropdown($object, $id, $value, $options) { $list = $options['list']; unset($options['list']); $selected = $value; if(isset($options['value'])) { $selected = $options['value']; unset($options['value']); } if( ! is_array($selected)) { $selected = array($selected); } else { // force multiple $options['multiple'] = 'multiple'; } $l = $this->_options($list, $selected); $name = $id; if(isset($options['multiple'])) { $name .= '[]'; } $a = $options + array( 'name' => $name, 'id' => $id ); return $this->_render_node('select', $a, $l); } // used to render an options list. function _options($list, $sel) { $l = ''; foreach($list as $opt => $label) { if(is_array($label)) { $l .= ''; $l .= $this->_options($label, $sel); $l .= ''; } else { $a = array('value' => $opt); if(in_array($opt, $sel)) { $a['selected'] = 'selected'; } $l .= $this->_render_node('option', $a, htmlspecialchars($label)); } } return $l; } // render a checkbox or series of checkboxes function _input_checkbox($object, $id, $value, $options) { return $this->_checkbox('checkbox', $id, $value, $options); } // render a series of radio buttons function _input_radio($object, $id, $value, $options) { return $this->_checkbox('radio', $id, $value, $options); } // renders one or more checkboxes or radio buttons function _checkbox($type, $id, $value, $options, $sub_id = '', $label = '') { if(isset($options['value'])) { $value = $options['value']; unset($options['value']); } // if there is a list in options, render our multiple checkboxes. if(isset($options['list'])) { $list = $options['list']; unset($options['list']); $ret = ''; if( ! is_array($value)) { if(is_null($value) || $value === FALSE || $value === '') { $value = array(); } else { $value = array($value); } } $sep = '
'; if(isset($options['input_separator'])) { $sep = $options['input_separator']; unset($options['input_separator']); } foreach($list as $k => $v) { if( ! empty($ret)) { // put each node on one line. $ret .= $sep; } $ret .= $this->_checkbox($type, $id, $value, $options, $k, $v); } return $ret; } else { // just render the single checkbox. $node_id = $id; if( ! empty($sub_id)) { // there are multiple nodes with this id, append the sub_id $node_id .= '_' . $sub_id; $field_value = $sub_id; } else { // sub_id is the same as the node's id $sub_id = $id; $field_value = '1'; } $name = $id; if(is_array($value)) { // allow for multiple results $name .= '[]'; } // node attributes $a = $options + array( 'type' => $type, 'id' => $node_id, 'name' => $name, 'value' => $field_value ); // if checked wasn't overridden if( ! isset($a['checked'])) { // determine if this is a multiple checkbox or not. $checked = $value; if(is_array($checked)) { $checked = in_array($sub_id, $value); } if($checked) { $a['checked'] = 'checked'; } } $ret = $this->_render_node('input', $a); if( ! empty($label)) { $ret .= ' ' . $this->_render_node('label', array('for' => $node_id), $label); } return $ret; } } // render a file upload input function _input_file($object, $id, $value, $options) { $a = $options + array( 'type' => 'file', 'name' => $id, 'id' => $id ); return $this->_render_node('input', $a); } // Utility method to render a normal function _render_simple_input($type, $id, $value, $options) { $a = $options + array( 'type' => $type, 'name' => $id, 'id' => $id, 'value' => $value ); return $this->_render_node('input', $a); } // Utility method to render a node. function _render_node($type, $attributes, $content = FALSE) { // generate node $res = '<' . $type; foreach($attributes as $att => $v) { // the special attribute '_' is rendered directly. if($att == '_') { $res .= ' ' . $v; } else { if($att != 'label') { $res .= ' ' . $att . '="' . htmlspecialchars((string)$v) . '"'; } } } // allow for content-containing nodes if($content !== FALSE) { $res .= '>' . $content . ''; } else { $res .= ' />'; } return $res; } } /* End of file htmlform.php */ /* Location: ./application/datamapper/htmlform.php */