Beispiel #1
0
 /**
  * Call validator method
  *
  * @param string $method
  * @param array $params
  * @param array $options
  * @param array $neighbouring_values
  * @return array
  */
 public static function method($method, $value, $params = [], $options = [], $neighbouring_values = [])
 {
     $method = factory::method($method);
     $params = $params ?? [];
     $params['options'] = $options;
     $params['neighbouring_values'] = $neighbouring_values;
     return factory::model($method[0], true)->{$method[1]}($value, $params);
 }
Beispiel #2
0
 /**
  * Acl handle
  *
  * @param string $acl_key
  * @param string $acl_type
  * @param array $data
  * @param array $options
  * @return boolean
  */
 public function acl_handle($acl_key, $acl_type, &$data, &$options)
 {
     // if we have no acls we must return null
     if (empty($this->data[$acl_key][$acl_type])) {
         return null;
     }
     // sort
     array_key_sort($this->data[$acl_key][$acl_type], ['order' => SORT_ASC], ['order' => SORT_NUMERIC]);
     // process one by one
     foreach ($this->data[$acl_key][$acl_type] as $k => $v) {
         $method = factory::method($v['method'], null, true);
         $result = $method[0]->{$method[1]}($acl_key, $acl_type, $data, $options);
         if (!$result) {
             debug::$data['acls'][$acl_key][$acl_type] = 'Failed';
             return false;
         } else {
             debug::$data['acls'][$acl_key][$acl_type] = 'Success';
         }
     }
     return true;
 }
 /**
  * Initialize
  * 
  * @param array $options
  */
 public static function init($options = [])
 {
     // default options
     self::$defaut_options = ['language_code' => 'sys', 'locale' => 'en_CA.UTF-8', 'timezone' => 'America/Toronto', 'server_timezone' => application::get('php.date.timezone'), 'date' => 'Y-m-d', 'time' => 'H:i:s', 'datetime' => 'Y-m-d H:i:s', 'timestamp' => 'Y-m-d H:i:s.u', 'amount_frm' => 20, 'amount_fs' => 40, 'settings' => ['currency_codes' => []], 'locale_locales' => [], 'locale_locale_js' => null, 'locale_set_name' => null, 'locale_options' => [], 'locale_override_class' => null];
     // settings from config files
     $config = application::get('flag.global.format');
     // settings from user account
     $entity = entity::groupped('format');
     // merge all of them together
     self::$options = array_merge_hard(self::$defaut_options, $config, i18n::$options, $entity, $options);
     // fix utf8
     self::$options['locale'] = str_replace(['utf8', 'utf-8'], 'UTF-8', self::$options['locale']);
     // generate a list of available locales
     $locale_settings = self::set_locale(self::$options['locale'], self::$defaut_options['locale']);
     self::$options = array_merge_hard(self::$options, $locale_settings);
     // fix values
     self::$options['amount_frm'] = (int) self::$options['amount_frm'];
     self::$options['amount_fs'] = (int) self::$options['amount_fs'];
     self::$options['locale_options']['mon_thousands_sep'] = self::$options['locale_options']['mon_thousands_sep'] ?? ',';
     self::$options['locale_options']['mon_decimal_point'] = self::$options['locale_options']['mon_decimal_point'] ?? '.';
     if (empty(self::$options['locale_options']['mon_grouping'])) {
         self::$options['locale_options']['mon_grouping'] = [3, 3];
     }
     // load data from models
     if (!empty(self::$options['model'])) {
         foreach (self::$options['model'] as $k => $v) {
             $method = factory::method($v, null);
             self::$options['settings'][$k] = factory::model($method[0], true)->{$method[1]}();
         }
         unset(self::$options['model']);
     }
     // push js format version to frontend
     if (!empty(self::$options['locale_override_class'])) {
         $locale_override_class = self::$options['locale_override_class'];
         $locale_override_class::js();
     }
 }
 /**
  * Options
  *
  * @param array $data
  * @param array $options_map
  * @param array $options
  * @return array
  */
 public static function options($data, $options_map, $options = [])
 {
     $i18n = [];
     $format = [];
     $options_map_new = [];
     $format_methods = [];
     foreach ($options_map as $k => $v) {
         if (is_array($v)) {
             $options_map_new[$k] = $v['field'];
             if (!empty($options['i18n']) && !empty($v['i18n']) && !array_key_exists('i18n', $v)) {
                 $i18n[$k] = !empty($options['i18n']);
             }
             if (!empty($v['format'])) {
                 $format[$k] = $v;
                 $format_methods[$k] = factory::method($v['format'], 'format');
             }
         } else {
             $options_map_new[$k] = $v;
             if (!empty($options['i18n'])) {
                 $i18n[$k] = true;
             }
         }
     }
     // we need to i18n and process formats
     if (!empty($i18n) || !empty($format)) {
         foreach ($data as $k => $v) {
             // localize
             if (!empty($i18n)) {
                 foreach ($i18n as $k2 => $v2) {
                     // we need to skip few things
                     if (!isset($data[$k][$k2])) {
                         continue;
                     }
                     $data[$k][$k2] = i18n(null, $data[$k][$k2]);
                 }
             }
             // format
             if (!empty($format)) {
                 foreach ($format as $k2 => $v2) {
                     $data[$k][$k2] = call_user_func_array([$format_methods[$k2][0], $format_methods[$k2][1]], [$data[$k][$k2], $v2['format_options'] ?? []]);
                 }
             }
         }
     }
     // inactive
     $inactive_message = '*Inactive';
     $i18n_inactive = !empty($options['i18n']) ? i18n(null, $inactive_message) : $inactive_message;
     foreach ($data as $k => $v) {
         if (!empty($options['column_prefix']) && !empty($v[$options['column_prefix'] . 'inactive'])) {
             $options_map_new[$options['column_prefix'] . 'inactive'] = 'inactive';
             $options_map_new['__prepend'] = 'name';
             $data[$k]['__prepend'] = $i18n_inactive;
         }
     }
     return remap($data, $options_map_new);
 }
