PageRenderTime 76ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/fuel/modules/fuel/libraries/Form_builder.php

http://github.com/daylightstudio/FUEL-CMS
PHP | 4334 lines | 2895 code | 488 blank | 951 comment | 460 complexity | 7bde994c3a244e1146b6b31d7d67cea5 MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception
  1. <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
  2. /**
  3. * FUEL CMS
  4. * http://www.getfuelcms.com
  5. *
  6. * An open source Content Management System based on the
  7. * Codeigniter framework (http://codeigniter.com)
  8. *
  9. * @package FUEL CMS
  10. * @author David McReynolds @ Daylight Studio
  11. * @copyright Copyright (c) 2018, Daylight Studio LLC.
  12. * @license http://docs.getfuelcms.com/general/license
  13. * @link http://www.getfuelcms.com
  14. */
  15. // ------------------------------------------------------------------------
  16. /**
  17. * A form creation class
  18. *
  19. * The Form_builder class allows you to create forms by passing in configurable
  20. * array values. Each field has a base set of parameters that can be set
  21. * for it. Other fields have additional parameters you can pass to it
  22. * (e.g. the date field). This class works with the
  23. * <a href="[user_guide_url]libraries/my_model#func_form_fields">MY_Model form_fields</a>
  24. * method which returns table meta information regarding the fields of a
  25. * table.
  26. *
  27. * The <a href="[user_guide_url]libraries/form">Form.php</a> class is required if a
  28. * form object is not passed in the initialization process.
  29. *
  30. * Custom form fields can be configured in the <span class="file">fuel/application/config/custom_fields.php</span> file.
  31. *
  32. * <p class="important">Additional information about <a href="[user_guide_url]general/forms">creating forms using Form_builder can be found in the General Topics area</a>.
  33. *
  34. * @package FUEL CMS
  35. * @subpackage Libraries
  36. * @category Libraries
  37. * @author David McReynolds @ Daylight Studio
  38. * @link http://docs.getfuelcms.com/libraries/form_builder
  39. */
  40. class Form_builder {
  41. public $form; // form object used to create the form fields and associate errors with
  42. public $id = ''; // id to be used for the containing table or div
  43. public $css_class = 'form'; // css class to be used with the form
  44. public $form_attrs = 'method="post" action=""'; // form tag attributes
  45. public $label_colons = FALSE; // add colons to form labels?
  46. public $textarea_rows = 10; // number of rows for a textarea
  47. public $textarea_cols = 60; // number of columns for a textarea
  48. public $text_size_limit = 40; // text size for a text input
  49. public $submit_name = ''; // submit id and name values
  50. public $submit_value = 'Submit'; // submit value (what the button says)
  51. public $cancel_value = ''; // cancel value (what the button says)
  52. public $cancel_action = ''; // what the cancel button does
  53. public $reset_value = ''; // reset button value (what the button says)
  54. public $other_actions = ''; // additional actions to be displayed at the bottom of the form
  55. public $use_form_tag = TRUE; // include the form opening/closing tags in rendered output
  56. public $exclude = array(); // exclude these fields from the form
  57. public $hidden = array('id'); // hidden fields
  58. public $readonly = array(); // readonly fields
  59. public $disabled = array(); // disabled fields
  60. public $displayonly = array(); // for display purposes only
  61. public $date_format = 'm/d/Y'; // date format for date type fields
  62. public $section_tag = 'h3'; // section html tag
  63. public $copy_tag = 'p'; // copy html tag
  64. public $fieldset = ''; // field set name
  65. public $name_array = ''; // put the form fields into an array for namespacing
  66. public $name_prefix = ''; // prefix the form fields as an alternatie to an array for namespacing
  67. public $class_type_prefix = 'field_type_'; // the CSS class prefix to associate with each field type
  68. public $names_id_match = TRUE; // determines if the names and ids match if using a name_prefix or name_array
  69. public $key_check = ''; // the keycheck value used for forms that create session unique session variables to prevent spamming
  70. public $key_check_name = ''; // the keycheck form name used for forms that create session unique session variables to prevent spamming
  71. public $tooltip_format = '<span title="{?}" class="tooltip">[?]</span>'; // tooltip formatting string
  72. public $tooltip_labels = TRUE; // use tooltip labels?
  73. public $single_select_mode = 'auto'; // auto will use enum if 2 or less and a single select if greater than 2. Other values are enum or select
  74. public $multi_select_mode = 'auto'; // auto will use a series of checkboxes if 5 or less and a multiple select if greater than 5. Other values are multi or checkbox
  75. public $boolean_mode = 'checkbox'; // boolean mode can be checkbox or enum (which will display radio inputs)
  76. public $display_errors_func = 'display_errors'; // the function used to generate errors... usually display_errors is the name
  77. public $display_errors = FALSE; // displays errors at the top of the form if TRUE
  78. public $question_keys = array('how', 'do', 'when', 'what', 'why', 'where', 'how', 'is', 'which', 'did', 'any','would', 'should', 'could'); // adds question marks to the label if has these words in the label
  79. public $show_required = TRUE; // show the required fields text at the bottom of the form
  80. public $required_indicator = '*'; // indicator for a required field
  81. public $required_text = '<span class="required">{required_indicator}</span> required fields'; // the required field text
  82. public $label_layout = 'left'; // label layout... can be left or top
  83. public $has_required = FALSE; // does the form have required fields
  84. public $render_format = 'table'; // default render format
  85. public $row_id_prefix = ''; // the row id prefix
  86. public $lang_prefix = 'form_label_'; // language prefix to be applied before a label
  87. public $custom_fields = array(); // custom fields
  88. public $auto_execute_js = TRUE; // automatically execute the javascript for the form
  89. public $html_prepend = ''; // prepended HTML to the form HINT: Can include JS script tags
  90. public $html_append = ''; // appended HTML to the form HINT: Can include JS script tags
  91. public $representatives = array(); // an array of fields that have arrays or regular expression values to match against different field types (e.g. 'number'=>'bigint|smallint|tinyint|int')
  92. public $js; // javascript files to associate with the form fields to be executed once per render
  93. public $css; // CSS files to associate with the form fields to be executed once per render
  94. public $no_css_js = FALSE; // used to not display the CSS and JS when rendering to prevent issues with nested forms and post_processing
  95. public $template = ''; // the html template view file to use for rendering the form when using "render_template"
  96. public $is_pre_processing = FALSE; // flag set when form builder is pre processing fields
  97. public $is_post_processing = FALSE; // flag set when form builder is post processing fields
  98. protected $_html; // html string
  99. protected $_fields; // fields to be used for the form
  100. protected $_values; // an array of the field values
  101. protected $_cached; // cached parameters
  102. protected $_pre_process; // pre_process functions
  103. protected $_post_process; // post_process functions
  104. protected $_rendering = FALSE; // used to prevent infinite loops when calling form_builder reference from within a custom form field
  105. protected $_rendered_field_types = array(); // holds all the fields types rendered
  106. protected $_is_nested = FALSE; // used to detect nested fields
  107. protected $CI;
  108. // --------------------------------------------------------------------
  109. /**
  110. * Constructor
  111. *
  112. * Accepts an associative array as input, containing preferences (optional)
  113. *
  114. * @access public
  115. * @param array config preferences
  116. * @return void
  117. */
  118. public function __construct($params = array())
  119. {
  120. $this->CI =& get_instance();
  121. $this->initialize($params);
  122. }
  123. // --------------------------------------------------------------------
  124. /**
  125. * Initialize preferences
  126. *
  127. * @access public
  128. * @param array
  129. * @return void
  130. */
  131. public function initialize($params = array())
  132. {
  133. // clear out any data before initializing
  134. $this->reset();
  135. $this->set_params($params);
  136. // setup custom fields
  137. if (!empty($this->custom_fields))
  138. {
  139. $this->load_custom_fields($this->custom_fields);
  140. }
  141. // create form object if not in initialization params
  142. if (is_null($this->form))
  143. {
  144. $this->CI->load->library('form');
  145. $this->CI->load->library('encryption');
  146. $this->form = new Form();
  147. // load localization helper if not already
  148. if (!function_exists('lang'))
  149. {
  150. $this->CI->load->helper('language');
  151. }
  152. // CSRF protections
  153. if ($this->CI->config->item('csrf_protection') === TRUE AND empty($this->key_check))
  154. {
  155. $this->CI->security->csrf_set_cookie(); // need to set it again here just to be sure ... on initial page loads this may not be there
  156. $this->key_check = $this->CI->security->get_csrf_hash();
  157. $this->key_check_name = $this->CI->security->get_csrf_token_name();
  158. }
  159. }
  160. }
  161. // --------------------------------------------------------------------
  162. /**
  163. * Set object parameters
  164. *
  165. * @access public
  166. * @param array
  167. * @return void
  168. */
  169. public function set_params($params)
  170. {
  171. if (is_array($params) AND count($params) > 0)
  172. {
  173. foreach ($params as $key => $val)
  174. {
  175. if (isset($this->$key))
  176. {
  177. $method = 'set_'.$key;
  178. if (method_exists($this, $method))
  179. {
  180. $this->$method($val);
  181. }
  182. else
  183. {
  184. $this->$key = $val;
  185. }
  186. }
  187. }
  188. }
  189. }
  190. // --------------------------------------------------------------------
  191. /**
  192. * Same as reset
  193. *
  194. * @access public
  195. * @return void
  196. */
  197. public function clear()
  198. {
  199. $this->reset();
  200. }
  201. // --------------------------------------------------------------------
  202. /**
  203. * Clear class values
  204. *
  205. * @access public
  206. * @return void
  207. */
  208. public function reset()
  209. {
  210. $this->_fields = array();
  211. $this->_values = array();
  212. $this->_html = '';
  213. $this->js = array();
  214. $this->css = array();
  215. $this->_pre_process = array();
  216. $this->_post_process = array();
  217. $this->key_check = NULL;
  218. $this->key_check_name = NULL;
  219. }
  220. // --------------------------------------------------------------------
  221. /**
  222. * Set the fields for the form
  223. *
  224. * Check the normalize_params method for possible values
  225. *
  226. * @access public
  227. * @param array
  228. * @return void
  229. */
  230. public function set_fields($fields)
  231. {
  232. $i = 1;
  233. // clear it out first
  234. $this->_fields = array();
  235. foreach ($fields as $key => $val)
  236. {
  237. $this->add_field($key, $val, $i);
  238. $i++;
  239. }
  240. }
  241. // --------------------------------------------------------------------
  242. /**
  243. * Set the fields for the form
  244. *
  245. * Check the normalize_params method for possible values
  246. *
  247. * @access public
  248. * @param string The key to associate with the field
  249. * @param array The field parameters
  250. * @param int The order value of the parameter
  251. * @return void
  252. */
  253. public function add_field($key, $val, $order = NULL)
  254. {
  255. // __FORM_BUILDER__ allows you to set properties on the class
  256. // convenient for models setting values
  257. if (strtoupper($key) == '__FORM_BUILDER__')
  258. {
  259. $this->set_params($val);
  260. }
  261. else
  262. {
  263. if (is_string($val))
  264. {
  265. $this->_fields[$key] = array('name' => $key, 'value' => $val);
  266. }
  267. else
  268. {
  269. $this->_fields[$key] = $val;
  270. }
  271. // set the key value
  272. if (empty($this->_fields[$key]['key']))
  273. {
  274. $this->_fields[$key]['key'] = $key;
  275. }
  276. if (empty($val['name']))
  277. {
  278. $this->_fields[$key]['name'] = $key;
  279. }
  280. // set the order of the field
  281. if (isset($order) AND empty($val['order']))
  282. {
  283. $this->_fields[$key]['order'] = $order;
  284. }
  285. if (empty($this->_fields[$key]['order']))
  286. {
  287. $this->_fields[$key]['order'] = count($this->_fields);
  288. }
  289. }
  290. }
  291. // --------------------------------------------------------------------
  292. /**
  293. * Removes a field before rendering
  294. *
  295. * @access public
  296. * @param string
  297. * @return void
  298. */
  299. public function remove_field($key)
  300. {
  301. if (is_array($key))
  302. {
  303. foreach($key as $k)
  304. {
  305. $this->remove_field($k);
  306. }
  307. }
  308. else
  309. {
  310. if (isset($this->_fields[$key]))
  311. {
  312. unset($this->_fields[$key]);
  313. }
  314. }
  315. }
  316. // --------------------------------------------------------------------
  317. /**
  318. * Returns the fields for the form. If a key value is passed, it will only return that one field
  319. *
  320. * @access public
  321. * @param string field key
  322. * @return array
  323. */
  324. public function fields($key = NULL)
  325. {
  326. if (!empty($key))
  327. {
  328. if (isset($this->_fields[$key]))
  329. {
  330. return $this->_fields[$key];
  331. }
  332. return FALSE;
  333. }
  334. return $this->_fields;
  335. }
  336. // --------------------------------------------------------------------
  337. /**
  338. * Returns the values of the form fields. If a key value is passed, it will only return that one value
  339. *
  340. * @access public
  341. * @param string field key
  342. * @return array
  343. */
  344. public function values($key = NULL)
  345. {
  346. if (!empty($key))
  347. {
  348. if (isset($this->_values[$key]))
  349. {
  350. return $this->_values[$key];
  351. }
  352. return FALSE;
  353. }
  354. return $this->_values;
  355. }
  356. // --------------------------------------------------------------------
  357. /**
  358. * Sets the value attribute for the fields of the form
  359. *
  360. * Often times this is database or post data
  361. *
  362. * @access public
  363. * @param array
  364. * @return void
  365. */
  366. public function set_field_values($values)
  367. {
  368. if (!is_array($values))
  369. {
  370. return FALSE;
  371. }
  372. // set the values in a different array to keep track of them separate from the form field...
  373. // this is in case the form field gets removed
  374. $this->_values = $values;
  375. // set values for fields that are arrays
  376. foreach($values as $key => $val)
  377. {
  378. if (is_array($values[$key]))
  379. {
  380. foreach($values[$key] as $k => $v)
  381. {
  382. $values[$key.'['.$k.']'] = $v;
  383. }
  384. }
  385. }
  386. if (!empty($this->_fields))
  387. {
  388. foreach($this->_fields as $key => $val)
  389. {
  390. if (isset($values[$key]))
  391. {
  392. if (empty($val['type']))
  393. {
  394. $is_checkbox = FALSE;
  395. }
  396. // don't set the values of these form types'
  397. else if ($val['type'] == 'submit' OR $val['type'] == 'button')
  398. {
  399. continue;
  400. }
  401. else
  402. {
  403. $is_checkbox = (($val['type'] == 'checkbox') OR ($val['type'] == 'boolean' AND $this->boolean_mode == 'checkbox'));
  404. }
  405. if (!$is_checkbox)
  406. {
  407. $this->_fields[$key]['value'] = $values[$key];
  408. }
  409. if (!empty($val['type']))
  410. {
  411. if ($is_checkbox)
  412. {
  413. $this->_fields[$key]['checked'] = ((isset($this->_fields[$key]['value']) AND $values[$key] == $this->_fields[$key]['value']) OR
  414. $values[$key] === TRUE OR
  415. $values[$key] === 1 OR
  416. $values[$key] === 'y' OR
  417. $values[$key] === 'yes') ? TRUE : FALSE;
  418. }
  419. }
  420. }
  421. }
  422. }
  423. }
  424. // --------------------------------------------------------------------
  425. /**
  426. * Render the HTML output
  427. *
  428. * @access public
  429. * @param array fields values... will overwrite anything done with the set_fields method previously
  430. * @param string 'divs or table
  431. * @param string 'a view path (only used for the template)
  432. * @return string
  433. */
  434. public function render($fields = NULL, $render_format = NULL, $template = NULL)
  435. {
  436. if (empty($render_format)) $render_format = $this->render_format;
  437. if ($render_format == 'divs')
  438. {
  439. return $this->render_divs($fields);
  440. }
  441. else if ($render_format == 'template')
  442. {
  443. if (empty($template))
  444. {
  445. $template = $this->template;
  446. }
  447. return $this->render_template($template, $fields);
  448. }
  449. else
  450. {
  451. return $this->render_table($fields);
  452. }
  453. }
  454. // --------------------------------------------------------------------
  455. /**
  456. * Render the HTML output
  457. *
  458. * @access public
  459. * @param array fields values... will overwrite anything done with the set_fields method previously
  460. * @return string
  461. */
  462. public function render_divs($fields = NULL)
  463. {
  464. if (!empty($fields)) $this->set_fields($fields);
  465. // reorder
  466. $this->set_field_order();
  467. // pre process field values
  468. $this->pre_process_field_values();
  469. $this->_html = $this->html_prepend;
  470. $str = '';
  471. $begin_str = '';
  472. $end_str = '';
  473. if ($this->display_errors)
  474. {
  475. $func = $this->display_errors_func;
  476. if (function_exists($func))
  477. {
  478. $str .= $func();
  479. }
  480. }
  481. $colspan = ($this->label_layout == 'top') ? '1' : '2';
  482. $first = $this->_find_first_renderable_field();;
  483. $is_fieldset_first = FALSE;
  484. if ($first['type'] != 'fieldset')
  485. {
  486. $str .= $this->_open_div();
  487. }
  488. else
  489. {
  490. $is_fieldset_first = TRUE;
  491. }
  492. $fieldset_on = FALSE;
  493. foreach($this->_fields as $key => $val)
  494. {
  495. $val = $this->normalize_params($val);
  496. if ($val['type'] == 'fieldset' OR !empty($val['fieldset']))
  497. {
  498. // don't close the table if it isn't opened earlier
  499. if ($is_fieldset_first == FALSE)
  500. {
  501. $str .= $this->_close_div();
  502. }
  503. $is_fieldset_first = FALSE;
  504. // close any existing field sets
  505. if ($fieldset_on)
  506. {
  507. $fieldset_val['open'] = FALSE;
  508. $str .= $this->create_fieldset($fieldset_val);
  509. }
  510. $fieldset_val['open'] = TRUE;
  511. if (!empty($val['fieldset']))
  512. {
  513. $fieldset_val['value'] = $val['fieldset'];
  514. }
  515. else
  516. {
  517. $fieldset_val = $val;
  518. }
  519. $str .= $this->create_fieldset($fieldset_val);
  520. $str .= $this->_open_div();
  521. $fieldset_on = TRUE;
  522. // continue if the fieldset is part of the field values and not the "type"
  523. if (empty($val['fieldset']))
  524. {
  525. continue;
  526. }
  527. }
  528. if ($val['type'] == 'section')
  529. {
  530. $str .= "<div".$this->_open_row_attrs($val).'>';
  531. $str .= "<div class=\"section\">".$this->create_section($val)."</div>\n";
  532. $str .= "</div>\n";
  533. continue;
  534. }
  535. else if (!empty($val['section']))
  536. {
  537. $str .= "<div class=\"section\"><".$this->section_tag.">".$val['section']."</".$this->section_tag."></div>\n";
  538. }
  539. if ($val['type'] == 'copy')
  540. {
  541. $str .= "<div".$this->_open_row_attrs($val).'>';
  542. $str .= "<div class=\"copy\">".$this->create_copy($val)."</div>\n";
  543. $str .= "</div>\n";
  544. continue;
  545. }
  546. else if (!empty($val['copy']))
  547. {
  548. $str .= "<div".$this->_open_row_attrs($val).'>';
  549. $str .= "<div class=\"copy\"><".$this->copy_tag.">".$val['copy']."</".$this->copy_tag."></div>\n";
  550. $str .= "</div>\n";
  551. }
  552. if (!empty($val['custom']))
  553. {
  554. $str .= "<div".$this->_open_row_attrs($val).'>';
  555. $str .= "<span class=\"label\">";
  556. $str .= $this->create_label($val, TRUE);
  557. $str .= "</span>";
  558. $str .= "<span".$this->_open_field_attrs($val).">";
  559. $str .= $val['custom'];
  560. $str .= "</span>";
  561. $str .= "</div>\n";
  562. }
  563. else if (in_array($val['name'], $this->hidden) OR $val['type'] == 'hidden')
  564. {
  565. $end_str .= $this->create_hidden($val);
  566. }
  567. else if ((is_array($val['name']) AND in_array($val['name'], $this->displayonly)) OR $val['displayonly'] OR (is_string($this->displayonly) AND strtolower($this->displayonly) == 'all'))
  568. {
  569. if (isset($val['displayonly']) AND !is_bool($val['displayonly']))
  570. {
  571. $display_value = $val['displayonly'];
  572. }
  573. else
  574. {
  575. $display_value = (is_array($val['value'])) ? print_r($val['value'], TRUE) : $val['value'];
  576. }
  577. $str .= "<div".$this->_open_row_attrs($val).'>';
  578. $str .= "<span class=\"label\">";
  579. $str .= $val['before_label'].$this->create_label($val, FALSE).$val['after_label'];
  580. $str .= "</span>";
  581. $str .= "<span".$this->_open_field_attrs($val)."><span class=\"displayonly noclone\">";
  582. $str .= $val['before_html'].$display_value.$val['after_html'];
  583. $str .= "</span></span>";
  584. $str .= "</div>\n";
  585. }
  586. else if (!in_array($val['name'], $this->exclude))
  587. {
  588. $str .= "<div".$this->_open_row_attrs($val).'>';
  589. $str .= "<span class=\"label\">";
  590. $str .= $val['before_label'].$this->create_label($val, TRUE).$val['after_label'];
  591. $str .= "</span>";
  592. $str .= "<span".$this->_open_field_attrs($val).">";
  593. $str .= $this->create_field($val, FALSE);
  594. $str .= "</span>";
  595. $str .= "</div>\n";
  596. }
  597. }
  598. // close any open fieldsets
  599. if ($fieldset_on)
  600. {
  601. $str .= $this->_close_div();
  602. $val['open'] = FALSE;
  603. $str .= $this->create_fieldset($val);
  604. $str .= $this->_open_div();
  605. }
  606. $actions = $this->_render_actions();
  607. if (!empty($actions))
  608. {
  609. $str .= "<div class=\"actions\"><div class=\"actions_inner\">";
  610. $str .= $actions;
  611. $str .= "</div></div>\n";
  612. }
  613. if ($this->has_required AND ($this->show_required AND strtolower($this->show_required) != 'top'))
  614. {
  615. $str .= "<div class=\"required\">";
  616. $str .= str_replace('{required_indicator}', $this->required_indicator, $this->required_text);
  617. $str .= "</div>\n";
  618. }
  619. $str .= "</div>\n";
  620. $str = $begin_str . $str . $end_str;
  621. $this->_close_form($str);
  622. return $this->_html;
  623. }
  624. // --------------------------------------------------------------------
  625. /**
  626. * Render the HTML output
  627. *
  628. * @access public
  629. * @param array fields values... will overwrite anything done with the set_fields method previously
  630. * @return string
  631. */
  632. public function render_table($fields = NULL)
  633. {
  634. if (!empty($fields)) $this->set_fields($fields);
  635. // reorder
  636. $this->set_field_order();
  637. // pre process field values
  638. $this->pre_process_field_values();
  639. $this->_html = $this->html_prepend;
  640. $str = '';
  641. $begin_str = '';
  642. $end_str = '';
  643. if ($this->display_errors)
  644. {
  645. $func = $this->display_errors_func;
  646. if (function_exists($func))
  647. {
  648. $str .= $func();
  649. }
  650. }
  651. $colspan = ($this->label_layout == 'top') ? '1' : '2';
  652. $first = $this->_find_first_renderable_field();
  653. $is_fieldset_first = FALSE;
  654. if ($first['type'] != 'fieldset')
  655. {
  656. $str .= $this->_open_table();
  657. }
  658. else
  659. {
  660. $is_fieldset_first = TRUE;
  661. }
  662. $fieldset_on = FALSE;
  663. foreach($this->_fields as $key => $val)
  664. {
  665. $val = $this->normalize_params($val);
  666. if ($val['type'] == 'fieldset' OR !empty($val['fieldset']))
  667. {
  668. // don't close the table if it isn't opened earlier
  669. if ($is_fieldset_first == FALSE)
  670. {
  671. $str .= $this->_close_table();
  672. }
  673. $is_fieldset_first = FALSE;
  674. // close any existing field sets
  675. if ($fieldset_on)
  676. {
  677. $fieldset_val['open'] = FALSE;
  678. $str .= $this->create_fieldset($fieldset_val);
  679. }
  680. $fieldset_val['open'] = TRUE;
  681. if (!empty($val['fieldset']))
  682. {
  683. $fieldset_val['value'] = $val['fieldset'];
  684. }
  685. else
  686. {
  687. $fieldset_val = $val;
  688. }
  689. $str .= $this->create_fieldset($fieldset_val);
  690. $str .= $this->_open_table();
  691. $fieldset_on = TRUE;
  692. // continue if the fieldset is part of the field values and not the "type"
  693. if (empty($val['fieldset']))
  694. {
  695. continue;
  696. }
  697. }
  698. if ($val['type'] == 'section')
  699. {
  700. $str .= "<tr".$this->_open_row_attrs($val);
  701. $str .= ">\n\t<td colspan=\"".$colspan."\" class=\"section\">".$this->create_section($val)."</td>\n</tr>\n";
  702. continue;
  703. }
  704. else if (!empty($val['section']))
  705. {
  706. $str .= "<tr".$this->_open_row_attrs($val);
  707. $str .= ">\n\t<td colspan=\"".$colspan."\" class=\"section\"><".$this->section_tag.">".$val['section']."</".$this->section_tag."></td>\n</tr>\n";
  708. }
  709. if ($val['type'] == 'copy')
  710. {
  711. $str .= "<tr".$this->_open_row_attrs($val);
  712. $str .= ">\n\t<td colspan=\"".$colspan."\" class=\"copy\">".$this->create_copy($val)."</td></tr>\n";
  713. continue;
  714. }
  715. else if (!empty($val['copy']))
  716. {
  717. $str .= "<tr".$this->_open_row_attrs($val);
  718. $str .= ">\n\t<td colspan=\"".$colspan."\" class=\"copy\"><".$this->copy_tag.">".$val['copy']."</".$this->copy_tag."></td>\n</tr>\n";
  719. }
  720. if (!empty($val['custom']))
  721. {
  722. $str .= "<tr".$this->_open_row_attrs($val);
  723. $str .= ">\n\t";
  724. if ($val['display_label'] !== FALSE)
  725. {
  726. $str .= "<td class=\"label\">";
  727. if ($this->label_layout != 'top')
  728. {
  729. $str .= $this->create_label($val, TRUE);
  730. $str .= "</td>\n\t<td".$this->_open_field_attrs($val).">".$val['custom']."</td>\n</tr>\n";
  731. }
  732. else
  733. {
  734. $str .= $this->create_label($val, TRUE)."</td></tr>\n";
  735. $str .= "<tr".$this->_open_row_attrs($val);
  736. $str .= ">\n\t<td".$this->_open_field_attrs($val).">".$val['custom']."</td>\n</tr>\n";
  737. }
  738. }
  739. else
  740. {
  741. $str .= "<td class=\"value\" colspan=\"2\">".$val['custom']."</td>\n</tr>\n";
  742. }
  743. }
  744. else if (in_array($val['name'], $this->hidden) OR $val['type'] == 'hidden')
  745. {
  746. $end_str .= $this->create_hidden($val);
  747. }
  748. else if ((is_array($val['name']) AND in_array($val['name'], $this->displayonly)) OR $val['displayonly'] OR (is_string($this->displayonly) AND strtolower($this->displayonly) == 'all') OR $this->displayonly === TRUE)
  749. {
  750. $str .= "<tr".$this->_open_row_attrs($val);
  751. $str .= ">\n\t<td class=\"label\">";
  752. if (isset($val['displayonly']) AND !is_bool($val['displayonly']))
  753. {
  754. $display_value = $val['displayonly'];
  755. }
  756. else
  757. {
  758. $display_value = (is_array($val['value'])) ? print_r($val['value'], TRUE) : $val['value'];
  759. }
  760. if ($this->label_layout != 'top')
  761. {
  762. $str .= $val['before_label'].$this->create_label($val, FALSE).$val['after_label'];
  763. $str .= "</td>\n\t<td".$this->_open_field_attrs($val)."><span class=\"displayonly noclone\">".$val['before_html'].$display_value.$val['after_html']."\n".$this->create_hidden($val)."</span></td>\n</tr>\n";
  764. }
  765. else
  766. {
  767. $str .= $val['before_label'].$this->create_label($val, FALSE).$val['after_label']."</td></tr>\n";
  768. $str .= "<tr".$this->_open_row_attrs($val);
  769. $str .= ">\n\t<td".$this->_open_field_attrs($val).">".$val['before_html'].$display_value.$val['after_html']."</td>\n</tr>\n";
  770. }
  771. }
  772. else if (!in_array($val['name'], $this->exclude))
  773. {
  774. $str .= "<tr".$this->_open_row_attrs($val);
  775. $str .= ">\n\t";
  776. if ($val['display_label'] !== FALSE)
  777. {
  778. $str .= "<td class=\"label\">";
  779. if ($this->label_layout != 'top')
  780. {
  781. $str .= $val['before_label'].$this->create_label($val, TRUE).$val['after_label'];
  782. $str .= "</td>\n\t<td".$this->_open_field_attrs($val).">".$this->create_field($val, FALSE)."</td>\n</tr>\n";
  783. }
  784. else
  785. {
  786. $str .= $val['before_label'].$this->create_label($val, TRUE).$val['after_label']."</td></tr>\n";
  787. $str .= "<tr".$this->_open_row_attrs($val);
  788. $str .= ">\n\t<td".$this->_open_field_attrs($val).">".$this->create_field($val, FALSE)."</td>\n</tr>\n";
  789. }
  790. }
  791. else
  792. {
  793. $str .= "<td class=\"value\" colspan=\"2\">".$this->create_field($val, FALSE)."</td>\n</tr>\n";
  794. }
  795. }
  796. }
  797. // close any open fieldsets
  798. if ($fieldset_on)
  799. {
  800. $str .= $this->_close_table();
  801. $val['open'] = FALSE;
  802. $str .= $this->create_fieldset($val);
  803. $str .= $this->_open_table();
  804. }
  805. $actions = $this->_render_actions();
  806. if (!empty($actions))
  807. {
  808. if ($this->label_layout != 'top')
  809. {
  810. $str .= "<tr";
  811. if (!empty($this->row_id_prefix))
  812. {
  813. $str .= ' id="'.$this->row_id_prefix.'actions"';
  814. }
  815. $str .= ">\n\t<td></td>\n\t<td class=\"actions\"><div class=\"actions_inner\">";
  816. }
  817. else
  818. {
  819. $str .= "<tr>\n\t<td class=\"actions\"><div class=\"actions\">";
  820. }
  821. $str .= $actions;
  822. $str .= "</div></td>\n</tr>\n";
  823. }
  824. if ($this->has_required AND ($this->show_required AND strtolower($this->show_required) != 'top'))
  825. {
  826. $str .= "<tr>\n\t<td colspan=\"".$colspan."\" class=\"required\">";
  827. $str .= str_replace('{required_indicator}', $this->required_indicator, $this->required_text);
  828. $str .= "</td>\n</tr>\n";
  829. }
  830. $str .= $this->_close_table();
  831. $str = $begin_str . $str . $end_str;
  832. $this->_close_form($str);
  833. return $this->_html;
  834. }
  835. // --------------------------------------------------------------------
  836. /**
  837. * Render the HTML output using a specified template.
  838. *
  839. * Will provide an array of form fields that can be parsed like so {my_field}
  840. *
  841. * @access public
  842. * @param string the name of the template view file to use
  843. * @param array fields values... will overwrite anything done with the set_fields method previously
  844. * @return string
  845. */
  846. public function render_template($template, $fields = NULL, $parse = TRUE)
  847. {
  848. if (!empty($fields)) $this->set_fields($fields);
  849. // reorder
  850. $this->set_field_order();
  851. // pre process field values
  852. $this->pre_process_field_values();
  853. $this->_html = $this->html_prepend;
  854. $errors = NULL;
  855. if ($this->display_errors)
  856. {
  857. $func = $this->display_errors_func;
  858. if (function_exists($func))
  859. {
  860. $errors = $func();
  861. }
  862. }
  863. $fields = array();
  864. foreach($this->_fields as $key => $field)
  865. {
  866. $fields[$key]['field'] = $this->create_field($field);
  867. $fields[$key]['label'] = $this->create_label($field);
  868. }
  869. $vars['fields'] = $fields;
  870. $vars['errors'] = $errors;
  871. if (is_array($template))
  872. {
  873. $module = key($template);
  874. $view = current($template);
  875. $str = $this->CI->load->module_view($module, $view, $vars, TRUE);
  876. }
  877. else
  878. {
  879. $str = $this->CI->load->view($template, $vars, TRUE);
  880. }
  881. if ($parse === TRUE)
  882. {
  883. $this->CI->load->library('parser');
  884. $str = parse_template_syntax($str, $vars, 'ci');
  885. }
  886. $actions = $this->_render_actions();
  887. if (!empty($actions))
  888. {
  889. $str .= '<div class="actions">';
  890. $str .= $actions;
  891. $str .= "</div>";
  892. }
  893. $this->_html = $this->_close_form($str);
  894. return $this->_html;
  895. }
  896. // --------------------------------------------------------------------
  897. /**
  898. * Finds the first field that is not hidden and renderable
  899. *
  900. * @access protected
  901. * @return array
  902. */
  903. protected function _find_first_renderable_field()
  904. {
  905. foreach($this->_fields as $key => $field)
  906. {
  907. $invalid_types = array('hidden');
  908. if ( ! in_array($field['type'], $invalid_types) AND ! in_array($key, $this->hidden))
  909. {
  910. return $field;
  911. }
  912. }
  913. return reset($this->_fields);
  914. }
  915. // --------------------------------------------------------------------
  916. /**
  917. * Creates the opening div element that contains the form fields
  918. *
  919. * @access protected
  920. * @return string
  921. */
  922. protected function _open_div()
  923. {
  924. $str = '';
  925. $str .= "<div>\n";
  926. return $str;
  927. }
  928. // --------------------------------------------------------------------
  929. /**
  930. * Creates the opening table element
  931. *
  932. * @access protected
  933. * @return string
  934. */
  935. protected function _open_table()
  936. {
  937. $str = '';
  938. $str .= "<table>\n";
  939. $str .= "<colgroup>\n";
  940. $str .= "<col class=\"label_column\">\n";
  941. $str .= "<col class=\"field_column\">\n";
  942. $str .= "</colgroup>\n";
  943. $str .= "<tbody>\n";
  944. return $str;
  945. }
  946. // --------------------------------------------------------------------
  947. /**
  948. * Creates the closing element
  949. *
  950. * @access protected
  951. * @return string
  952. */
  953. protected function _close_div()
  954. {
  955. $str = '';
  956. $str .= "</div>\n";
  957. return $str;
  958. }
  959. // --------------------------------------------------------------------
  960. /**
  961. * Creates the closing table elements
  962. *
  963. * @access protected
  964. * @return string
  965. */
  966. protected function _close_table()
  967. {
  968. $str = '';
  969. $str .= "</tbody>\n";
  970. $str .= "</table>\n";
  971. return $str;
  972. }
  973. // --------------------------------------------------------------------
  974. /**
  975. * Creates the opening row TR or div with attrs
  976. *
  977. * @access protected
  978. * @param array fields parameters
  979. * @return string
  980. */
  981. protected function _open_row_attrs($val)
  982. {
  983. $str = '';
  984. if (!empty($this->row_id_prefix))
  985. {
  986. $str .= ' id="'.$this->row_id_prefix.Form::create_id($val['name']).'"';
  987. }
  988. if (!empty($val['row_class']))
  989. {
  990. $str .= ' class="'.$val['row_class'].'"';
  991. }
  992. if (!empty($val['row_style']))
  993. {
  994. $str .= ' style="'.$val['row_style'].'"';
  995. }
  996. return $str;
  997. }
  998. // --------------------------------------------------------------------
  999. /**
  1000. * Creates the opening field td.value or div.value with attrs
  1001. *
  1002. * @access protected
  1003. * @param array fields parameters
  1004. * @return string
  1005. */
  1006. protected function _open_field_attrs($val)
  1007. {
  1008. $str = ' class="field';
  1009. if (!empty($val['field_class']))
  1010. {
  1011. $str .= ' '.$val['field_class'];
  1012. }
  1013. $str .= '"';
  1014. if (!empty($val['field_style']))
  1015. {
  1016. $str .= ' style="'.$val['field_style'].'"';
  1017. }
  1018. return $str;
  1019. }
  1020. // --------------------------------------------------------------------
  1021. /**
  1022. * Outputs the actions for the form
  1023. *
  1024. * @access protected
  1025. * @param string
  1026. * @return string
  1027. */
  1028. protected function _render_actions()
  1029. {
  1030. $str = '';
  1031. if ( ! empty($this->reset_value))
  1032. {
  1033. if (preg_match("/^</i", $this->reset_value))
  1034. {
  1035. $str .= $this->reset_value;
  1036. }
  1037. else
  1038. {
  1039. $str .= $this->form->reset($this->reset_value, '', array('class' => 'reset'));
  1040. }
  1041. }
  1042. if ( ! empty($this->cancel_value))
  1043. {
  1044. if (preg_match("/^</i", $this->cancel_value))
  1045. {
  1046. $str .= $this->cancel_value;
  1047. }
  1048. else
  1049. {
  1050. $cancel_attrs = array('class' => 'cancel');
  1051. if ( ! empty($this->cancel_action))
  1052. {
  1053. $cancel_attrs['onclick'] = $this->cancel_action;
  1054. }
  1055. $str .= $this->form->button($this->cancel_value, '', $cancel_attrs);
  1056. }
  1057. }
  1058. if (!empty($this->submit_value) AND $this->displayonly != 'all')
  1059. {
  1060. // check if the string has a tag and if so just pump in the string
  1061. if (preg_match("/^</i", $this->submit_value))
  1062. {
  1063. $str .= $this->submit_value;
  1064. }
  1065. else
  1066. {
  1067. $submit_btn = (preg_match("/(.)+\\.(jp(e){0,1}g$|gif$|png$)/i", $this->submit_value)) ? 'image' : 'submit';
  1068. $submit_name = (empty($this->submit_name)) ? url_title($this->submit_value, '_') : $this->submit_name;
  1069. if (empty($submit_name)) $submit_name = 'submit_form';
  1070. $submit_name = (!empty($this->name_prefix) AND $this->names_id_match) ? $this->name_prefix.'--'.$submit_name : $submit_name;
  1071. $submit_id = $submit_name;
  1072. // if (!empty($this->name_prefix))
  1073. // {
  1074. // $submit_id = $this->name_prefix.'--'.$submit_id;
  1075. // }
  1076. $str .= $this->form->$submit_btn($this->submit_value, $submit_name, array('class' => 'submit', 'id' => $submit_id));
  1077. }
  1078. }
  1079. if (!empty($this->other_actions)) $str .= $this->other_actions;
  1080. return $str;
  1081. }
  1082. // --------------------------------------------------------------------
  1083. /**
  1084. * Outputs the last part of the form rendering for both a table and div
  1085. *
  1086. * @access protected
  1087. * @param string
  1088. * @return string
  1089. */
  1090. protected function _close_form($str)
  1091. {
  1092. if ($this->use_form_tag)
  1093. {
  1094. $this->_html .= $this->form->open($this->form_attrs);
  1095. }
  1096. if (!empty($this->fieldset))
  1097. {
  1098. $this->_html .= $this->form->fieldset_open($this->fieldset);
  1099. }
  1100. // wrapper div to apply ID
  1101. $wrapper_open_str = "<div class=\"".$this->css_class."\"";
  1102. if (empty($this->id))
  1103. {
  1104. $this->id = $this->id();
  1105. }
  1106. $wrapper_open_str .= ' id="'.$this->id.'"';
  1107. $wrapper_open_str .= ">\n";
  1108. $wrapper_close_str = "</div>";
  1109. // apply any CSS first
  1110. foreach($this->css as $css)
  1111. {
  1112. $this->_html .= $this->_apply_asset_files('css', $css);
  1113. }
  1114. $this->_html .= $wrapper_open_str.$str.$wrapper_close_str;
  1115. if (!empty($this->key_check))
  1116. {
  1117. $this->_html .= $this->create_hidden(array('name' => $this->key_check_name, 'value' => $this->key_check));
  1118. }
  1119. if (!empty($this->fieldset))
  1120. {
  1121. $this->_html .= $this->form->fieldset_close();
  1122. }
  1123. if ($this->use_form_tag)
  1124. {
  1125. $this->_html .= $this->form->close('', FALSE); // we set the token above just in case form tags are turned off
  1126. }
  1127. $this->_html .= $this->_render_js();
  1128. $this->_html .= $this->html_append;
  1129. return $this->_html;
  1130. }
  1131. // --------------------------------------------------------------------
  1132. /**
  1133. * Normalize the fields so that the other methods can expect certain field attributes
  1134. *
  1135. * @access protected
  1136. * @param array fields values... will overwrite anything done with the set_fields method previously
  1137. * @return array
  1138. */
  1139. protected function _default($val)
  1140. {
  1141. if (is_object($val)) $val = get_object_vars($val);
  1142. $defaults = array(
  1143. 'key' => '', // a unique identifier for the field. By default, it will be the the same as the ID. This parameter is used mostly for post processing of a field
  1144. 'id' => '', // the ID attribute of the field. This value will be auto generated if not provided. Set to FALSE if you don't want an ID value
  1145. 'name' => '', // the name attribute of the field
  1146. 'type' => '', // the type attribute of the field (e.g. text, select, password, etc.)
  1147. 'default' => '', // the default value of the field
  1148. 'max_length' => 0, // the maxlength parameter to associate with the field
  1149. 'comment' => '', // a comment to associate with the field's label
  1150. 'label' => '', // the label to associate with the field
  1151. 'before_label' => '', // for HTML before the label
  1152. 'after_label' => '', // for HTML after the label
  1153. 'required' => FALSE, // puts a required flag next to field label
  1154. 'size' => '', // the size attribute of the field
  1155. 'class' => '', // the CSS class attribute to associate with the field
  1156. 'style' => '', // inline style
  1157. 'value' => '', // the value of the field
  1158. 'readonly' => '', // sets readonly attribute on field
  1159. 'disabled' => '', // sets disabled attribute on the field
  1160. 'tabindex' => '', // adds the tab index attribute to a field
  1161. 'label_colons' => NULL, // whether to display the label colons
  1162. 'display_label' => TRUE, // whether to display the label
  1163. 'order' => NULL, // the display order value to associate with the field
  1164. 'before_html' => '', // for HTML before the field
  1165. 'after_html' => '', // for HTML after the field
  1166. 'displayonly' => FALSE, // only displays the value (no field)
  1167. 'pre_process' => NULL, // a pre process function
  1168. 'post_process' => NULL, // a post process function run on post
  1169. 'js' => '', // js file or script using <script> tag
  1170. 'css' => '', // css to associate with the field
  1171. 'represents' => '', // specifies what other types of fields that this field should represent
  1172. 'ignore_representative' => FALSE, // ignores any representative
  1173. 'data' => array(), // data attributes
  1174. 'title' => NULL, // the title attribute
  1175. 'attributes' => '', // a generic string value of attributes for the form field (e.g. 'class="myclass"')
  1176. '__DEFAULTS__' => TRUE // set so that we no that the array has been processed and we can check it so it won't process it again
  1177. );
  1178. $params = array_merge($defaults, $val);
  1179. if (empty($params['orig_name'])) $params['orig_name'] = $params['name']; // for labels in case the name_array is used
  1180. if (empty($params['key']))
  1181. {
  1182. $params['key'] = Form::create_id($params['orig_name']);
  1183. }
  1184. if (!isset($val['value']) AND ($params['type'] != 'checkbox' AND !($params['type'] == 'boolean' AND $this->boolean_mode == 'checkbox')))
  1185. {
  1186. $params['value'] = $params['default'];
  1187. }
  1188. if (!isset($params['label_colons']))
  1189. {
  1190. $params['label_colons'] = $this->label_colons;
  1191. }
  1192. if (!empty($val['name']))
  1193. {
  1194. if ((is_array($this->readonly) AND in_array($val['name'], $this->readonly)) OR (is_string($this->readonly) AND strtolower($this->readonly) == 'all'))
  1195. {
  1196. $params['readonly'] = 'readonly';
  1197. }
  1198. if ((is_array($this->disabled) AND in_array($val['name'], $this->disabled)) OR (is_string($this->disabled) AND strtolower($this->disabled) == 'all'))
  1199. {
  1200. $params['disabled'] = 'disabled';
  1201. }
  1202. }
  1203. if (!empty($this->name_array) AND strpos($params['name'], '[') === FALSE)
  1204. {
  1205. if (!$this->names_id_match)
  1206. {
  1207. if ($params['id'] !== FALSE)
  1208. {
  1209. $params['id'] = $this->name_array.'['.$params['orig_name'].']';
  1210. }
  1211. $params['name'] = $params['orig_name'];
  1212. }
  1213. else
  1214. {
  1215. if ($this->key_check_name != $params['orig_name'])
  1216. {
  1217. $params['name'] = $this->name_array.'['.$params['orig_name'].']';
  1218. }
  1219. else
  1220. {
  1221. $params['name'] = $params['orig_name'];
  1222. }
  1223. }
  1224. if (in_array($params['orig_name'], $this->hidden) AND !in_array($params['name'], $this->hidden)) $this->hidden[] = $params['name'];
  1225. }
  1226. if (!empty($this->name_prefix))
  1227. {
  1228. if (!$this->names_id_match)
  1229. {
  1230. if ($params['id'] !== FALSE)
  1231. {
  1232. $params['id'] = $this->name_prefix.'--'.$params['orig_name']; // used double hyphen so easier to explode
  1233. }
  1234. $params['name'] = $params['orig_name'];
  1235. }
  1236. else
  1237. {
  1238. if ($this->key_check_name != $params['orig_name'])
  1239. {
  1240. $params['name'] = $this->name_prefix.'--'.$params['orig_name'];
  1241. }
  1242. else
  1243. {
  1244. $params['name'] = $params['orig_name'];
  1245. }
  1246. }
  1247. if (in_array($params['orig_name'], $this->hidden) AND !in_array($params['name'], $this->hidden)) $this->hidden[] = $params['name'];
  1248. }
  1249. // grab options from a model if a model is specified
  1250. if (!empty($params['model']))
  1251. {
  1252. $model_params = (!empty($params['model_params'])) ? $params['model_params'] : array();
  1253. $params['options'] = $this->options_from_model($params['model'], $model_params);
  1254. }
  1255. if ($params['type'] == 'enum' OR $params['type'] == 'select')
  1256. {
  1257. if (!isset($params['options']))
  1258. {
  1259. $params['options'] = array();
  1260. }
  1261. if ((empty($params['options']) AND is_array($params['options'])) AND is_array($params['max_length']) AND !empty($params['max_length']))
  1262. {
  1263. $params['options'] = $params['max_length'];
  1264. }
  1265. if (!empty($params['hide_if_one']) AND count($params['options']) <= 1)
  1266. {
  1267. $params['type'] = 'hidden';
  1268. $params['display_label'] = FALSE;
  1269. }
  1270. }
  1271. // fix common errors
  1272. if (!empty($params['maxlength']) AND empty($params['max_length']))
  1273. {
  1274. $params['max_length'] = $params['maxlength'];
  1275. unset($params['maxlength']);
  1276. }
  1277. // take out javascript so we execute it only once per render
  1278. if (!empty($params['js']))
  1279. {
  1280. $this->add_js($params['js']);
  1281. }
  1282. // take out css so we execute it only once per render
  1283. if (!empty($params['css']))
  1284. {
  1285. $this->add_css($params['css']);
  1286. }
  1287. // says whether this field can represent other field types
  1288. if (!empty($params['represents']))
  1289. {
  1290. $this->representatives[$params['type']] = $params['represents'];
  1291. }
  1292. // set the field type CSS class
  1293. $type = (!empty($params['type'])) ? $params['type'] : 'text';
  1294. $field_class = $this->class_type_prefix.$type;
  1295. $params['class'] = (!empty($params['class']) AND strpos($params['class'], $field_class) === FALSE) ? $field_class.' '.$params['class'] : $params['class'];
  1296. $this->_cached[$params['name']] = $params;
  1297. return $params;
  1298. }
  1299. // --------------------------------------------------------------------
  1300. /**
  1301. * Returns the id for a form
  1302. *
  1303. * @access public
  1304. * @return string
  1305. */
  1306. public function id()
  1307. {
  1308. if (empty($this->id))
  1309. {
  1310. $this->id = uniqid('form_');
  1311. }
  1312. return $this->id;
  1313. }
  1314. // --------------------------------------------------------------------
  1315. /**
  1316. * Get the default values for any field
  1317. *
  1318. * @access public
  1319. * @param array fields values... will overwrite anything done with the set_fields method previously
  1320. * @return array
  1321. */
  1322. public function normalize_params($val, $defaults = array())
  1323. {
  1324. if ($val == '')
  1325. {
  1326. $val = array();
  1327. }
  1328. // check to see if the array is already normalized
  1329. if (!$this->_has_defaults($val))
  1330. {
  1331. $val = $this->_default($val);
  1332. }
  1333. // set up defaults
  1334. $params = array_merge($defaults, $val);
  1335. return $params;
  1336. }
  1337. // --------------------------------------------------------------------
  1338. /**
  1339. * Renders the custom field
  1340. *
  1341. * @access protected
  1342. * @param array fields values... will overwrite anything done with the set_fields method previously
  1343. * @return array
  1344. */
  1345. protected function _render_custom_field($params)
  1346. {
  1347. $field = FALSE;
  1348. if (is_array($params) AND isset($this->custom_fields[$params['type']]))
  1349. {
  1350. $func = $this->custom_fields[$params['type']];
  1351. if (is_a($func, 'Form_builder_field'))
  1352. {
  1353. // give custom fields a reference to the current object
  1354. $params['instance'] =& $this;
  1355. $this->_rendering = TRUE;
  1356. // take out CSS so we execute it only once per render
  1357. if (!empty($params['css']))
  1358. {
  1359. $this->add_css($params['css'], $params['type']);
  1360. }
  1361. // same here... but we are looking for CSS on the object
  1362. if (!empty($func->css))
  1363. {
  1364. $this->add_css($func->css, $params['type']);
  1365. }
  1366. // take out javascript so we execute it only once per render
  1367. if (!empty($params['js']))
  1368. {
  1369. $this->add_js($params['js'], $params['type']);
  1370. }
  1371. // same here... but we are looking for js on the object
  1372. if (!empty($func->js))
  1373. {
  1374. $this->add_js($func->js, $params['type']);
  1375. }
  1376. $field = $func->render($params);
  1377. }
  1378. else if (is_callable($func))
  1379. {
  1380. $this->_rendering = TRUE;
  1381. $field = $this->create_custom($func, $params);
  1382. }
  1383. }
  1384. else if (is_string($params))
  1385. {
  1386. $field = $params;
  1387. }
  1388. $this->_rendering = FALSE;
  1389. return $field;
  1390. }
  1391. // --------------------------------------------------------------------
  1392. /**
  1393. * Checks to see if the array to initialize a field is normalized or not
  1394. *
  1395. * @access protected
  1396. * @param array fields parameters
  1397. * @return string
  1398. */
  1399. protected function _has_defaults($vals)
  1400. {
  1401. return (!empty($vals['__DEFAULTS__']));
  1402. }
  1403. // --------------------------------------------------------------------
  1404. /**
  1405. * Looks at the field type attribute and determines which form field to render
  1406. *
  1407. * IMPORTANT! You probably shouldn't call this method from within a custom field type because it may create an infinite loop!
  1408. *
  1409. * @access public
  1410. * @param array fields parameters
  1411. * @param boolean should the normalization be ran again?
  1412. * @return string
  1413. */
  1414. public function create_field($params, $normalize = TRUE)
  1415. {
  1416. // needed to prevent runaway loops from custom fields... actually the template field type wont work with this
  1417. // if ($this->_rendering)
  1418. // {
  1419. // return FALSE;
  1420. // }
  1421. if ($normalize) $params = $this->normalize_params($params); // done again here in case you create a field without doing the render method
  1422. // now we look at all the fields that may represent other field types based on parameters
  1423. if (!empty($this->representatives) AND is_array($this->representatives) AND empty($params['ignore_representative']))
  1424. {
  1425. foreach($this->representatives as $key => $val)
  1426. {
  1427. $matched = FALSE;
  1428. // if the representative is an associative array with keys being parameters to match (e.g. type and name), then we loop through those parameters to find a match
  1429. if (is_array($val) AND is_string(key($val)))
  1430. {
  1431. foreach($val as $k => $v)
  1432. {
  1433. $matched = (is_array($v) AND in_array($params[$k], $v) OR (is_string($v) AND preg_match('#'.$v.'#', $params[$k]))) ? TRUE : FALSE;
  1434. if (!$matched)
  1435. {
  1436. break;
  1437. }
  1438. }
  1439. }
  1440. // if the representative is an array and the param type is in that array then we are a match
  1441. else if (is_array($val) AND in_array($params['type'], $val))
  1442. {
  1443. $matched = TRUE;
  1444. }
  1445. // if the representative is a string, then we do a regex to see if we are a match
  1446. else if (is_string($val) AND preg_match('#'.$val.'#', $params['type']))
  1447. {
  1448. $matched = TRUE;
  1449. }
  1450. // if we matched,then set the param type to it's representative and we break the loop and continue on
  1451. if ($matched)
  1452. {
  1453. $params['type'] = $key;
  1454. break;
  1455. }
  1456. }
  1457. }
  1458. $str = $this->_render_custom_field($params);
  1459. if (!$str)
  1460. {
  1461. switch($params['type'])
  1462. {
  1463. case 'none': case 'blank' :
  1464. $str = '';
  1465. break;
  1466. case 'custom':
  1467. $func = (isset($params['func'])) ? $params['func'] : function($params) { return (isset($params["value"])) ? $params["value"] : "" ; };
  1468. $str = $this->create_custom($func, $params);
  1469. break;
  1470. default :
  1471. $method = 'create_'.$params['type'];
  1472. if (method_exists($this, $method) AND $params['type'] != 'field')
  1473. {
  1474. $str = $this->$method($params);
  1475. }
  1476. else
  1477. {
  1478. $params['type'] = 'text';
  1479. $str = $this->create_text($params);
  1480. }
  1481. }
  1482. }
  1483. // cache the field types being rendered
  1484. $rendered_type = (!empty($matched)) ? $key : $params['type'];
  1485. $this->_rendered_field_types[$rendered_type] = $rendered_type;
  1486. // $this->_rendered_field_types[$params['type']] = $params['type'];
  1487. // add before/after html
  1488. $str = $params['before_html'].$str.$params['after_html'];
  1489. return $str;
  1490. }
  1491. // --------------------------------------------------------------------
  1492. /**
  1493. * Creates the label for the form
  1494. *
  1495. * By default, if no label value is given, the method will generate one
  1496. * based on the name of the field
  1497. *
  1498. * @access public
  1499. * @param array fields parameters
  1500. * @param boolean should the label be displayed?
  1501. * @return string
  1502. */
  1503. public function create_label($params, $use_label = TRUE)
  1504. {
  1505. if (is_string($params))
  1506. {
  1507. $params = array('label' => $params);
  1508. }
  1509. $params = $this->normalize_params($params);
  1510. $str = '';
  1511. if (isset($params['display_label']) AND $params['display_label'] === FALSE) return $str;
  1512. if (empty($params['label']))
  1513. {
  1514. if ($lang = $this->label_lang($params['orig_name']))
  1515. {
  1516. $params['label'] = $lang;
  1517. }
  1518. else
  1519. {
  1520. $params['label'] = ucfirst(str_replace('_', ' ', $params['orig_name']));
  1521. }
  1522. $label_words = explode(' ', $params['label']);
  1523. if (in_array(strtolower($label_words[0]), $this->question_keys))
  1524. {
  1525. $params['label'] .= '?';
  1526. }
  1527. }
  1528. $mode = (!empty($params['mode'])) ? $params['mode'] : $this->single_select_mode;
  1529. if (($params['type'] == 'enum' OR $params['type'] == 'multi' OR $params['type'] == 'array') AND ($mode == 'radios' OR ($mode == 'auto' AND count($params['options']) <= 2)))
  1530. {
  1531. $use_label = FALSE;
  1532. }
  1533. if ($use_label)
  1534. {
  1535. if (!empty($params['id']))
  1536. {
  1537. $id_name = $params['id'];
  1538. }
  1539. elseif (!empty($this->name_prefix))
  1540. {
  1541. $name_parts = explode($this->name_prefix.'--', $params['name']);
  1542. $id_name = $this->name_prefix.'--'.end($name_parts); // ugly... bug needed for nested repeatable fields
  1543. }
  1544. else
  1545. {
  1546. $id_name = $params['orig_name'];
  1547. }
  1548. $class = '';
  1549. if (isset($params['label_class']) AND !empty($params['label_class']))
  1550. {
  1551. $class = ' class="'.$params['label_class'].'"';
  1552. }
  1553. $styles = (isset($params['nowrap']) AND $params['nowrap'] === TRUE) ? ' style="white-space: nowrap;"' : '';
  1554. $str .= "<label for=\"".Form::create_id($id_name)."\"".$class." id=\"label_".Form::create_id($id_name)."\"".$styles.">";
  1555. }
  1556. if ($this->tooltip_labels)
  1557. {
  1558. $str .= $this->create_tooltip($params);
  1559. }
  1560. else
  1561. {
  1562. $str .= $params['label'];
  1563. }
  1564. if ($params['required'])
  1565. {
  1566. $str .= '<span class="required">'.$this->required_indicator.'</span>';
  1567. $this->has_required = TRUE;
  1568. }
  1569. if ($params['label_colons']) $str .= ':';
  1570. if ($use_label)
  1571. {
  1572. $str .= "</label>";
  1573. }
  1574. return $str;
  1575. }
  1576. // --------------------------------------------------------------------
  1577. /**
  1578. * Creates the text input for the form
  1579. *
  1580. * @access public
  1581. * @param array fields parameters
  1582. * @return string
  1583. */
  1584. public function create_text($params)
  1585. {
  1586. $params = $this->normalize_params($params);
  1587. if (empty($params['size']))
  1588. {
  1589. if (!empty($params['max_length']))
  1590. {
  1591. $size = ($params['max_length'] > $this->text_size_limit) ? $this->text_size_limit : $params['max_length'];
  1592. }
  1593. else
  1594. {
  1595. $size = $this->text_size_limit;
  1596. }
  1597. }
  1598. else
  1599. {
  1600. $size = $params['size'];
  1601. }
  1602. $attrs = array(
  1603. 'id' => $params['id'],
  1604. 'class' => $params['class'],
  1605. 'maxlength' => $params['max_length'],
  1606. 'size' => $size,
  1607. 'readonly' => $params['readonly'],
  1608. 'autocomplete' => (!empty($params['autocomplete']) ? $params['autocomplete'] : NULL),
  1609. 'placeholder' => (!empty($params['placeholder']) ? $params['placeholder'] : NULL),
  1610. 'required' => (!empty($params['required']) ? TRUE : NULL),
  1611. 'data' => $params['data'],
  1612. 'style' => $params['style'],
  1613. 'tabindex' => $params['tabindex'],
  1614. 'attributes' => $params['attributes'],
  1615. 'disabled' => $params['disabled'],
  1616. 'pattern' => (!empty($params['pattern']) ? $params['pattern'] : NULL),
  1617. );
  1618. if (isset($params['attrs']))
  1619. {
  1620. $attrs = array_merge($attrs, $params['attrs']);
  1621. }
  1622. return $this->form->input($params['name'], $params['type'], $params['value'], $attrs);
  1623. }
  1624. // --------------------------------------------------------------------
  1625. /**
  1626. * Creates the password input for the form
  1627. *
  1628. * @access public
  1629. * @param array fields parameters
  1630. * @return string
  1631. */
  1632. public function create_password($params)
  1633. {
  1634. $params['type'] = 'password';
  1635. return $this->create_text($params);
  1636. }
  1637. // --------------------------------------------------------------------
  1638. /**
  1639. * Creates the select input for the form
  1640. *
  1641. * @access public
  1642. * @param array fields parameters
  1643. * @return string
  1644. */
  1645. public function create_select($params)
  1646. {
  1647. $defaults = array(
  1648. 'options' => array(),
  1649. 'first_option' => '',
  1650. 'disabled_options' => array(),
  1651. );
  1652. $params = $this->normalize_params($params, $defaults);
  1653. $attrs = array(
  1654. 'id' => $params['id'],
  1655. 'class' => $params['class'],
  1656. 'readonly' => $params['readonly'],
  1657. 'disabled' => $params['disabled'],
  1658. 'required' => (!empty($params['required']) ? TRUE : NULL),
  1659. 'data' => $params['data'],
  1660. 'style' => $params['style'],
  1661. 'tabindex' => $params['tabindex'],
  1662. 'attributes' => $params['attributes'],
  1663. 'placeholder' => (!empty($params['placeholder']) ? $params['placeholder'] : NULL),
  1664. 'autocomplete' => (!empty($params['autocomplete']) ? $params['autocomplete'] : NULL),
  1665. );
  1666. $name = $params['name'];
  1667. if (!empty($params['multiple']))
  1668. {
  1669. $attrs['multiple'] = 'multiple';
  1670. $name = $params['name'].'[]';
  1671. }
  1672. if (!empty($params['options']) AND !empty($params['equalize_key_value']) AND is_array($params['options']))
  1673. {
  1674. $options = array_values($params['options']);
  1675. $options = array_combine($options, $options);
  1676. $params['options'] = $options;
  1677. }
  1678. return $this->form->select($name, $params['options'], $params['value'], $attrs, $params['first_option'], $params['disabled_options']);
  1679. }
  1680. // --------------------------------------------------------------------
  1681. /**
  1682. * Creates the checkbox input for the form
  1683. *
  1684. * @access public
  1685. * @param array fields parameters
  1686. * @return string
  1687. */
  1688. public function create_checkbox($params)
  1689. {
  1690. $defaults = array(
  1691. 'checked' => FALSE // for checkbox/radio
  1692. );
  1693. $params = $this->normalize_params($params, $defaults);
  1694. $str = '';
  1695. $attrs = array(
  1696. 'id' => $params['id'],
  1697. 'class' => $params['class'],
  1698. 'readonly' => $params['readonly'],
  1699. 'disabled' => $params['disabled'],
  1700. 'data' => $params['data'],
  1701. 'style' => $params['style'],
  1702. 'tabindex' => $params['tabindex'],
  1703. 'attributes' => $params['attributes'],
  1704. 'required' => (!empty($params['required']) ? TRUE : NULL),
  1705. );
  1706. if ($params['checked'])
  1707. {
  1708. $attrs['checked'] = 'checked';
  1709. }
  1710. if (isset($params['value']) AND $params['value'] == '')
  1711. {
  1712. $params['value'] = 1;
  1713. }
  1714. $str .= $this->form->checkbox($params['name'], $params['value'], $attrs);
  1715. return $str;
  1716. }
  1717. // --------------------------------------------------------------------
  1718. /**
  1719. * Creates the radio input for the form
  1720. *
  1721. * @access public
  1722. * @param array fields parameters
  1723. * @return string
  1724. */
  1725. public function create_radio($params)
  1726. {
  1727. $defaults = array(
  1728. 'checked' => FALSE // for checkbox/radio
  1729. );
  1730. $params = $this->normalize_params($params, $defaults);
  1731. $str = '';
  1732. $attrs = array(
  1733. 'id' => $params['id'],
  1734. 'class' => $params['class'],
  1735. 'readonly' => $params['readonly'],
  1736. 'disabled' => $params['disabled'],
  1737. 'data' => $params['data'],
  1738. 'style' => $params['style'],
  1739. 'tabindex' => $params['tabindex'],
  1740. 'attributes' => $params['attributes'],
  1741. 'required' => (!empty($params['required']) ? TRUE : NULL),
  1742. );
  1743. if ($params['checked'])
  1744. {
  1745. $attrs['checked'] = 'checked';
  1746. }
  1747. if (isset($params['value']) AND $params['value'] == '')
  1748. {
  1749. $params['value'] = 1;
  1750. }
  1751. $str .= $this->form->radio($params['name'], $params['value'], $attrs);
  1752. return $str;
  1753. }
  1754. // --------------------------------------------------------------------
  1755. /**
  1756. * Creates the textarea input for the form
  1757. *
  1758. * @access public
  1759. * @param array fields parameters
  1760. * @return string
  1761. */
  1762. public function create_textarea($params)
  1763. {
  1764. $params = $this->normalize_params($params);
  1765. $attrs = array(
  1766. 'id' => $params['id'],
  1767. 'class' => $params['class'],
  1768. 'rows' => (!empty($params['rows'])) ? $params['rows'] : $this->textarea_rows,
  1769. 'cols' => (!empty($params['cols'])) ? $params['cols'] : $this->textarea_cols,
  1770. 'readonly' => $params['readonly'],
  1771. 'autocomplete' => (!empty($params['autocomplete']) ? $params['autocomplete'] : NULL),
  1772. 'placeholder' => (!empty($params['placeholder']) ? $params['placeholder'] : NULL),
  1773. 'required' => (!empty($params['required']) ? TRUE : NULL),
  1774. 'data' => $params['data'],
  1775. 'style' => $params['style'],
  1776. 'tabindex' => $params['tabindex'],
  1777. 'attributes' => $params['attributes'],
  1778. 'disabled' => $params['disabled'],
  1779. 'maxlength' => (!empty($params['max_length']) ? $params['max_length'] : NULL),
  1780. );
  1781. return $this->form->textarea($params['name'], $params['value'], $attrs);
  1782. }
  1783. // --------------------------------------------------------------------
  1784. /**
  1785. * Creates the hidden input for the form
  1786. *
  1787. * @access public
  1788. * @param array fields parameters
  1789. * @return string
  1790. */
  1791. public function create_hidden($params)
  1792. {
  1793. $params = $this->normalize_params($params);
  1794. // need to do check here because hidden is used for key_check
  1795. $attrs = array(
  1796. 'id' => $params['id'],
  1797. 'data' => $params['data'],
  1798. 'class' => $params['class'],
  1799. );
  1800. return $this->form->hidden($params['name'], $params['value'], $attrs);
  1801. }
  1802. // --------------------------------------------------------------------
  1803. /**
  1804. * Creates a submit button input for the form
  1805. *
  1806. * @access public
  1807. * @param array fields parameters
  1808. * @return string
  1809. */
  1810. public function create_submit($params)
  1811. {
  1812. $params = $this->normalize_params($params);
  1813. $attrs = array(
  1814. 'id' => $params['id'],
  1815. 'class' => $params['class'],
  1816. 'readonly' => $params['readonly'],
  1817. 'disabled' => $params['disabled'],
  1818. 'data' => $params['data'],
  1819. 'style' => $params['style'],
  1820. 'tabindex' => $params['tabindex'],
  1821. 'attributes' => $params['attributes'],
  1822. );
  1823. return $this->form->submit($params['value'], $params['name'], $attrs);
  1824. }
  1825. // --------------------------------------------------------------------
  1826. /**
  1827. * Creates a basic form button for the form
  1828. *
  1829. * @access public
  1830. * @param array fields parameters
  1831. * @return string
  1832. */
  1833. public function create_button($params)
  1834. {
  1835. $params = $this->normalize_params($params);
  1836. $attrs = array(
  1837. 'id' => $params['id'],
  1838. 'class' => $params['class'],
  1839. 'readonly' => $params['readonly'],
  1840. 'disabled' => $params['disabled'],
  1841. 'data' => $params['data'],
  1842. 'style' => $params['style'],
  1843. 'tabindex' => $params['tabindex'],
  1844. 'attributes' => $params['attributes'],
  1845. );
  1846. $use_input_type = (isset($params['use_input']) AND $params['use_input'] === FALSE) ? FALSE : TRUE;
  1847. return $this->form->button($params['value'], $params['name'], $attrs, $use_input_type);
  1848. }
  1849. // --------------------------------------------------------------------
  1850. /**
  1851. * Creates the either select field or a set of radio buttons
  1852. *
  1853. * If set to auto and their are less than 2 options, then it will render
  1854. * radio inputs. Otherwise it will render a select input
  1855. *
  1856. * @access public
  1857. * @param array fields parameters
  1858. * @return string
  1859. */
  1860. public function create_enum($params)
  1861. {
  1862. $defaults = array(
  1863. 'checked' => FALSE, // for radio
  1864. 'options' => array(),
  1865. 'mode' => NULL,
  1866. 'model' => NULL,
  1867. 'wrapper_tag' => 'span',// for checkboxes
  1868. 'wrapper_class' => 'multi_field',
  1869. 'spacer' => "&nbsp;&nbsp;&nbsp;",
  1870. 'enum_params' => array(),
  1871. );
  1872. $params = $this->normalize_params($params, $defaults);
  1873. $i = 0;
  1874. $str = '';
  1875. $mode = (!empty($params['mode'])) ? $params['mode'] : $this->single_select_mode;
  1876. if ($mode == 'radios' OR ($mode == 'auto' AND count($params['options']) <= 2))
  1877. {
  1878. $default = (isset($params['value'])) ? $params['value'] : FALSE;
  1879. foreach($params['options'] as $key => $val)
  1880. {
  1881. $v = (!empty($params['equalize_key_value']) AND is_int($key)) ? $val : $key;
  1882. $attrs['data']['orig_checked'] = '0';
  1883. $str .= '<'.$params['wrapper_tag'].' class="'.$params['wrapper_class'].'">';
  1884. $attrs = array(
  1885. 'class' => $params['class'],
  1886. 'readonly' => $params['readonly'],
  1887. 'disabled' => $params['disabled'],
  1888. 'style' => $params['style'],
  1889. 'tabindex' => ((is_array($params['tabindex']) AND isset($params['tabindex'][$i])) ? $params['tabindex'][$i] : NULL),
  1890. 'data' => $params['data'],
  1891. );
  1892. if (empty($params['null']) OR (!empty($params['null']) AND (!empty($params['default']) OR !empty($params['value']))))
  1893. {
  1894. if (($i == 0 AND !$default) OR ($default == $v))
  1895. {
  1896. $attrs['checked'] = 'checked';
  1897. $attrs['data']['orig_checked'] = '1';
  1898. }
  1899. }
  1900. $str .= $this->form->radio($params['name'], $v, $attrs);
  1901. $name = Form::create_id($params['orig_name']);
  1902. //$str .= ' <label for="'.$name.'_'.str_replace(' ', '_', $key).'">'.$val.'</label>';
  1903. $enum_name = $name.'_'.Form::create_id($v);
  1904. $label = ($lang = $this->label_lang($enum_name)) ? $lang : $val;
  1905. if (!empty($this->name_prefix))
  1906. {
  1907. $enum_name = $this->name_prefix.'--'.$enum_name;
  1908. }
  1909. $enum_params = array('label' => $label, 'name' => $enum_name);
  1910. if (!empty($params['enum_params']) AND is_array($params['enum_params'])) {
  1911. $enum_params = array_merge($enum_params, $params['enum_params']);
  1912. }
  1913. $str .= ' '.$this->create_label($enum_params);
  1914. $str .= $params['spacer'];
  1915. $str .= '</'.$params['wrapper_tag'].'>';
  1916. $i++;
  1917. }
  1918. }
  1919. else
  1920. {
  1921. if (!isset($params['equalize_key_value']))
  1922. {
  1923. $params['equalize_key_value'] = TRUE;
  1924. }
  1925. $str .= $this->create_select($params);
  1926. }
  1927. return $str;
  1928. }
  1929. // --------------------------------------------------------------------
  1930. /**
  1931. * Creates the multi select input for the form (this is overwritten by the Fuel_custom_fields to give more functionality)
  1932. *
  1933. * @access public
  1934. * @param array fields parameters
  1935. * @return string
  1936. */
  1937. public function create_multi($params)
  1938. {
  1939. $defaults = array(
  1940. 'options' => array(),
  1941. 'mode' => NULL,
  1942. 'wrapper_tag' => 'span',// for checkboxes
  1943. 'wrapper_class' => 'multi_field',
  1944. 'spacer' => "&nbsp;&nbsp;&nbsp;",
  1945. 'enum_params' => array(),
  1946. );
  1947. $params = $this->normalize_params($params, $defaults);
  1948. $str = '';
  1949. $mode = (!empty($params['mode'])) ? $params['mode'] : $this->multi_select_mode;
  1950. if ($mode == 'checkbox' OR ($mode == 'auto' AND (isset($params['options']) AND count($params['options']) <= 5)))
  1951. {
  1952. $value = (isset($params['value'])) ? (array)$params['value'] : array();
  1953. $params['name'] = $params['name'].'[]';
  1954. $i = 1;
  1955. if (!empty($params['options']))
  1956. {
  1957. foreach($params['options'] as $key => $val)
  1958. {
  1959. $v = (!empty($params['equalize_key_value']) AND is_int($key)) ? $val : $key;
  1960. $tabindex_id = $i -1;
  1961. $str .= '<'.$params['wrapper_tag'].' class="'.$params['wrapper_class'].'">';
  1962. $attrs = array(
  1963. 'readonly' => $params['readonly'],
  1964. 'disabled' => $params['disabled'],
  1965. 'id' => Form::create_id($params['name']).$i,
  1966. 'style' => '', // to overwrite any input width styles
  1967. 'tabindex' => ((is_array($params['tabindex']) AND isset($params['tabindex'][$i - 1])) ? $params['tabindex'][$i - 1] : NULL),
  1968. 'attributes' => $params['attributes'],
  1969. 'data' => $params['data'],
  1970. );
  1971. if (in_array($key, $value))
  1972. {
  1973. $attrs['checked'] = 'checked';
  1974. }
  1975. $str .= $this->form->checkbox($params['name'], $v, $attrs);
  1976. $label = ($lang = $this->label_lang($attrs['id'])) ? $lang : $val;
  1977. $enum_params = array('label' => $label, 'name' => $attrs['id']);
  1978. if (!empty($params['enum_params']) AND is_array($params['enum_params'])) {
  1979. $enum_params = array_merge($enum_params, $params['enum_params']);
  1980. }
  1981. $str .= ' '.$this->create_label($enum_params);
  1982. $str .= $params['spacer'];
  1983. $str .= '</'.$params['wrapper_tag'].'>';
  1984. $i++;
  1985. }
  1986. }
  1987. }
  1988. else
  1989. {
  1990. $params['multiple'] = TRUE;
  1991. $str .= $this->create_select($params);
  1992. }
  1993. return $str;
  1994. }
  1995. // --------------------------------------------------------------------
  1996. /**
  1997. * Creates the file input for the form
  1998. *
  1999. * @access public
  2000. * @param array fields parameters
  2001. * @return string
  2002. */
  2003. public function create_file($params)
  2004. {
  2005. $defaults = array(
  2006. 'overwrite' => NULL, // sets a paramter to either overwrite or create a new file if one already exists on the server
  2007. 'display_overwrite' => TRUE, // displays the overwrite checkbox
  2008. 'accept' => '', // specifies which files are acceptable to upload
  2009. 'upload_path' => NULL, // the server path to upload the file to
  2010. 'folder' => NULL, // the folder name relative to the assets folder to upload the file
  2011. 'file_name' => NULL, // for file uploading
  2012. 'encrypt_name' => NULL, // determines whether to encrypt the uploaded file name to give it a unique value
  2013. );
  2014. $params = $this->normalize_params($params, $defaults);
  2015. $attrs = array(
  2016. 'id' => $params['id'],
  2017. 'class' => $params['class'],
  2018. 'readonly' => $params['readonly'],
  2019. 'disabled' => $params['disabled'],
  2020. 'required' => (!empty($params['required']) ? TRUE : NULL),
  2021. 'accept' => str_replace('|', ',', $params['accept']),
  2022. 'tabindex' => $params['tabindex'],
  2023. 'attributes' => $params['attributes'],
  2024. );
  2025. if (is_array($this->form_attrs))
  2026. {
  2027. $this->form_attrs['enctype'] = 'multipart/form-data';
  2028. }
  2029. else if (is_string($this->form_attrs) AND strpos($this->form_attrs, 'enctype') === FALSE)
  2030. {
  2031. $this->form_attrs .= ' enctype="multipart/form-data"';
  2032. }
  2033. $file = $this->form->file($params['name'], $attrs);
  2034. if (isset($params['overwrite']))
  2035. {
  2036. $overwrite = ($params['overwrite'] == 1 OR $params['overwrite'] === TRUE OR $params['overwrite'] === 'yes' OR $params['overwrite'] === 'y') ? '1' : '0';
  2037. if (!empty($params['display_overwrite']))
  2038. {
  2039. $file .= ' &nbsp; <span class="overwrite_field">'.$this->form->checkbox($params['name'].'_overwrite', 1, Form::do_checked($overwrite));
  2040. $file .= ' '. $this->create_label($this->label_lang('overwrite')).'</span>';
  2041. }
  2042. else
  2043. {
  2044. $file .= $this->form->hidden($params['name'].'_overwrite', $overwrite);
  2045. }
  2046. }
  2047. if (isset($params['upload_path']) OR isset($params['folder']))
  2048. {
  2049. if (isset($params['folder']))
  2050. {
  2051. $upload_path = $this->CI->encryption->encrypt(assets_server_path($params['folder']));
  2052. }
  2053. else
  2054. {
  2055. $upload_path = $this->CI->encryption->encrypt($params['upload_path']);
  2056. }
  2057. $file .= $this->form->hidden($params['name'].'_upload_path', $upload_path, 'class="noclear"');
  2058. }
  2059. if (isset($params['file_name']) OR isset($params['filename']))
  2060. {
  2061. $file_name = (isset($params['file_name'])) ? $params['file_name'] : $params['filename'];
  2062. $file .= $this->form->hidden($params['name'].'_file_name', $file_name, 'class="noclear"');
  2063. }
  2064. if (isset($params['encrypt']) OR isset($params['encrypt_name']))
  2065. {
  2066. $encrypt_name = (isset($params['encrypt_name'])) ? $params['encrypt_name'] : $params['encrypt'];
  2067. $file .= $this->form->hidden($params['name'].'_encrypt_name', $encrypt_name, 'class="noclear"');
  2068. }
  2069. return $file;
  2070. }
  2071. // --------------------------------------------------------------------
  2072. /**
  2073. * Creates the date input for the form
  2074. *
  2075. * Adds the datepicker so that you can use jquery to
  2076. * add the datepicker jquery plugin
  2077. *
  2078. * @access public
  2079. * @param array fields parameters
  2080. * @return string
  2081. */
  2082. public function create_date($params)
  2083. {
  2084. if (empty($params['date_format']))
  2085. {
  2086. if (empty($params['date_format']))
  2087. {
  2088. $params['date_format'] = $this->CI->config->item('date_format');
  2089. }
  2090. else
  2091. {
  2092. $params['date_format'] = $this->date_format;
  2093. }
  2094. }
  2095. $defaults = array(
  2096. 'date_format' => '',
  2097. 'region' => '',
  2098. 'min_date' => date($params['date_format'], strtotime('01/01/2000')),
  2099. 'max_date' => date($params['date_format'], strtotime('12/31/2030')),
  2100. 'first_day' => 0,
  2101. 'show_on' => 'button',
  2102. 'button_image' => NULL,
  2103. );
  2104. $params = $this->normalize_params($params, $defaults);
  2105. // check date to format it
  2106. if ((!empty($params['value']) AND (int) $params['value'] != 0)
  2107. && (preg_match("#([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})#", $params['value'], $regs1)
  2108. || preg_match("#([0-9]{1,2})[/\-\.]([0-9]{1,2})[/\-\.]([0-9]{4})#", $params['value'], $regs2)))
  2109. {
  2110. $params['value'] = date($params['date_format'], strtotime($params['value']));
  2111. } else {
  2112. $params['value'] = '';
  2113. }
  2114. $params['maxlength'] = 10;
  2115. $params['size'] = 12; // extra room for cramped styling
  2116. // create the right format for placeholder display based on the date format
  2117. $date_arr = preg_split('#-|/|\.#', $params['date_format']);
  2118. $format = '';
  2119. // order counts here!
  2120. $format = str_replace('m', 'mm', $params['date_format']);
  2121. $format = str_replace('n', 'm', $format);
  2122. $format = str_replace('d', 'dd', $format);
  2123. $format = str_replace('j', 'd', $format);
  2124. $format = str_replace('y', 'yy', $format);
  2125. $format = str_replace('Y', 'yyyy', $format);
  2126. // set data parameters to be used by javascript
  2127. $params['data'] = array();
  2128. if (empty($params['region']))
  2129. {
  2130. $params['data']['date_format'] = str_replace('yyyy', 'yy', $format);
  2131. }
  2132. else
  2133. {
  2134. $params['data']['region'] = $params['region'];
  2135. }
  2136. $params['data']['min_date'] = $params['min_date'];
  2137. $params['data']['max_date'] = $params['max_date'];
  2138. $params['data']['first_day'] = $params['first_day'];
  2139. $params['data']['show_on'] = $params['show_on'];
  2140. if (!empty($params['button_image']))
  2141. {
  2142. $params['data']['button_image'] = $params['button_image'];
  2143. }
  2144. $params['placeholder'] = $format;
  2145. $params['type'] = 'text';
  2146. return $this->create_text($params);
  2147. }
  2148. // --------------------------------------------------------------------
  2149. /**
  2150. * Creates the time input for the form
  2151. *
  2152. * @access public
  2153. * @param array fields parameters
  2154. * @return string
  2155. */
  2156. public function create_time($params)
  2157. {
  2158. $params = $this->normalize_params($params);
  2159. if (!isset($params['ampm']))
  2160. {
  2161. $params['ampm'] = TRUE;
  2162. }
  2163. $key = (isset($params['subkey'])) ? 'subkey' : 'key';
  2164. if (!empty($params['value']) AND is_numeric(substr($params['value'], 0, 1)) AND $params['value'] != '0000-00-00 00:00:00')
  2165. {
  2166. $hour_format = ($params['ampm']) ? 'g' : 'G';
  2167. $time_params['value'] = date($hour_format, strtotime($params['value']));
  2168. }
  2169. $time_params['size'] = 3;
  2170. $time_params['maxlength'] = 2;
  2171. $field_name = (empty($params['is_datetime'])) ? $params[$key] : $params[$key].'_hour';
  2172. $time_params['name'] = str_replace($params[$key], $field_name, $params['orig_name']);
  2173. $time_params['class'] = 'datepicker_hh';
  2174. $time_params['disabled'] = $params['disabled'];
  2175. $time_params['readonly'] = $params['readonly'];
  2176. $time_params['placeholder'] = 'hh';
  2177. if (isset($params['tabindex'][0]))
  2178. {
  2179. $time_params['tabindex'] = $params['tabindex'][0];
  2180. }
  2181. $str = $this->create_text($this->normalize_params($time_params));
  2182. $str .= ":";
  2183. if (!empty($params['value']) AND is_numeric(substr($params['value'], 0, 1)) AND $params['value'] != '0000-00-00 00:00:00') $time_params['value'] = date('i', strtotime($params['value']));
  2184. $time_params['name'] = str_replace($params[$key], $params[$key].'_min', $params['orig_name']);
  2185. $time_params['class'] = 'datepicker_mm';
  2186. $time_params['placeholder'] = 'mm';
  2187. if (isset($params['tabindex'][1]))
  2188. {
  2189. $time_params['tabindex'] = $params['tabindex'][1];
  2190. }
  2191. $str .= $this->create_text($this->normalize_params($time_params));
  2192. if (!empty($params['ampm']))
  2193. {
  2194. $ampm_params['options'] = array('am' => 'am', 'pm' => 'pm');
  2195. $ampm_params['name'] = str_replace($params[$key], $params[$key].'_am_pm', $params['orig_name']);
  2196. $ampm_params['value'] = (!empty($params['value']) AND is_numeric(substr($params['value'], 0, 1)) AND date('H', strtotime($params['value'])) >= 12) ? 'pm' : 'am';
  2197. $ampm_params['disabled'] = $params['disabled'];
  2198. $ampm_params['readonly'] = $params['readonly'];
  2199. if (isset($params['tabindex']) AND is_array($params['tabindex']))
  2200. {
  2201. array_shift($params['tabindex']);
  2202. array_shift($params['tabindex']);
  2203. $ampm_params['tabindex'] = $params['tabindex'];
  2204. }
  2205. $str .= $this->create_enum($this->normalize_params($ampm_params));
  2206. }
  2207. $process_key = $params[$key];
  2208. // needed for post processing
  2209. if (!empty($_POST) AND !isset($_POST[$params['key']]))
  2210. {
  2211. $_POST[$time_params['name']] = '';
  2212. }
  2213. if (empty($params['is_datetime']))
  2214. {
  2215. // create post processer to recreate date value
  2216. $func = function($value) use ($process_key){
  2217. if (is_array($value))
  2218. {
  2219. foreach($value as $key => $val)
  2220. {
  2221. $hr = (isset($val[$process_key]) AND (int)$val[$process_key] > 0 AND (int)$val[$process_key] < 24) ? $val[$process_key] : "";
  2222. $min = (isset($val[$process_key.'_min']) AND is_numeric($val[$process_key.'_min'])) ? $val[$process_key.'_min'] : "00";
  2223. $ampm = (isset($val[$process_key.'_am_pm']) AND $hr AND $min) ? $val[$process_key.'_am_pm'] : "";
  2224. if (!empty($ampm) AND !empty($hr) AND $hr > 12)
  2225. {
  2226. if ($hr > 24)
  2227. {
  2228. $hr = "00";
  2229. }
  2230. else
  2231. {
  2232. $hr = (int) $hr - 12;
  2233. $ampm = "pm";
  2234. }
  2235. }
  2236. if (empty($hr))
  2237. {
  2238. $hr = "00";
  2239. }
  2240. $dateval = $hr.":".$min.$ampm;
  2241. $value[$key][$process_key] = date("H:i:s", strtotime($dateval));
  2242. }
  2243. return $value;
  2244. }
  2245. else
  2246. {
  2247. $hr = (isset($_POST[$process_key]) AND (int)$_POST[$process_key] > 0 AND (int)$_POST[$process_key] < 24) ? $_POST[$process_key] : "";
  2248. $min = (isset($_POST[$process_key.'_min']) AND is_numeric($_POST[$process_key.'_min'])) ? $_POST[$process_key.'_min'] : "00";
  2249. $ampm = (isset($_POST[$process_key.'_am_pm']) AND $hr AND $min) ? $_POST[$process_key.'_am_pm'] : "";
  2250. if (!empty($ampm) AND !empty($hr) AND $hr > 12)
  2251. {
  2252. if ($hr > 24)
  2253. {
  2254. $hr = "00";
  2255. }
  2256. else
  2257. {
  2258. $hr = (int) $hr - 12;
  2259. $ampm = "pm";
  2260. }
  2261. }
  2262. if (empty($hr))
  2263. {
  2264. $hr = "00";
  2265. }
  2266. $dateval = $hr.":".$min.$ampm;
  2267. $dateval = date("H:i:s", strtotime($dateval));
  2268. return $dateval;
  2269. }
  2270. };
  2271. $this->set_post_process($params['key'], $func);
  2272. }
  2273. return $str;
  2274. }
  2275. // --------------------------------------------------------------------
  2276. /**
  2277. * Creates the date/time input for the form
  2278. *
  2279. * @access public
  2280. * @param array fields parameters
  2281. * @return string
  2282. */
  2283. public function create_datetime($params)
  2284. {
  2285. $date_params = $params;
  2286. if (isset($params['tabindex']) AND is_array($params['tabindex']))
  2287. {
  2288. $date_params['tabindex'] = current($params['tabindex']);
  2289. }
  2290. $str = $this->create_date($date_params);
  2291. $str .= ' ';
  2292. $params['is_datetime'] = TRUE;
  2293. if (!isset($params['ampm']))
  2294. {
  2295. $params['ampm'] = TRUE;
  2296. }
  2297. $time_params = $params;
  2298. if (isset($params['tabindex']) AND is_array($params['tabindex']))
  2299. {
  2300. array_shift($time_params['tabindex']);
  2301. }
  2302. $str .= $this->create_time($time_params);
  2303. $process_key = (isset($params['subkey'])) ? $params['subkey'] : $params['key'];
  2304. $func = function($value) use ($process_key) {
  2305. if (is_array($value))
  2306. {
  2307. foreach($value as $key => $val)
  2308. {
  2309. if (isset($val[$process_key]))
  2310. {
  2311. $date = (!empty($val[$process_key]) AND is_date_format($val[$process_key])) ? current(explode(" ", $val[$process_key])) : "";
  2312. $hr = (!empty($val[$process_key.'_hour']) AND (int)$val[$process_key.'_hour'] > 0 AND (int)$val[$process_key.'_hour'] < 24) ? $val[$process_key.'_hour'] : "";
  2313. $min = (!empty($val[$process_key.'_min']) AND is_numeric($val[$process_key.'_min'])) ? $val[$process_key.'_min'] : "00";
  2314. $ampm = (isset($val[$process_key.'_am_pm']) AND $hr AND $min) ? $val[$process_key.'_am_pm'] : "";
  2315. if (is_string($val[$process_key]))
  2316. {
  2317. $date = (!empty($val[$process_key]) AND is_date_format($val[$process_key])) ? current(explode(" ", $val[$process_key])) : "";
  2318. $hr = (!empty($val[$process_key.'_hour']) AND (int)$val[$process_key.'_hour'] > 0 AND (int)$val[$process_key.'_hour'] < 24) ? $val[$process_key.'_hour'] : "";
  2319. $min = (!empty($val[$process_key.'_min']) AND is_numeric($val[$process_key.'_min'])) ? $val[$process_key.'_min'] : "00";
  2320. $ampm = (isset($val[$process_key.'_am_pm']) AND $hr AND $min) ? $val[$process_key.'_am_pm'] : "";
  2321. if (!empty($ampm) AND !empty($hr) AND $hr > 12)
  2322. {
  2323. if ($hr > 24)
  2324. {
  2325. $hr = "00";
  2326. }
  2327. else
  2328. {
  2329. $hr = (int) $hr - 12;
  2330. $ampm = "pm";
  2331. }
  2332. }
  2333. $dateval = current(explode(" ", $value[$key][$process_key]));
  2334. if ($date != "")
  2335. {
  2336. if (!empty($hr)) $dateval .= " ".$hr.":".$min.$ampm;
  2337. }
  2338. if (!empty($dateval))
  2339. {
  2340. $value[$key][$process_key] = $dateval;
  2341. }
  2342. }
  2343. else if (is_array($val[$process_key]) AND isset($val[$process_key][$params['name']]))
  2344. {
  2345. $date = (!empty($val[$process_key][$params['name']]) AND is_date_format($val[$process_key][$params['name']])) ? current(explode(" ", $val[$process_key][$params['name']])) : "";
  2346. $hr = (isset($val[$process_key][$params['name'].'_hour']) AND (int)$val[$process_key][$params['name'].'_hour'] >= 0 AND (int)$val[$process_key][$params['name'].'_hour'] < 24) ? $val[$process_key][$params['name'].'_hour'] : "";
  2347. $min = (!empty($val[$process_key][$params['name'].'_min']) AND is_numeric($val[$process_key][$params['name'].'_min'])) ? $val[$process_key][$params['name'].'_min'] : "00";
  2348. $ampm = (isset($val[$process_key][$params['name'].'_am_pm']) AND $hr AND $min) ? $val[$process_key][$params['name'].'_am_pm'] : "";
  2349. if (!empty($ampm) AND !empty($hr) AND $hr > 12)
  2350. {
  2351. if ($hr > 24)
  2352. {
  2353. $hr = "12";
  2354. $ampm = "am";
  2355. }
  2356. else
  2357. {
  2358. $hr = (int) $hr - 12;
  2359. $ampm = "pm";
  2360. }
  2361. }
  2362. elseif ((int) $hr === 0)
  2363. {
  2364. $hr = "12";
  2365. $ampm = "am";
  2366. }
  2367. $dateval = current(explode(" ", $value[$key][$process_key][$params['name']]));
  2368. if ($date != "")
  2369. {
  2370. if ($hr !== "") $dateval .= " ".$hr.":".$min.$ampm;
  2371. }
  2372. if (!empty($dateval))
  2373. {
  2374. $value[$key][$process_key][$params['name']] = $dateval;
  2375. }
  2376. }
  2377. }
  2378. }
  2379. return $value;
  2380. }
  2381. else
  2382. {
  2383. $date = (!empty($_POST[$process_key]) AND is_date_format($_POST[$process_key])) ? current(explode(" ", $_POST[$process_key])) : "";
  2384. $hr = (isset($_POST[$process_key.'_hour']) AND (int)$_POST[$process_key.'_hour'] >= 0 AND (int)$_POST[$process_key.'_hour'] < 24) ? $_POST[$process_key.'_hour'] : "";
  2385. $min = (!empty($_POST[$process_key.'_min']) AND is_numeric($_POST[$process_key.'_min'])) ? $_POST[$process_key.'_min'] : "00";
  2386. $ampm = (isset($_POST[$process_key.'_am_pm']) AND $hr AND $min) ? $_POST[$process_key.'_am_pm'] : "";
  2387. if (!empty($ampm) AND !empty($hr) AND $hr > 12)
  2388. {
  2389. if ($hr > 24)
  2390. {
  2391. $hr = "12";
  2392. $ampm = "am";
  2393. }
  2394. else
  2395. {
  2396. $hr = (int) $hr - 12;
  2397. $ampm = "pm";
  2398. }
  2399. }
  2400. elseif ((int) $hr === 0)
  2401. {
  2402. $hr = "12";
  2403. $ampm = "am";
  2404. }
  2405. $dateval = $value;
  2406. if ($date != "")
  2407. {
  2408. $dateval = $date;
  2409. if ($hr !== "") $dateval .= " ".$hr.":".$min.$ampm;
  2410. if (!empty($dateval))
  2411. {
  2412. $dateval = date("Y-m-d H:i:s", strtotime($dateval));
  2413. }
  2414. }
  2415. return $dateval;
  2416. }
  2417. };
  2418. $this->set_post_process($params['key'], $func);
  2419. return $str;
  2420. }
  2421. // --------------------------------------------------------------------
  2422. /**
  2423. * Creates a number field for the form... basically a text field
  2424. *
  2425. * @access public
  2426. * @param array fields parameters
  2427. * @return string
  2428. */
  2429. public function create_number($params)
  2430. {
  2431. $defaults = array(
  2432. 'min' => '0', // sets the minimum number that can be entered
  2433. 'max' => NULL, // sets the maximum number that can be entered
  2434. 'step' => NULL, // specifies the increment that gets applied when pressing the up/down increment arrows
  2435. 'decimal' => 0, // determines whether to allow for decimal numbers
  2436. 'negative' => 0, // determines whether to allow for negative numbers
  2437. );
  2438. $params = $this->normalize_params($params, $defaults);
  2439. $attrs = array(
  2440. 'id' => $params['id'],
  2441. 'class' => $params['class'],
  2442. 'readonly' => $params['readonly'],
  2443. 'disabled' => $params['disabled'],
  2444. 'required' => (!empty($params['required']) ? TRUE : NULL),
  2445. 'min' => (isset($params['min']) ? $params['min'] : NULL),
  2446. 'max' => (isset($params['max']) ? $params['max'] : NULL),
  2447. 'step' => (isset($params['step']) ? $params['step'] : NULL),
  2448. 'data' => $params['data'],
  2449. 'style' => $params['style'],
  2450. 'tabindex' => $params['tabindex'],
  2451. 'attributes' => $params['attributes'],
  2452. );
  2453. // force to a string so that it doesn't go to negative number if the value is set to 0
  2454. setType($attrs['min'], 'string');
  2455. $numeric_class = 'numeric';
  2456. $attrs['class'] = (!empty($params['class'])) ? $params['class'].' '.$numeric_class : $numeric_class;
  2457. $params['type'] = 'number';
  2458. $decimal = (!empty($params['decimal'])) ? (int) $params['decimal'] : 0;
  2459. $negative = (!empty($params['negative'])) ? 1 : 0;
  2460. // invalid HTML
  2461. // if (empty($params['size']))
  2462. // {
  2463. // $attrs['size'] = 10;
  2464. // }
  2465. // if (empty($params['maxlength']))
  2466. // {
  2467. // $attrs['maxlength'] = 10;
  2468. // }
  2469. // set data values for jquery plugin to use
  2470. $attrs['data'] = array(
  2471. 'decimal' => $decimal,
  2472. 'negative' => $negative,
  2473. );
  2474. return $this->form->input($params['name'], $params['type'], $params['value'], $attrs);
  2475. }
  2476. // --------------------------------------------------------------------
  2477. /**
  2478. * Creates an email field for the form... supported by modern browsers
  2479. *
  2480. * @access public
  2481. * @param array fields parameters
  2482. * @return string
  2483. */
  2484. public function create_email($params)
  2485. {
  2486. $params['attrs']['attributes'] = (isset($params['attributes'])) ? $params['attributes'] : '';
  2487. $params['type'] = 'email';
  2488. if (strpos($params['attrs']['attributes'], 'class=') === FALSE)
  2489. {
  2490. $email_class = 'email';
  2491. $params['class'] = (!empty($params['class'])) ? $params['class'].' '.$email_class : $email_class;
  2492. }
  2493. return $this->create_text($params);
  2494. }
  2495. // --------------------------------------------------------------------
  2496. /**
  2497. * Creates a phone field for the form... supported by modern browsers
  2498. *
  2499. * @access public
  2500. * @param array fields parameters
  2501. * @return string
  2502. */
  2503. public function create_phone($params)
  2504. {
  2505. $params['attrs']['attributes'] = (isset($params['attributes'])) ? $params['attributes'] : '';
  2506. $params['type'] = 'tel';
  2507. if (strpos($params['attrs']['attributes'], 'class=') === FALSE)
  2508. {
  2509. $email_class = 'phone';
  2510. $params['class'] = (!empty($params['class'])) ? $params['class'].' '.$email_class : $email_class;
  2511. }
  2512. return $this->create_text($params);
  2513. }
  2514. // --------------------------------------------------------------------
  2515. /**
  2516. * Creates a range field for the form... supported by modern browsers
  2517. *
  2518. * @access public
  2519. * @param array fields parameters
  2520. * @return string
  2521. */
  2522. public function create_range($params)
  2523. {
  2524. $params['type'] = 'range';
  2525. $params['attrs'] = array();
  2526. $params['attrs']['min'] = (isset($params['min'])) ? $params['min'] : 0;
  2527. $params['attrs']['max'] = (isset($params['max'])) ? $params['max'] : 10;
  2528. $params['attrs']['attributes'] = (isset($params['attributes'])) ? $params['attributes'] : '';
  2529. if (strpos($params['attrs']['attributes'], 'class=') === FALSE)
  2530. {
  2531. $email_class = 'range';
  2532. $params['class'] = (!empty($params['class'])) ? $params['class'].' '.$email_class : $email_class;
  2533. }
  2534. return $this->create_text($params);
  2535. }
  2536. // --------------------------------------------------------------------
  2537. /**
  2538. * Creates either a checkbox or a radio input for the form
  2539. *
  2540. * This method check the boolean_mode class attribute to determine
  2541. * what type of field, either checkbox or radio, to render
  2542. *
  2543. * @access public
  2544. * @param array fields parameters
  2545. * @return string
  2546. */
  2547. public function create_boolean($params)
  2548. {
  2549. $mode = (!empty($params['mode'])) ? $params['mode'] : $this->boolean_mode;
  2550. if ($mode == 'checkbox')
  2551. {
  2552. return $this->create_checkbox($params);
  2553. }
  2554. else
  2555. {
  2556. return $this->create_enum($params);
  2557. }
  2558. }
  2559. // --------------------------------------------------------------------
  2560. /**
  2561. * Creates a section in the form
  2562. *
  2563. * First checks the value, then the label, then the name attribute
  2564. * then wraps it in the section_tag
  2565. *
  2566. * @access public
  2567. * @param array fields parameters
  2568. * @return string
  2569. */
  2570. public function create_section($params)
  2571. {
  2572. $params = $this->normalize_params($params);
  2573. $section = $this->simple_field_value($params);
  2574. $id = isset($params['id']) ? ' id="'.$params['id'].'"' : '';
  2575. $class = isset($params['class']) ? ' class="'.$params['class'].'"' : '';
  2576. $tag = (empty($params['tag'])) ? $this->section_tag : $params['tag'];
  2577. return '<'.$tag.$id.$class.'>'.$section.'</'.$tag.'>';
  2578. }
  2579. // --------------------------------------------------------------------
  2580. /**
  2581. * Creates a copy area for for the form
  2582. *
  2583. * First checks the value, then the label, then the name attribute
  2584. * then wraps it in the copy_tag
  2585. *
  2586. * @access public
  2587. * @param array fields parameters
  2588. * @return string
  2589. */
  2590. public function create_copy($params)
  2591. {
  2592. $params = $this->normalize_params($params);
  2593. $id = isset($params['id']) ? ' id="'.$params['id'].'"' : '';
  2594. $class = isset($params['class']) ? ' class="'.$params['class'].'"' : '';
  2595. $copy = $this->simple_field_value($params);
  2596. $tag = (empty($params['tag'])) ? $this->copy_tag : $params['tag'];
  2597. return '<'.$tag.$id.$class.'>'.$copy.'</'.$tag.'>';
  2598. }
  2599. // --------------------------------------------------------------------
  2600. /**
  2601. * Creates a tooltip on the label
  2602. *
  2603. * Creates a tooltip on the label. Uses the tooltip_format class attribute
  2604. * to determine how to render tooltip
  2605. *
  2606. * @access public
  2607. * @param array fields parameters
  2608. * @return string
  2609. */
  2610. public function create_tooltip($params)
  2611. {
  2612. $params = $this->normalize_params($params);
  2613. $str = '';
  2614. if (!empty($params['comment']))
  2615. {
  2616. $tooltip = $params['comment'];
  2617. }
  2618. else if (!empty($params['description']))
  2619. {
  2620. $tooltip = $params['description'];
  2621. }
  2622. if (!empty($tooltip))
  2623. {
  2624. $tooltip = htmlentities($tooltip, ENT_QUOTES, config_item('charset'));
  2625. $str = str_replace(array('{?}', '[?]'), array($tooltip, $params['label']), $this->tooltip_format);
  2626. }
  2627. else
  2628. {
  2629. $str = $params['label'];
  2630. }
  2631. return $str;
  2632. }
  2633. // --------------------------------------------------------------------
  2634. /**
  2635. * Creates a read only field
  2636. *
  2637. * @access public
  2638. * @param array fields parameters
  2639. * @return string
  2640. */
  2641. public function create_readonly($params)
  2642. {
  2643. $params = $this->normalize_params($params);
  2644. $str = $params['value']."\n".$this->create_hidden($params);
  2645. return $str;
  2646. }
  2647. // --------------------------------------------------------------------
  2648. /**
  2649. * Creates a legend for a form
  2650. *
  2651. * @access public
  2652. * @param array fields parameters
  2653. * @return string
  2654. */
  2655. public function create_fieldset($params)
  2656. {
  2657. $params = $this->normalize_params($params);
  2658. $attrs = array(
  2659. 'class' => $params['class'],
  2660. );
  2661. $str = '';
  2662. $legend = $this->simple_field_value($params);
  2663. $str = "\n";
  2664. if (isset($params['open']) AND $params['open'] === FALSE)
  2665. {
  2666. $str .= $this->form->fieldset_close();
  2667. }
  2668. else
  2669. {
  2670. $id = isset($params['id']) ? $params['id'] : '';
  2671. $str .= $this->form->fieldset_open($legend, $attrs, $id);
  2672. }
  2673. return $str;
  2674. }
  2675. // --------------------------------------------------------------------
  2676. /**
  2677. * Creates a nested form_builder object and renders it
  2678. *
  2679. * @access public
  2680. * @param array fields parameters
  2681. * @return string
  2682. */
  2683. public function create_nested($params, $return_object = FALSE)
  2684. {
  2685. $this->CI =& get_instance();
  2686. $this->CI->load->library('parser');
  2687. $fb_class = get_class($this);
  2688. if (empty($params['fields']) OR !is_array($params['fields']))
  2689. {
  2690. return '';
  2691. }
  2692. if (empty($params['init']))
  2693. {
  2694. $params['init'] = array();
  2695. }
  2696. if (empty($params['value']))
  2697. {
  2698. $params['value'] = array();
  2699. }
  2700. $form_builder = new $fb_class($params['init']);
  2701. $form_builder->set_fields($params['fields']);
  2702. $form_builder->submit_value = '';
  2703. $form_builder->cancel_value = '';
  2704. $form_builder->reset_value = '';
  2705. $form_builder->other_actions = '';
  2706. $form_builder->required_text = '';
  2707. $form_builder->name_prefix = $this->name_prefix;
  2708. $form_builder->name_array = $this->name_array;
  2709. $form_builder->custom_fields = $this->custom_fields;
  2710. $form_builder->representatives = $this->representatives;
  2711. $form_builder->set_validator($this->form->validator);
  2712. $form_builder->use_form_tag = FALSE;
  2713. $form_builder->set_field_values($params['value']);
  2714. $form_builder->no_css_js = TRUE; // used to prevent multiple loading of assets
  2715. $form_builder->_is_nested = TRUE; // used to detect if it is a nested form
  2716. $form_builder->auto_execute_js = FALSE;
  2717. // add accumulated js
  2718. $js = $form_builder->get_js();
  2719. $this->add_js($js);
  2720. // add accumulated css
  2721. $css = $form_builder->get_css();
  2722. $this->add_css($css);
  2723. if ($return_object)
  2724. {
  2725. return $form_builder;
  2726. }
  2727. $form = $form_builder->render();
  2728. return $form;
  2729. }
  2730. // --------------------------------------------------------------------
  2731. /**
  2732. * Creates a custom input form field
  2733. *
  2734. * Calls a function and passes it the field params
  2735. *
  2736. * @access public
  2737. * @param array fields parameters
  2738. * @return string
  2739. */
  2740. public function create_custom($func, $params)
  2741. {
  2742. $params = $this->normalize_params($params);
  2743. // give custom fields a reference to the current object
  2744. $params['instance'] =& $this;
  2745. if (!empty($params['js']))
  2746. {
  2747. $this->add_js($params['js'], $params['type']);
  2748. }
  2749. // render
  2750. return call_user_func($func, $params);
  2751. }
  2752. // --------------------------------------------------------------------
  2753. /**
  2754. * Adds multiple custom fields
  2755. *
  2756. * @access public
  2757. * @param array Array of custom fields to load
  2758. * @return void
  2759. */
  2760. public function load_custom_fields($file)
  2761. {
  2762. if (is_string($file))
  2763. {
  2764. if (file_exists($file))
  2765. {
  2766. include($file);
  2767. }
  2768. else
  2769. {
  2770. return FALSE;
  2771. }
  2772. }
  2773. else if (is_array($file))
  2774. {
  2775. $fields = $file;
  2776. }
  2777. if (!empty($fields) AND is_array($fields))
  2778. {
  2779. // setup custom fields
  2780. foreach($fields as $type => $custom)
  2781. {
  2782. $this->register_custom_field($type, $custom);
  2783. }
  2784. }
  2785. }
  2786. // --------------------------------------------------------------------
  2787. /**
  2788. * Add a custom field type
  2789. *
  2790. * @access public
  2791. * @param string key used to identify the field type
  2792. * @param string function or class/method array to use for rendering
  2793. * @return void
  2794. */
  2795. public function register_custom_field($key, $custom_field)
  2796. {
  2797. // if an array, then we will assess the properties of the array and load classes/helpers appropriately
  2798. if (is_array($custom_field))
  2799. {
  2800. $this->CI =& get_instance();
  2801. // must have at least a function value otherwise you get nada
  2802. if (empty($custom_field['function']))
  2803. {
  2804. if (method_exists($this, 'create_'.$key))
  2805. {
  2806. $custom_field['function'] = array($this, 'create_'.$key);
  2807. }
  2808. else
  2809. {
  2810. return FALSE;
  2811. }
  2812. }
  2813. // if there's a filepath, then load it and instantiate the class
  2814. if (!empty($custom_field['filepath']))
  2815. {
  2816. require_once($custom_field['filepath']);
  2817. if (!empty($custom_field['class']))
  2818. {
  2819. $class = new $custom_field['class']();
  2820. $func = array($class, $custom_field['function']);
  2821. }
  2822. else
  2823. {
  2824. $func = $custom_field['function'];
  2825. }
  2826. }
  2827. // if class parameter is set, then it will assume it's a library'
  2828. else if (!empty($custom_field['class']))
  2829. {
  2830. if (is_array($custom_field['class']))
  2831. {
  2832. $module = key($custom_field['class']);
  2833. $library = current($custom_field['class']);
  2834. $this->CI->load->module_library($module, $library);
  2835. }
  2836. else
  2837. {
  2838. $library = $custom_field['class'];
  2839. $this->CI->load->library($custom_field['class']);
  2840. }
  2841. $library_parts = explode('/', strtolower($library));
  2842. $library = end($library_parts);
  2843. $func = array($this->CI->$library, $custom_field['function']);
  2844. }
  2845. // if no class parameter is set, then it will assume it's a helper'
  2846. else
  2847. {
  2848. if (!is_callable($custom_field['function']))
  2849. {
  2850. if (is_array($custom_field['function']) AND is_string(key($custom_field['function'])))
  2851. {
  2852. $module = key($custom_field['function']);
  2853. $helper = current($custom_field['function']);
  2854. $this->CI->load->module_library($module, $helper, $custom_field);
  2855. }
  2856. else
  2857. {
  2858. $this->CI->load->helper($custom_field['function'], $custom_field);
  2859. }
  2860. }
  2861. $func = $custom_field['function'];
  2862. }
  2863. $params = $custom_field;
  2864. }
  2865. // if it's a simple function, then we'll just use this for rendering
  2866. else if (is_callable($custom_field))
  2867. {
  2868. $func = $custom_field;
  2869. }
  2870. else
  2871. {
  2872. return FALSE;
  2873. }
  2874. $params['render_func'] = $func;
  2875. $field = new Form_builder_field($params);
  2876. $this->custom_fields[$key] = $field;
  2877. // says whether this field can represent other field types
  2878. if (!empty($params['represents']))
  2879. {
  2880. $this->representatives[$key] = $params['represents'];
  2881. }
  2882. }
  2883. // --------------------------------------------------------------------
  2884. /**
  2885. * Sets the validator object on the form object
  2886. *
  2887. * The validator object is used to determine if the fields have been
  2888. * filled out properly and will display any errors at the top of the form
  2889. *
  2890. * @access public
  2891. * @param array fields parameters
  2892. * @return void
  2893. */
  2894. public function set_validator(&$validator)
  2895. {
  2896. $this->form->validator = $validator;
  2897. }
  2898. // --------------------------------------------------------------------
  2899. /**
  2900. * Returns the validator object on the form object
  2901. *
  2902. * The validator object is used to determine if the fields have been
  2903. * filled out properly and will display any errors at the top of the form
  2904. *
  2905. * @access public
  2906. * @return object
  2907. */
  2908. public function &get_validator()
  2909. {
  2910. return $this->form->validator;
  2911. }
  2912. // --------------------------------------------------------------------
  2913. /**
  2914. * Handles validation for the form builder fields
  2915. *
  2916. * If valid it will return TRUE. If not, it will return an array of errors
  2917. *
  2918. * @access public
  2919. * @param object the validator object to use for validating (optional)
  2920. * @return mixed
  2921. */
  2922. public function validate($validator = NULL)
  2923. {
  2924. if (empty($validator))
  2925. {
  2926. $validator = $this->form->validator;
  2927. }
  2928. if ( ! empty($_POST) AND (get_class($validator) == 'Validator'))
  2929. {
  2930. // $this->CI->load->library('validator');
  2931. $this->CI->load->helper('inflector');
  2932. $validator->reset();
  2933. foreach ($this->_fields as $field_name => $params)
  2934. {
  2935. $field_validations = array();
  2936. if (array_key_exists('validation', $params))
  2937. {
  2938. foreach ($params['validation'] as $rule => $args) {
  2939. $field_validations[$rule] = $args;
  2940. }
  2941. }
  2942. // add to validation only if it is in the $_POST
  2943. if (array_key_exists('validation_if_exists', $params))
  2944. {
  2945. if ($this->CI->input->post($field_name))
  2946. {
  2947. foreach ($params['validation_if_exists'] as $rule => $args) {
  2948. $field_validations[$rule] = $args;
  2949. }
  2950. }
  2951. }
  2952. // add required validation if it is set outside of the validation
  2953. if (array_key_exists('required', $params) AND $params['required']
  2954. AND ! array_key_exists('required', $field_validations)) {
  2955. $field_validations['required'] = '';
  2956. }
  2957. // add the rules
  2958. $field_value = $params['value'];
  2959. foreach ($field_validations as $rule => $args)
  2960. {
  2961. $field_label = humanize($field_name);
  2962. if (is_array($args) AND array_key_exists('message', $args))
  2963. {
  2964. $msg = $args['message'];
  2965. unset($args['message']);
  2966. }
  2967. else
  2968. {
  2969. $msg = "{$field_label} is {$rule}.";
  2970. }
  2971. $rule_params = array();
  2972. if ( ! empty($args) AND array_key_exists('params', $args) AND is_array($args['params'])) {
  2973. $rule_params = $args['params'];
  2974. }
  2975. $validation_val = ( ! empty($args['validation_val'])) ? $args['validation_val'] : $field_value;
  2976. array_unshift($rule_params, $validation_val);
  2977. $validator->add_rule($field_name, $rule, $msg, $rule_params);
  2978. }
  2979. }
  2980. $validator->validate();
  2981. $errors = $validator->get_errors();
  2982. if (empty($errors))
  2983. {
  2984. return TRUE;
  2985. }
  2986. else
  2987. {
  2988. return $validator->get_errors();
  2989. }
  2990. }
  2991. }
  2992. // --------------------------------------------------------------------
  2993. /**
  2994. * Sets the order of the fields
  2995. *
  2996. * An array value with the keys as the order for the field
  2997. *
  2998. * @access public
  2999. * @param array fields parameters
  3000. * @return string
  3001. */
  3002. public function set_field_order($order_arr = array())
  3003. {
  3004. // normalize
  3005. foreach($this->_fields as $key => $val)
  3006. {
  3007. $this->_fields[$key] = $this->normalize_params($val);
  3008. }
  3009. if (!empty($order_arr))
  3010. {
  3011. $num = count($order_arr) - 1;
  3012. for ($i = 0; $i < $num; $i++)
  3013. {
  3014. if (isset($this->_fields[$order_arr[$i]]))
  3015. {
  3016. $this->_fields[$order_arr[$i]]['order'] = $i;
  3017. }
  3018. }
  3019. }
  3020. $this->_fields = $this->_fields_sorter($this->_fields, 'order');
  3021. }
  3022. // --------------------------------------------------------------------
  3023. /**
  3024. * Appends HTML to the form
  3025. *
  3026. * Used to append HTML after the form... good for Javascript files
  3027. *
  3028. * @access public
  3029. * @param string HTML to append
  3030. * @return void
  3031. */
  3032. public function append_html($html)
  3033. {
  3034. $this->html_append .= $html;
  3035. }
  3036. // --------------------------------------------------------------------
  3037. /**
  3038. * Prepends HTML to the form
  3039. *
  3040. * Used to prepend HTML before the form... good for Javascript files
  3041. *
  3042. * @access public
  3043. * @param string HTML to prepend
  3044. * @return void
  3045. */
  3046. public function prepend_html($html)
  3047. {
  3048. $this->html_prepend .= $html;
  3049. }
  3050. // --------------------------------------------------------------------
  3051. /**
  3052. * Registers a pre process function for a field
  3053. *
  3054. * @access public
  3055. * @param string
  3056. * @param array
  3057. * @return void
  3058. */
  3059. public function set_pre_process($field, $func)
  3060. {
  3061. $this->_pre_process[$field][] = $func;
  3062. }
  3063. // --------------------------------------------------------------------
  3064. /**
  3065. * Registers a post process function for a field
  3066. *
  3067. * @access public
  3068. * @param string
  3069. * @param array
  3070. * @return void
  3071. */
  3072. public function set_post_process($field, $func)
  3073. {
  3074. $this->_post_process[$field][] = $func;
  3075. }
  3076. // --------------------------------------------------------------------
  3077. /**
  3078. * Alters all the field values that have pre_process attribute specified
  3079. *
  3080. * @access public
  3081. * @return void
  3082. */
  3083. public function pre_process_field_values()
  3084. {
  3085. $this->is_pre_processing = TRUE;
  3086. // combine field pre processes with those already set
  3087. foreach($this->_fields as $key => $field)
  3088. {
  3089. if (!empty($field['pre_process']))
  3090. {
  3091. $this->set_pre_process($key, $field['pre_process']);
  3092. }
  3093. }
  3094. if (is_array($this->_pre_process))
  3095. {
  3096. foreach($this->_pre_process as $key => $functions)
  3097. {
  3098. foreach($functions as $function)
  3099. {
  3100. $process = $this->_normalize_process_func($function, $this->_fields[$key]['value']);
  3101. $func = $process['func'];
  3102. $params = $process['params'];
  3103. $this->_fields[$key]['value'] = call_user_func_array($func, $params);
  3104. }
  3105. }
  3106. }
  3107. $this->is_pre_processing = FALSE;
  3108. }
  3109. // --------------------------------------------------------------------
  3110. /**
  3111. * Alters all the field post values that have post_process attribute specified
  3112. *
  3113. * @access public
  3114. * @return array
  3115. */
  3116. public function post_process_field_values($posted = array(), $set_post = TRUE)
  3117. {
  3118. $this->is_post_processing = TRUE;
  3119. $this->no_css_js = TRUE; // set no display so that it won't load the JS and CSS
  3120. // yes... we render the form which is strange, but it executes all the custom field types which may contain post_processing rules
  3121. $form = $this->render();
  3122. $this->no_css_js = FALSE; // then set it back to preven any issues with further use
  3123. if (empty($posted)) $posted = $_POST;
  3124. // combine field post processes with those already set
  3125. foreach($this->_fields as $key => $field)
  3126. {
  3127. if (!empty($field['post_process']) AND isset($posted[$key]))
  3128. {
  3129. $this->set_post_process($key, $field['post_process']);
  3130. }
  3131. }
  3132. if (is_array($this->_post_process))
  3133. {
  3134. foreach($this->_post_process as $key => $functions)
  3135. {
  3136. foreach($functions as $function)
  3137. {
  3138. if (isset($this->_fields[$key]))
  3139. {
  3140. if (isset($posted[$key]))
  3141. {
  3142. $process = $this->_normalize_process_func($function, $posted[$key]);
  3143. $func = $process['func'];
  3144. $params = $process['params'];
  3145. $posted[$key] = call_user_func_array($func, $params);
  3146. if ($set_post)
  3147. {
  3148. $_POST[$key] = $posted[$key];
  3149. }
  3150. }
  3151. }
  3152. }
  3153. }
  3154. }
  3155. $this->is_post_processing = FALSE;
  3156. return $posted;
  3157. }
  3158. // --------------------------------------------------------------------
  3159. /**
  3160. * Normalizes the processing function to be used in post/pre processing of a field
  3161. *
  3162. * @access protected
  3163. * @return array
  3164. */
  3165. protected function _normalize_process_func($process, $value)
  3166. {
  3167. $params = array($value);
  3168. if (is_array($process))
  3169. {
  3170. if (isset($process['func']))
  3171. {
  3172. $func = $process['func'];
  3173. // set any additional parameters
  3174. if (isset($process['params']))
  3175. {
  3176. $params = array_merge($params, $process['params']);
  3177. }
  3178. }
  3179. else
  3180. {
  3181. $func = current($process);
  3182. array_shift($process);
  3183. $params = array_merge($params, $process);
  3184. }
  3185. }
  3186. else
  3187. {
  3188. $func = $process;
  3189. }
  3190. // shorthand if the function name is remove or clear, then we return an empty string
  3191. if ($func == 'remove' OR $func == 'clear')
  3192. {
  3193. $func = function($value){ return ""; };
  3194. }
  3195. return array('func' => $func, 'params' => $params);
  3196. }
  3197. // --------------------------------------------------------------------
  3198. /**
  3199. * Sets a representative for a field type.
  3200. *
  3201. * This allows for one field type to represent several field types (e.g. number = int, bigint, smallint, tinyint,...etc)
  3202. *
  3203. * @access public
  3204. * @param string The field type to be the representative
  3205. * @param mixed Either an array or a regex that other fields must match
  3206. * @return void
  3207. */
  3208. public function set_representative($type, $match = '')
  3209. {
  3210. $this->representatives[$type] = $match;
  3211. }
  3212. // --------------------------------------------------------------------
  3213. /**
  3214. * Removes a representative for a field type.
  3215. *
  3216. * This removes a representative globally for all fields
  3217. *
  3218. * @access public
  3219. * @param string The field type to be the representative
  3220. * @return void
  3221. */
  3222. public function remove_representative($type)
  3223. {
  3224. unset($this->representatives[$type]);
  3225. }
  3226. // --------------------------------------------------------------------
  3227. /**
  3228. * Adds a js file to be rendered with the form a representative for a field type.
  3229. *
  3230. * @access public
  3231. * @param string A js file name
  3232. * @param mixed A key value to associate with the JS file (so it only gets loaded once). Or an associative array of keyed javascript file names
  3233. * @return void
  3234. */
  3235. public function add_js($js = NULL, $key = NULL)
  3236. {
  3237. if (is_null($this->js))
  3238. {
  3239. $this->js = array();
  3240. }
  3241. if (is_array($key))
  3242. {
  3243. foreach($key as $k => $j)
  3244. {
  3245. if (!in_array($j, $this->js))
  3246. {
  3247. $this->js[$k] = $j;
  3248. }
  3249. }
  3250. }
  3251. else
  3252. {
  3253. if (empty($key))
  3254. {
  3255. if (is_array($js))
  3256. {
  3257. $this->js = $this->js + $js;
  3258. }
  3259. else if (!in_array($js, $this->js))
  3260. {
  3261. $this->js[] = $js;
  3262. }
  3263. }
  3264. else if (!in_array($js, $this->js))
  3265. {
  3266. $this->js[$key] = $js;
  3267. }
  3268. }
  3269. }
  3270. // --------------------------------------------------------------------
  3271. /**
  3272. * Removes a javascript file
  3273. *
  3274. * @access public
  3275. * @param string A js file name or an array of file names
  3276. * @return void
  3277. */
  3278. public function remove_js($key = NULL)
  3279. {
  3280. if (is_null($key))
  3281. {
  3282. $this->js = array();
  3283. }
  3284. else if (is_array($key))
  3285. {
  3286. foreach($key as $k)
  3287. {
  3288. if (in_array($k, $this->js))
  3289. {
  3290. unset($this->js[$k]);
  3291. }
  3292. }
  3293. }
  3294. else if (isset($this->js[$k]))
  3295. {
  3296. unset($this->js[$k]);
  3297. }
  3298. }
  3299. // --------------------------------------------------------------------
  3300. /**
  3301. * Returns the javascript used for the form
  3302. *
  3303. * @access public
  3304. * @param string The key name of a javascript file used when adding
  3305. * @return array
  3306. */
  3307. public function get_js($js = NULL)
  3308. {
  3309. if (!empty($js))
  3310. {
  3311. return $this->js[$js];
  3312. }
  3313. return $this->js;
  3314. }
  3315. // --------------------------------------------------------------------
  3316. /**
  3317. * Adds a CSS file to be rendered with the form a representative for a field type.
  3318. *
  3319. * @access public
  3320. * @param string A CSS file name
  3321. * @param mixed A key value to associate with the CSS file (so it only gets loaded once). Or an associative array of keyed CSS file names
  3322. * @return void
  3323. */
  3324. public function add_css($css = NULL, $key = NULL)
  3325. {
  3326. if (is_null($this->css))
  3327. {
  3328. $this->css = array();
  3329. }
  3330. if (is_array($key))
  3331. {
  3332. foreach($key as $k => $c)
  3333. {
  3334. if (!in_array($c, $this->css))
  3335. {
  3336. $this->css[$k] = $c;
  3337. }
  3338. }
  3339. }
  3340. else
  3341. {
  3342. if (empty($key) AND !in_array($css, $this->css))
  3343. {
  3344. $this->css[] = $css;
  3345. }
  3346. else if (!in_array($css, $this->css))
  3347. {
  3348. $this->css[$key] = $css;
  3349. }
  3350. }
  3351. }
  3352. // --------------------------------------------------------------------
  3353. /**
  3354. * Returns the CSS used for the form
  3355. *
  3356. * @access public
  3357. * @param string The key name of a CSS file used when adding
  3358. * @return array
  3359. */
  3360. public function get_css($css = NULL)
  3361. {
  3362. if (!empty($css))
  3363. {
  3364. return $this->css[$css];
  3365. }
  3366. return $this->css;
  3367. }
  3368. // --------------------------------------------------------------------
  3369. /**
  3370. * Removes a css file
  3371. *
  3372. * @access public
  3373. * @param string A css file name or an array of file names
  3374. * @return void
  3375. */
  3376. public function remove_css($key = NULL)
  3377. {
  3378. if (is_null($key))
  3379. {
  3380. $this->css = array();
  3381. }
  3382. else if (is_array($key))
  3383. {
  3384. foreach($key as $k)
  3385. {
  3386. if (in_array($k, $this->css))
  3387. {
  3388. unset($this->css[$k]);
  3389. }
  3390. }
  3391. }
  3392. else if (isset($this->css[$k]))
  3393. {
  3394. unset($this->css[$k]);
  3395. }
  3396. }
  3397. // --------------------------------------------------------------------
  3398. /**
  3399. * Returns an array of options if a model is specified
  3400. *
  3401. * @access public
  3402. * @param mixed model, model/method or module/model/method
  3403. * @return array
  3404. */
  3405. public function options_from_model($model, $params = array())
  3406. {
  3407. // can't think of a good reason to grab options when post processing
  3408. // so will just return an empty array to speed things up on post processing
  3409. if ($this->is_post_processing)
  3410. {
  3411. return array();
  3412. }
  3413. if (is_array($model))
  3414. {
  3415. $val = current($model);
  3416. $module = key($model);
  3417. // if an array is specified for the value, then we assume the key is the model name and the value is the method
  3418. if (is_array($val))
  3419. {
  3420. $model = key($val);
  3421. $method = current($val);
  3422. }
  3423. else
  3424. {
  3425. $model = $val;
  3426. }
  3427. }
  3428. if (!isset($method))
  3429. {
  3430. $method = 'options_list'; // default method'
  3431. }
  3432. if (substr($model, strlen($model) - 6) != '_model')
  3433. {
  3434. $model = $model.'_model';
  3435. }
  3436. // if the key is a string, then we assume its the modules name and we load it form the module
  3437. if (isset($module))
  3438. {
  3439. $this->CI->load->module_model($module, $model);
  3440. }
  3441. // if an indexed array is specified, we assume that it is simply a model from the application folder and we will look for an options_list function
  3442. else
  3443. {
  3444. $this->CI->load->model($model);
  3445. }
  3446. $func = array($this->CI->$model, $method);
  3447. $options = call_user_func_array($func, $params);
  3448. return $options;
  3449. }
  3450. // --------------------------------------------------------------------
  3451. /**
  3452. * Looks for value, label, then name as the values
  3453. *
  3454. * @access public
  3455. * @param array fields parameters
  3456. * @return string
  3457. */
  3458. public function simple_field_value($params)
  3459. {
  3460. if (is_array($params))
  3461. {
  3462. if (!empty($params['value']))
  3463. {
  3464. $str = $params['value'];
  3465. }
  3466. else if (!empty($params['label']))
  3467. {
  3468. $str = $params['label'];
  3469. }
  3470. else if (!empty($params['name']))
  3471. {
  3472. $str = $params['name'];
  3473. }
  3474. }
  3475. else
  3476. {
  3477. $str = $params;
  3478. }
  3479. return $str;
  3480. }
  3481. // --------------------------------------------------------------------
  3482. /**
  3483. * Helper method that returns the language key value if it exist, otherwise it returns FALSE
  3484. *
  3485. * @access protected
  3486. * @param string
  3487. * @return string
  3488. */
  3489. public function label_lang($key)
  3490. {
  3491. if (isset($this->lang_prefix) AND function_exists('lang') AND $lang = lang($this->lang_prefix.$key))
  3492. {
  3493. return $lang;
  3494. }
  3495. return FALSE;
  3496. }
  3497. // --------------------------------------------------------------------
  3498. /**
  3499. * Returns a boolean value as to whether the rendered form_builder instance is nested or not
  3500. *
  3501. * @access protected
  3502. * @return boolean
  3503. */
  3504. public function is_nested()
  3505. {
  3506. return $this->_is_nested;
  3507. }
  3508. // --------------------------------------------------------------------
  3509. /**
  3510. * Sorts the fields for the form
  3511. *
  3512. * Same as the MY_array_helper array_sorter function
  3513. *
  3514. * @access protected
  3515. * @param array fields parameters
  3516. * @return string
  3517. */
  3518. protected function _fields_sorter($array, $index, $order = 'asc', $nat_sort = FALSE, $case_sensitive = FALSE)
  3519. {
  3520. if (is_array($array) AND count($array) > 0)
  3521. {
  3522. foreach (array_keys($array) as $key)
  3523. {
  3524. $temp[$key] = $array[$key][$index];
  3525. if (!$nat_sort)
  3526. {
  3527. ($order=='asc') ? asort($temp) : arsort($temp);
  3528. }
  3529. else
  3530. {
  3531. ($case_sensitive) ? natsort($temp) : natcasesort($temp);
  3532. }
  3533. if ($order != 'asc') $temp = array_reverse($temp,TRUE);
  3534. }
  3535. foreach(array_keys($temp) as $key)
  3536. {
  3537. (is_numeric($key)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
  3538. }
  3539. return $sorted;
  3540. }
  3541. return $array;
  3542. }
  3543. // --------------------------------------------------------------------
  3544. /**
  3545. * Renders the javascript for fields (only once)
  3546. *
  3547. * @access protected
  3548. * @return string
  3549. */
  3550. protected function _render_js()
  3551. {
  3552. if ($this->no_css_js) return '';
  3553. if (empty($GLOBALS['__js_files__']))
  3554. {
  3555. $GLOBALS['__js_files__'] = array();
  3556. }
  3557. $_js = $this->get_js();
  3558. $str = '';
  3559. $str_inline = '';
  3560. $str_files = '';
  3561. $js_exec = array();
  3562. $script_regex = '#^<script(.+)src=#U';
  3563. $orig_ignore = $this->CI->asset->ignore_if_loaded;
  3564. $this->CI->asset->ignore_if_loaded = TRUE;
  3565. $orig_asset_output = $this->CI->asset->assets_output;
  3566. $this->CI->asset->assets_output = FALSE;
  3567. $add_js = array();
  3568. if (!empty($_js))
  3569. {
  3570. // loop through to generate javascript
  3571. foreach($_js as $type => $js)
  3572. {
  3573. // if a string with a slash in it, then we will assume it's just a single file to load'
  3574. if (is_array($js) OR ((strpos($js, '/') !== FALSE OR preg_match('#.+\.js$#U', $js)) AND strpos($js, '<script') === FALSE))
  3575. {
  3576. $str_files .= $this->_apply_asset_files('js', $js);
  3577. }
  3578. // if it starts with a script tag and DOES have a src attribute
  3579. else if (strpos($js, '<script') !== FALSE)
  3580. {
  3581. $str_inline .= $js;
  3582. }
  3583. // else it will simply call a function if it exists
  3584. else
  3585. {
  3586. $str .= "if (typeof(".$js.") == 'function'){\n";
  3587. $str .= "\t".$js."();\n";
  3588. $str .= "}\n";
  3589. }
  3590. }
  3591. }
  3592. // loop through custom fields to generate any js function calls
  3593. foreach($this->_rendered_field_types as $type => $cs_field)
  3594. {
  3595. if (isset($this->custom_fields[$type]))
  3596. {
  3597. $cs_field = $this->custom_fields[$type];
  3598. // check if the field type has a js function to call
  3599. if (!empty($cs_field->js_function))
  3600. {
  3601. $js_options = (!empty($cs_field->js_params)) ? $cs_field->js_params : NULL;
  3602. $js_exec_order = (!empty($cs_field->js_exec_order)) ? $cs_field->js_exec_order : 0;
  3603. $js_exec[$type] = array('func' => $cs_field->js_function, 'options' => $js_options, 'order' => $js_exec_order);
  3604. }
  3605. }
  3606. }
  3607. if (!empty($js_exec) OR !empty($str_files) OR !empty($str_inline) OR !empty($str))
  3608. {
  3609. // change ignore value on asset back to original
  3610. $this->CI->asset->ignore_if_loaded = $orig_ignore;
  3611. $this->CI->asset->assets_output = $orig_asset_output;
  3612. // sort the javascript
  3613. $js_exec = $this->_fields_sorter($js_exec, 'order');
  3614. $out = $str_files;
  3615. $out .= $str_inline;
  3616. $out .= "<script type=\"text/javascript\">\n";
  3617. $out .= $str."\n";
  3618. if (!empty($js_exec))
  3619. {
  3620. $out .= 'if (jQuery){ jQuery(function(){';
  3621. $out .= 'if (jQuery.fn.formBuilder) {';
  3622. $out .= 'if (typeof(window[\'formBuilderFuncs\']) == "undefined") { window[\'formBuilderFuncs\'] = {}; };';
  3623. $out .= 'window[\'formBuilderFuncs\'] = jQuery.extend(window[\'formBuilderFuncs\'], '.json_encode($js_exec).');';
  3624. $out .= 'jQuery("#'.$this->id.'").formBuilder(window[\'formBuilderFuncs\']);';
  3625. if ($this->auto_execute_js) $out .= 'jQuery("#'.$this->id.'").formBuilder().initialize();';
  3626. $out .= '}';
  3627. $out .= '})}';
  3628. }
  3629. $out .= "</script>\n";
  3630. return $out;
  3631. }
  3632. }
  3633. // --------------------------------------------------------------------
  3634. /**
  3635. * Applies the JS OR CSS for the fields. A variable of $css must exist for the page to render
  3636. *
  3637. * @access protected
  3638. * @param string 'js' OR 'css'
  3639. * @param array An array of js or css files. If it is an array and has a string key, then the key will be assumed to be the module in which the asset belongs
  3640. * @return string
  3641. */
  3642. protected function _apply_asset_files($type, $files)
  3643. {
  3644. if (empty($this->$type) OR $this->no_css_js) return;
  3645. // set as global variable to help with nested forms
  3646. $global_key = '__'.$type.'_files__';
  3647. $path_func = $type.'_path';
  3648. $tag_selector = ($type == 'js') ? 'head script' : 'head link';
  3649. $tag_attr = ($type == 'js') ? 'src' : 'href';
  3650. if (empty($GLOBALS[$global_key]))
  3651. {
  3652. $GLOBALS[$global_key] = array();
  3653. }
  3654. $out = '';
  3655. $to_add = array();
  3656. if (is_string($files))
  3657. {
  3658. $files = array($files);
  3659. }
  3660. foreach($files as $module => $asset)
  3661. {
  3662. if (is_string($asset))
  3663. {
  3664. $asset = preg_split('#\s*,\s*#', $asset);
  3665. }
  3666. $module = is_string($module) ? $module : NULL;
  3667. foreach($asset as $k => $a)
  3668. {
  3669. if (is_array($a))
  3670. {
  3671. foreach($a as $file)
  3672. {
  3673. $f = call_user_func($path_func, $file, $module);
  3674. $f_arr = explode('?', $f);
  3675. $f = $f_arr[0];
  3676. if (!empty($f))
  3677. {
  3678. array_push($GLOBALS[$global_key], $f);
  3679. $to_add[] = $f;
  3680. }
  3681. }
  3682. }
  3683. else
  3684. {
  3685. $file = call_user_func($path_func, $a, $module);
  3686. $file_arr = explode('?', $file);
  3687. $file = $file_arr[0];
  3688. if (!empty($file))
  3689. {
  3690. array_push($GLOBALS[$global_key], $file);
  3691. $to_add[] = $file;
  3692. }
  3693. }
  3694. }
  3695. }
  3696. // must use javascript to do this because forms may get ajaxed in and we need to inject their CSS into the head
  3697. if (!empty($to_add))
  3698. {
  3699. $out .= "\n<script>\n";
  3700. $out .= 'if (jQuery){ (function($) {
  3701. var currentCacheSetting = $.ajaxSetup()["cache"];
  3702. $.ajaxSetup({cache: true});
  3703. var files = '.json_encode($to_add).';
  3704. var assets = [];
  3705. jQuery("'.$tag_selector.'").each(function(i){
  3706. var attr = $(this).attr("'.$tag_attr.'");
  3707. if (attr){
  3708. attr = attr.split("?")[0];
  3709. assets.push(attr);
  3710. }
  3711. });
  3712. for(var n in files){
  3713. if (jQuery.inArray(files[n], assets) == -1){';
  3714. if ($type == 'css')
  3715. {
  3716. $out .= '
  3717. // for IE 8
  3718. if (document.createStyleSheet){
  3719. var stylesheet = document.createStyleSheet(files[n])
  3720. } else {
  3721. var stylesheet = document.createElement("link");
  3722. stylesheet.rel = "stylesheet";
  3723. stylesheet.type = "text/css";
  3724. stylesheet.href = files[n];
  3725. }
  3726. document.getElementsByTagName("head")[0].appendChild(stylesheet);
  3727. ';
  3728. }
  3729. elseif ($type == 'js')
  3730. {
  3731. // $out .= ';
  3732. // var file = files[n].split("?")[0];
  3733. // var script = document.createElement("script");
  3734. // script.src = file;
  3735. // script.async = false;
  3736. // // var attachElement = document.getElementsByTagName("head");
  3737. // // if (!attachElement.length){
  3738. // // attachElement = document.getElementsByTagName("body");
  3739. // // }
  3740. // // attachElement[0].appendChild(script);
  3741. // // Strangely doesn\'t appear in the DOM... would like to know why... oh... well here we go:
  3742. // // http://stackoverflow.com/questions/610995/cant-append-script-element
  3743. // $("head").append(script);
  3744. // $.ajaxSetup({cache: currentCacheSetting});
  3745. // ';
  3746. $out .= ';
  3747. var file = files[n].split("?")[0];
  3748. var script = document.createElement("script");
  3749. script.src = file;
  3750. script.async = false;
  3751. $("head").append(script);
  3752. $.ajaxSetup({cache: currentCacheSetting});
  3753. ';
  3754. }
  3755. $out .= '
  3756. }
  3757. }
  3758. })(jQuery);}';
  3759. $out .= "</script>\n";
  3760. }
  3761. return $out;
  3762. }
  3763. }
  3764. // ------------------------------------------------------------------------
  3765. /**
  3766. * A custom field class for Form_builder
  3767. *
  3768. * The Form.php class is required if a form object is not passed in the
  3769. * initialization process.
  3770. *
  3771. * @package FUEL CMS
  3772. * @subpackage Libraries
  3773. * @category Libraries
  3774. * @author David McReynolds @ Daylight Studio
  3775. * @link http://docs.getfuelcms.com/libraries/form_builder.html
  3776. * @autodoc FALSE
  3777. */
  3778. class Form_builder_field {
  3779. public $type = ''; // the type value of the field (e.g. textare, enum, datetime)
  3780. public $render_func = array();
  3781. public $html = ''; // html output for form field
  3782. public $js = array(); // the name of javascript file(s) to load
  3783. public $js_class = ''; // the CSS class used by the javascript to execute any javascript on the field
  3784. public $js_params = array(); // parameters to pass to the javascript function
  3785. public $js_exec_order = 1; // the order in which the javascript should be executed in relation to other fields... lower the sooner
  3786. public $js_function = ''; // the name of the javascript function to execute for the form field
  3787. public $represents = ''; // the field types this form field will represent (e.g. 'number'=>'bigint|smallint|tinyint|int')
  3788. public $css = ''; // a CSS file to load for this form field
  3789. public $css_class = ''; // a CSS class to automatically apply to the form field... very convenient for simply adding JS functionality to an existing field type
  3790. /**
  3791. * Constructor - Sets Form_builder preferences
  3792. *
  3793. * The constructor can be passed an array of config values
  3794. */
  3795. public function __construct($params = array())
  3796. {
  3797. $this->initialize($params);
  3798. }
  3799. // --------------------------------------------------------------------
  3800. /**
  3801. * Initialize preferences
  3802. *
  3803. * @access public
  3804. * @param array
  3805. * @return void
  3806. */
  3807. public function initialize($params = array())
  3808. {
  3809. foreach ($params as $key => $val)
  3810. {
  3811. if (isset($this->$key))
  3812. {
  3813. $this->$key = $val;
  3814. }
  3815. }
  3816. }
  3817. // --------------------------------------------------------------------
  3818. /**
  3819. * Overwrite to display
  3820. *
  3821. * @access public
  3822. * @param array
  3823. * @return string
  3824. */
  3825. public function render($params = array())
  3826. {
  3827. // add the CSS any css class as an additional parameter
  3828. if (!empty($this->css_class))
  3829. {
  3830. $params['class'] = $this->css_class.' '.$params['class'];
  3831. }
  3832. if (!empty($this->render_func))
  3833. {
  3834. return call_user_func($this->render_func, $params);
  3835. }
  3836. else
  3837. {
  3838. return (string)$this->html;
  3839. }
  3840. }
  3841. }
  3842. /* End of file Form_builder.php */
  3843. /* Location: ./modules/fuel/libraries/Form_builder.php */