Beispiel #5
0
 /**
  * Render elements value
  *
  * @param array $options
  * @param mixed $value
  * @param array $neighbouring_values
  * @return string
  * @throws Exception
  */
 public function render_element_value(&$options, $value = null, &$neighbouring_values = [])
 {
     // field name and values_key
     $options['options']['field_name'] = $options['options']['details_field_name'] ?? $options['options']['name'];
     $options['options']['field_values_key'] = implode('[::]', $options['options']['field_values_key'] ?? [$options['options']['field_name']]);
     // custom renderer
     if (!empty($options['options']['custom_renderer'])) {
         $method = factory::method($options['options']['custom_renderer'], null, true);
         $options_custom_renderer = $options;
         call_user_func_array($method, [&$this, &$options, &$value, &$neighbouring_values]);
     }
     // handling override_field_value method
     if (!empty($this->wrapper_methods['override_field_value']['main'])) {
         call_user_func_array($this->wrapper_methods['override_field_value']['main'], [&$this, &$options, &$value, &$neighbouring_values]);
     }
     $result_options = $options['options'];
     $options['options']['value'] = $value;
     array_key_extract_by_prefix($result_options, 'label_');
     $element_expand = !empty($result_options['expand']);
     $html_suffix = $result_options['html_suffix'] ?? '';
     // unset certain keys
     unset($result_options['order'], $result_options['required'], $result_options['html_suffix']);
     // processing options
     $flag_select_or_autocomplete = !empty($result_options['options_model']) || !empty($result_options['options']);
     if (!empty($result_options['options_model'])) {
         if (empty($result_options['options_params'])) {
             $result_options['options_params'] = [];
         }
         if (empty($result_options['options_options'])) {
             $result_options['options_options'] = [];
         }
         $result_options['options_options']['i18n'] = $result_options['options_options']['i18n'] ?? true;
         $result_options['options_options']['acl'] = $result_options['options_options']['acl'] ?? $this->acl;
         if (empty($result_options['options_depends'])) {
             $result_options['options_depends'] = [];
         }
         // options depends & params
         $this->process_params_and_depends($result_options['options_depends'], $neighbouring_values, $options, true);
         $this->process_params_and_depends($result_options['options_params'], $neighbouring_values, $options, false);
         $result_options['options_params'] = array_merge_hard($result_options['options_params'], $result_options['options_depends']);
         // we do not need options for autocomplete
         if (strpos($result_options['method'], 'autocomplete') === false) {
             $skip_values = [];
             if (!empty($options['options']['details_key'])) {
                 if (!empty($options['options']['details_parent_key'])) {
                     $temp_key = $options['options']['details_parent_key'] . '::' . $options['options']['details_key'];
                     if (!empty($this->misc_settings['details_unique_select'][$temp_key][$options['options']['details_field_name']][$options['options']['__parent_row_number']])) {
                         $skip_values = array_keys($this->misc_settings['details_unique_select'][$temp_key][$options['options']['details_field_name']][$options['options']['__parent_row_number']]);
                     }
                 } else {
                     if (!empty($this->misc_settings['details_unique_select'][$options['options']['details_key']][$options['options']['details_field_name']])) {
                         $skip_values = array_keys($this->misc_settings['details_unique_select'][$options['options']['details_key']][$options['options']['details_field_name']]);
                     }
                 }
             }
             $result_options['options'] = object_data_common::process_options($result_options['options_model'], $this, $result_options['options_params'], $value, $skip_values, $result_options['options_options']);
         } else {
             // we need to inject form id into autocomplete
             $result_options['form_id'] = "form_{$this->form_link}_form";
         }
     }
     // by default all selects are searchable if not specified otherwise
     if ($flag_select_or_autocomplete) {
         $result_options['searchable'] = $result_options['searchable'] ?? false;
     }
     // different handling for different type
     switch ($options['type']) {
         case 'container':
             $options_container = $options;
             //$options_container['previous_data'] = $v;
             // todo: pass $form_data_key from parent
             $options_container['previous_key'] = $options['previous_key'];
             // render container
             $temp_container_value = $this->render_container($data['fm_part_child_container_name'], $parents, $options_container);
             if (!empty($html_expand)) {
                 // get part id
                 $temp_id = $this->id('part_details', ['part_name' => $data['fm_part_name'], 'part_id' => $options_container['previous_id']]);
                 $temp_id_div_inner = $temp_id . '_html_expand_div_inner';
                 $temp_expand_div_inner = ['id' => $temp_id_div_inner, 'style' => 'display: none;', 'value' => $temp_container_value];
                 $temp_expand_div_a = ['href' => 'javascript:void(0);', 'onclick' => "numbers.element.toggle('{$temp_id_div_inner}');", 'value' => '+ / -'];
                 $temp_expand_div_outer = ['align' => 'left', 'value' => html::a($temp_expand_div_a) . '<br />' . html::div($temp_expand_div_inner)];
                 $value = html::div($temp_expand_div_outer);
             } else {
                 $value = $temp_container_value;
             }
             $result_options['value'] = $value;
             break;
         case 'field':
             $element_method = $result_options['method'] ?? 'html::input';
             if (strpos($element_method, '::') === false) {
                 $element_method = 'html::' . $element_method;
             }
             // value in special order
             $flag_translated = false;
             if (in_array($element_method, ['html::a', 'html::submit', 'html::button', 'html::button2'])) {
                 // translate value
                 $result_options['value'] = i18n($result_options['i18n'] ?? null, $result_options['value'] ?? null);
                 // process confirm_message
                 $result_options['onclick'] = $result_options['onclick'] ?? '';
                 if (!empty($result_options['confirm_message'])) {
                     $result_options['onclick'] .= 'return confirm(\'' . strip_tags(i18n(null, $result_options['confirm_message'])) . '\');';
                 }
                 // processing onclick for buttons
                 if (in_array($element_method, ['html::submit', 'html::button', 'html::button2'])) {
                     if (!empty($result_options['onclick']) && strpos($result_options['onclick'], 'this.form.submit();') !== false) {
                         $result_options['onclick'] = str_replace('this.form.submit();', "numbers.form.trigger_submit(this.form);", $result_options['onclick']) . ' return true;';
                     } else {
                         if (empty($result_options['onclick'])) {
                             $result_options['onclick'] .= 'numbers.form.trigger_submit_on_button(this); return true;';
                         } else {
                             $result_options['onclick'] = 'numbers.form.trigger_submit_on_button(this); ' . $result_options['onclick'];
                         }
                     }
                 }
                 $flag_translated = true;
                 // icon
                 if (!empty($result_options['icon'])) {
                     $result_options['value'] = html::icon(['type' => $result_options['icon']]) . ' ' . $result_options['value'];
                 }
                 // accesskey
                 if (isset($result_options['accesskey'])) {
                     $accesskey = explode('::', i18n(null, 'accesskey::' . $result_options['name'] . '::' . $result_options['accesskey'], ['skip_translation_symbol' => true]));
                     $result_options['accesskey'] = $accesskey[2];
                     $result_options['title'] = ($result_options['title'] ?? '') . ' ' . i18n(null, 'Shortcut Key: ') . $accesskey[2];
                 }
             } else {
                 if (in_array($element_method, ['html::div', 'html::span'])) {
                     if (!empty($result_options['i18n'])) {
                         $result_options['value'] = i18n($result_options['i18n'] ?? null, $result_options['value'] ?? null);
                         $flag_translated = true;
                     }
                 } else {
                     // editable fields
                     $result_options['value'] = $value;
                     // if we need to empty value, mostly for password fields
                     if (!empty($result_options['empty_value'])) {
                         $result_options['value'] = '';
                     }
                     // we need to empty zero integers and sequences, before format
                     if (($result_options['php_type'] ?? '') == 'integer' && ($result_options['type'] ?? '') != 'boolean' && ($result_options['domain'] ?? '') != 'counter' && 'counter' && empty($result_options['value'])) {
                         $result_options['value'] = '';
                     }
                     // format, not for selects/autocompletes/presets
                     if (!$flag_select_or_autocomplete) {
                         if (!empty($result_options['format'])) {
                             if (!empty($this->errors['fields'][$result_options['error_name']]) && empty($this->errors['formats'][$result_options['error_name']])) {
                                 // nothing
                             } else {
                                 $result_options['format_options'] = $result_options['format_options'] ?? [];
                                 if (!empty($result_options['format_depends'])) {
                                     $this->process_params_and_depends($result_options['format_depends'], $neighbouring_values, $options, true);
                                     $result_options['format_options'] = array_merge_hard($result_options['format_options'], $result_options['format_depends']);
                                 }
                                 $method = factory::method($result_options['format'], 'format');
                                 $result_options['value'] = call_user_func_array([$method[0], $method[1]], [$result_options['value'], $result_options['format_options']]);
                             }
                         }
                     }
                     // align
                     if (!empty($result_options['align'])) {
                         $result_options['style'] = ($result_options['style'] ?? '') . 'text-align:' . $result_options['align'] . ';';
                     }
                     // processing persistent
                     if (!empty($result_options['persistent']) && $this->values_loaded) {
                         if ($result_options['persistent'] === 'if_set') {
                             $original_value = $detail = array_key_get($this->original_values, $result_options['values_key']);
                             if (!empty($original_value)) {
                                 $result_options['readonly'] = true;
                             }
                         } else {
                             if (count($result_options['values_key']) == 1) {
                                 // parent record
                                 $result_options['readonly'] = true;
                             } else {
                                 if (empty($result_options['__new_row'])) {
                                     // details
                                     $temp = $result_options['values_key'];
                                     array_pop($temp);
                                     $detail = array_key_get($this->original_values, $temp);
                                     if (!empty($detail)) {
                                         $result_options['readonly'] = true;
                                     }
                                 }
                             }
                         }
                     }
                     // maxlength
                     if (in_array($result_options['type'] ?? '', ['char', 'varchar']) && !empty($result_options['length'])) {
                         $result_options['maxlength'] = $result_options['length'];
                     }
                     // global readonly
                     if (!empty($this->misc_settings['global']['readonly']) && empty($result_options['navigation'])) {
                         $result_options['readonly'] = true;
                     }
                     // title
                     if (isset($options['options']['label_name'])) {
                         $result_options['title'] = ($result_options['title'] ?? '') . ' ' . strip_tags(i18n(null, $options['options']['label_name']));
                     }
                 }
             }
             // translate place holder
             if (array_key_exists('placeholder', $result_options)) {
                 if (!empty($result_options['placeholder'])) {
                     $result_options['placeholder'] = strip_tags(i18n(null, $result_options['placeholder']));
                 }
             } else {
                 if (!empty($result_options['validator_method']) && empty($result_options['value'])) {
                     $temp = object_validator_base::method($result_options['validator_method'], $result_options['value'], $result_options['validator_params'] ?? [], $options['options'], $neighbouring_values);
                     if ($flag_select_or_autocomplete) {
                         $placeholder = $temp['placeholder_select'];
                     } else {
                         $placeholder = $temp['placeholder'];
                     }
                     if (!empty($placeholder)) {
                         $result_options['placeholder'] = strip_tags(i18n(null, $placeholder));
                     }
                 }
             }
             // events
             foreach (numbers_frontend_html_class_html5::$events as $e) {
                 if (!empty($result_options['readonly'])) {
                     // important - readonly emenets cannot have events
                     unset($result_options[$e]);
                 } else {
                     if (!empty($result_options[$e])) {
                         $result_options[$e] = str_replace('this.form.submit();', 'numbers.form.trigger_submit(this);', $result_options[$e]);
                         $result_options[$e] = str_replace('this.form.extended.', $this->misc_settings['extended_js_class'] . '.', $result_options[$e]);
                     }
                 }
             }
             break;
         case 'html':
             $element_method = null;
             break;
         default:
             throw new Exception('Render detail type: ' . $data['fm_part_type']);
     }
     // handling html_method
     if (isset($element_method)) {
         $method = factory::method($element_method, 'html');
         $field_method_object = factory::model($method[0], true);
         // todo: unset non html attributes
         $value = $field_method_object->{$method[1]}($result_options);
         // building navigation
         if (!empty($result_options['navigation'])) {
             $name = 'navigation[' . $result_options['name'] . ']';
             $temp = '<table width="100%" dir="ltr">';
             // always left to right
             $temp .= '<tr>';
             $temp .= '<td width="1%">' . html::button2(['name' => $name . '[first]', 'value' => html::icon(['type' => 'step-backward']), 'onclick' => 'numbers.form.trigger_submit_on_button(this);', 'title' => i18n(null, 'First')]) . '</td>';
             $temp .= '<td width="1%">&nbsp;</td>';
             $temp .= '<td width="1%">' . html::button2(['name' => $name . '[previous]', 'value' => html::icon(['type' => 'caret-left']), 'onclick' => 'numbers.form.trigger_submit_on_button(this);', 'title' => i18n(null, 'Previous')]) . '</td>';
             $temp .= '<td width="1%">&nbsp;</td>';
             $temp .= '<td width="90%">' . $value . '</td>';
             $temp .= '<td width="1%">&nbsp;</td>';
             $temp .= '<td width="1%">' . html::button2(['name' => $name . '[refresh]', 'value' => html::icon(['type' => 'refresh']), 'onclick' => 'numbers.form.trigger_submit_on_button(this);', 'title' => i18n(null, 'Refresh')]) . '</td>';
             $temp .= '<td width="1%">&nbsp;</td>';
             $temp .= '<td width="1%">' . html::button2(['name' => $name . '[next]', 'value' => html::icon(['type' => 'caret-right']), 'onclick' => 'numbers.form.trigger_submit_on_button(this);', 'title' => i18n(null, 'Next')]) . '</td>';
             $temp .= '<td width="1%">&nbsp;</td>';
             $temp .= '<td width="1%">' . html::button2(['name' => $name . '[last]', 'value' => html::icon(['type' => 'step-forward']), 'onclick' => 'numbers.form.trigger_submit_on_button(this);', 'title' => i18n(null, 'Last')]) . '</td>';
             $temp .= '</tr>';
             $temp .= '</table>';
             $value = $temp;
         }
     }
     // html suffix and prefix
     if (!empty($html_suffix)) {
         $value .= $html_suffix;
     }
     // if we need to display settings
     if (application::get('flag.numbers.frontend.html.form.show_field_settings')) {
         $id_original = $result_options['id'] . '__settings_original';
         $id_modified = $result_options['id'] . '__settings_modified';
         $value .= html::a(['href' => 'javascript:void(0);', 'onclick' => "\$('#{$id_original}').toggle();", 'value' => html::label2(['type' => 'primary', 'value' => count($options['options'])])]);
         $value .= html::a(['href' => 'javascript:void(0);', 'onclick' => "\$('#{$id_modified}').toggle();", 'value' => html::label2(['type' => 'warning', 'value' => count($result_options)])]);
         $value .= '<div id="' . $id_original . '" style="display:none; position: absolute; text-align: left; width: 500px; z-index: 32000;">' . print_r2($options['options'], true) . '</div>';
         $value .= '<div id="' . $id_modified . '" style="display:none; position: absolute; text-align: left; width: 500px; z-index: 32000;">' . print_r2($result_options, true) . '</div>';
     }
     // we need to put original options back
     if (!empty($options['options']['custom_renderer'])) {
         $options = $options_custom_renderer;
     }
     return $value;
 }
Beispiel #6
0
 /**
  * Data default renderer
  *
  * @return string
  */
 private final function render_data_default()
 {
     $result = '';
     // if we have no rows we display a messsage
     if ($this->num_rows == 0) {
         return html::message(['type' => 'warning', 'options' => [i18n(null, object_content_messages::no_rows_found)]]);
     }
     $counter = 1;
     $table = ['header' => [], 'options' => []];
     // action flags
     $actions = [];
     if (object_controller::can('record_view')) {
         $actions['view'] = true;
     }
     // generate columns
     foreach ($this->columns as $k => $v) {
         // if we can not view we skip action column
         if (empty($actions) && $k == 'action') {
             continue;
         }
         $table['header'][$k] = ['value' => i18n(null, $v['name']), 'nowrap' => true, 'width' => $v['width'] ?? null];
     }
     // generate rows
     foreach ($this->rows as $k => $v) {
         // process all columns first
         $row = [];
         foreach ($this->columns as $k2 => $v2) {
             // if we can not view we skip action column
             if (empty($actions) && $k2 == 'action') {
                 continue;
             }
             $value = [];
             // create cell properties
             foreach (['width', 'align'] as $v3) {
                 if (isset($v2[$v3])) {
                     $value[$v3] = $v2[$v3];
                 }
             }
             // process rows
             if ($k2 == 'action') {
                 $value['value'] = [];
                 if (!empty($actions['view'])) {
                     $mvc = application::get('mvc');
                     $pk = extract_keys($this->model_object->pk, $v);
                     $url = $mvc['controller'] . '/_edit?' . http_build_query2($pk);
                     $value['value'][] = html::a(['value' => i18n(null, 'View'), 'href' => $url]);
                 }
                 $value['value'] = implode(' ', $value['value']);
             } else {
                 if ($k2 == 'row_number') {
                     $value['value'] = format::id($counter) . '.';
                 } else {
                     if ($k2 == 'offset_number') {
                         $value['value'] = format::id($this->offset + $counter) . '.';
                     } else {
                         if (!empty($v2['options_model'])) {
                             if (strpos($v2['options_model'], '::') === false) {
                                 $v2['options_model'] .= '::options';
                             }
                             $params = $v2['options_params'] ?? [];
                             if (!empty($v2['options_depends'])) {
                                 foreach ($v2['options_depends'] as $k0 => $v0) {
                                     $params[$k0] = $v[$v0];
                                 }
                             }
                             $crypt_object = new crypt();
                             $hash = $crypt_object->hash($v2['options_model'] . serialize($params));
                             if (!isset($this->cached_options[$hash])) {
                                 $method = factory::method($v2['options_model'], null, true);
                                 $this->cached_options[$hash] = call_user_func_array($method, [['where' => $params]]);
                             }
                             if (isset($this->cached_options[$hash][$v[$k2]])) {
                                 $value['value'] = $this->cached_options[$hash][$v[$k2]]['name'];
                             } else {
                                 $value['value'] = null;
                             }
                         } else {
                             if (!empty($v2['options']) && !is_array($v[$k2])) {
                                 if (isset($v2['options'][$v[$k2]])) {
                                     $value['value'] = $v2['options'][$v[$k2]]['name'];
                                 } else {
                                     $value['value'] = null;
                                 }
                             } else {
                                 if (isset($v[$k2])) {
                                     $value['value'] = $v[$k2];
                                 } else {
                                     $value['value'] = null;
                                 }
                             }
                         }
                     }
                 }
             }
             // put value into row
             if (!empty($v2['format'])) {
                 $format_options = $v2['format_options'] ?? [];
                 if (!empty($v2['format_depends'])) {
                     $format_depends = $v2['format_depends'];
                     $this->process_params_and_depends($format_depends, $v);
                     $format_options = array_merge_hard($format_options, $format_depends);
                 }
                 $method = factory::method($v2['format'], 'format');
                 $value['value'] = call_user_func_array([$method[0], $method[1]], [$value['value'], $format_options]);
             }
             $row[$k2] = $value;
         }
         // put processed columns though user defined function
         if (method_exists($this, 'render_data_rows')) {
             $table['options'][$counter] = $this->render_data_rows($row, $v);
         } else {
             $table['options'][$counter] = $row;
         }
         $counter++;
     }
     return html::table($table);
 }