Example #1
0
 /**
  * Process single column
  *
  * @param string $column_name
  * @param array $column_options
  * @param array $data
  * @param array $options
  * @return array
  */
 public static function process_single_column($column_name, $column_options, $data, $options = [])
 {
     $result = [];
     // process domain
     if (!empty($options['process_domains'])) {
         $temp = [$column_name => $column_options];
         $temp = object_data_common::process_domains($temp);
         $column_options = $temp[$column_name];
     }
     // if we ignoring not set fields
     if (!empty($options['ignore_not_set_fields']) && !array_key_exists($column_name, $data)) {
         return $result;
     }
     // processing
     $value = $data[$column_name] ?? null;
     if (is_array($value)) {
         $result2 = [];
         foreach ($value as $k => $v) {
             $temp = self::process_single_column_type($column_name, $column_options, $v, ['ignore_defaults' => $options['ignore_defaults'] ?? false]);
             if (array_key_exists($column_name, $temp) && $temp[$column_name] !== null) {
                 $result2[] = $temp[$column_name];
             }
         }
         $result[$column_name] = $result2;
     } else {
         $result = self::process_single_column_type($column_name, $column_options, $value, ['ignore_defaults' => $options['ignore_defaults'] ?? false]);
     }
     return $result;
 }
 /**
  * Get filtered by permission
  *
  * @param array $options
  * @return array
  */
 public function optmultis_filtered_by_permission($options = [])
 {
     $data = $this->get(['where' => ['sm_controller_acl_permission' => 1, 'sm_controller_inactive' => 0]]);
     $optmultis_map = $this->optmultis_map;
     return object_data_common::optmultis($data, $optmultis_map, $options);
 }
Example #3
0
 /**
  * Build options based on parameters, this must be used to have
  * consistencies in selects
  *
  * @param array $data
  * @param array $options_map
  * @param array $orderby
  * @param array $options
  * @return array
  */
 public static function build_options($data, $options_map, $orderby, $options)
 {
     $data = object_data_common::options($data, $options_map, $options);
     // sorting
     if (!empty($options['i18n']) && $options['i18n'] !== 'skip_sorting') {
         // mandatory sorting if localized
         array_key_sort($data, ['name' => SORT_ASC], ['name' => SORT_NATURAL]);
     } else {
         if (empty($orderby)) {
             array_key_sort($data, ['name' => SORT_ASC], ['name' => SORT_NATURAL]);
         }
     }
     return $data;
 }
 /**
  * Merge data to database
  *
  * @param array $data
  * @param array $options
  * @param object $form
  * @return array
  */
 public function merge($data, $options = [], &$form = null)
 {
     $result = ['success' => false, 'error' => [], 'warning' => [], 'deleted' => false, 'inserted' => false, 'new_serials' => [], 'options_model' => []];
     do {
         // start transaction
         $this->primary_model->db_object->begin();
         // load data from database
         $original = [];
         if (array_key_exists('original', $options)) {
             $original = $options['original'];
         } else {
             // load data from database
             // assemble primary key
             $pk = [];
             $full_pk = true;
             foreach ($this->data['pk'] as $v) {
                 if (isset($data[$v])) {
                     $pk[$v] = $data[$v];
                 } else {
                     $full_pk = false;
                 }
             }
             // load data
             if (!empty($pk) && $full_pk) {
                 $original = $this->get(['where' => $pk, 'single_row' => true]);
             }
         }
         // validate optimistic lock
         if ($this->primary_model->optimistic_lock && !empty($original)) {
             if (($data[$this->primary_model->optimistic_lock_column] ?? '') !== $original[$this->primary_model->optimistic_lock_column]) {
                 $result['error'][] = object_content_messages::optimistic_lock;
                 break;
             }
         }
         // we need to validate options_model
         if (!empty($options['options_model'])) {
             // get existing values
             foreach ($options['options_model'] as $k => $v) {
                 // current values
                 $value = array_key_get($data, $v['key']);
                 if ($value !== null && (is_string($value) && $value !== '')) {
                     if (is_array($value)) {
                         $value = array_keys($value);
                     } else {
                         $value = [$value];
                     }
                     $options['options_model'][$k]['current_values'] = $value;
                 } else {
                     $options['options_model'][$k]['current_values'] = null;
                 }
                 // we skip if we have no values
                 if (empty($options['options_model'][$k]['current_values'])) {
                     unset($options['options_model'][$k]);
                     continue;
                 }
                 // existing values
                 $value = array_key_get($original, $v['key']);
                 if ($value !== null) {
                     if (is_array($value)) {
                         $value = array_keys($value);
                     } else {
                         $value = [$value];
                     }
                     $options['options_model'][$k]['existing_values'] = $value;
                 } else {
                     $options['options_model'][$k]['existing_values'] = null;
                 }
             }
             // validate object_data
             $sql_options = [];
             foreach ($options['options_model'] as $k => $v) {
                 // we skip inactive model validations
                 if ($v['options_model'] == 'object_data_model_inactive') {
                     continue;
                 }
                 // process models
                 $temp = explode('::', $v['options_model']);
                 $model = factory::model($temp[0], true);
                 if (empty($temp[1])) {
                     $temp[1] = 'options';
                 }
                 if ($model->initiator_class == 'object_data' || $model->initiator_class == 'object_table' && !in_array($temp[1], ['options', 'options_active'])) {
                     $temp_options = array_keys(object_data_common::process_options($v['options_model'], null, $v['options_params'], $v['existing_values']));
                     // difference between arrays
                     $diff = array_diff($v['current_values'], $temp_options);
                     if (!empty($diff)) {
                         $result['options_model'][$k] = 1;
                     }
                 } else {
                     if ($model->initiator_class == 'object_table' && in_array($temp[1], ['options', 'options_active'])) {
                         // last element in the pk is a field
                         $pk = $model->pk;
                         $last = array_pop($pk);
                         // handling inactive
                         $options_active = [];
                         if ($temp[1] == 'options_active') {
                             $options_active = $model->options_active ? $model->options_active : [$model->column_prefix . 'inactive' => 0];
                         }
                         $sql_options[$k] = ['model' => $temp[0], 'field' => $last, 'params' => $v['options_params'], 'values' => $v['current_values'], 'existing_values' => $v['existing_values'], 'options_active' => $options_active];
                     }
                 }
             }
             // validating options
             if (!empty($sql_options)) {
                 $sql_model = new object_table_validator();
                 $sql_result = $sql_model->validate_options_multiple($sql_options);
                 if (!empty($sql_result['discrepancies'])) {
                     foreach ($sql_result['discrepancies'] as $k => $v) {
                         $result['options_model'][$k] = 1;
                     }
                 }
             }
             // we roll back if we have errors
             if (!empty($result['options_model'])) {
                 break;
             }
         }
         // comapare main row
         $this->timestamp = format::now('timestamp');
         $temp = $this->compare_one_row($data, $original, $this->data, ['flag_delete_row' => $options['flag_delete_row'] ?? false, 'flag_main_record' => true]);
         // if we goe an error
         if (!empty($temp['error'])) {
             $result['error'] = $temp['error'];
             break;
         }
         // we display warning if form has not been changed
         if (empty($temp['data']['total'])) {
             $result['warning'][] = object_content_messages::no_changes;
             break;
         }
         // insert history
         if (!empty($temp['data']['history'])) {
             foreach ($temp['data']['history'] as $k => $v) {
                 $temp2 = $this->primary_model->db_object->insert($k, $v);
                 if (!$temp2['success']) {
                     $result['error'] = $temp2['error'];
                     goto error;
                 }
             }
         }
         // audit
         if (!empty($temp['data']['audit'])) {
             // we need to put relation into pk
             if (!empty($this->primary_model->relation['field'])) {
                 $temp['data']['audit']['pk'][$this->primary_model->relation['field']] = $temp['new_serials'][$this->primary_model->relation['field']] ?? $data[$this->primary_model->relation['field']] ?? $original[$this->primary_model->relation['field']];
             }
             // merge
             $temp2 = factory::model($this->primary_model->audit_model, true)->merge($temp['data']['audit'], ['changes' => $temp['data']['total']]);
             if (!$temp2['success']) {
                 $result['error'] = $temp2['error'];
                 break;
             }
         }
         // if we got here we can commit
         $result['success'] = true;
         $result['deleted'] = $temp['data']['deleted'];
         $result['inserted'] = $temp['data']['inserted'];
         $result['updated'] = $temp['data']['updated'];
         $result['new_serials'] = $temp['new_serials'];
         // commit transaction
         $this->primary_model->db_object->commit();
         return $result;
     } while (0);
     // we roll back on error
     error:
     $this->primary_model->db_object->rollback();
     return $result;
 }
Example #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;
 }
Example #6
0
 /**
  * Multi level options
  *
  * @see $this->get()
  */
 public function optmultis($options = [])
 {
     if (empty($this->optmultis_map)) {
         return [];
     } else {
         $data = $this->get($options);
         $optmultis_map = $this->optmultis_map;
         return object_data_common::optmultis($data, $optmultis_map);
     }
 }
Example #7
0
 /**
  * Constructor
  *
  * @param string $list_link
  * @param array $options
  */
 public function __construct($options = [])
 {
     $this->options = $options;
     // processing model
     if (!empty($this->model)) {
         $this->model_object = factory::model($this->model);
     }
     if (empty($this->columns) && !empty($this->model)) {
         $this->columns = $this->model_object->columns;
     }
     // check if we have columns
     if (empty($this->columns)) {
         throw new Exception('List must have columns!');
     }
     // process domains
     foreach ($this->columns as $k => $v) {
         $temp = object_data_common::process_domains(['options' => $v]);
         $this->columns[$k] = $temp['options'];
     }
     // limit
     $limit = intval($options['input']['limit'] ?? 0);
     if ($limit > 0) {
         $this->limit = $limit;
     }
     // we need to set maximum limit if we are exporting
     if (!empty($this->options['input']['submit_export']) && !empty($this->options['input']['export']['format'])) {
         $this->limit = PHP_INT_MAX;
     }
     // offset
     $offset = intval($options['input']['offset'] ?? 0);
     if ($offset > 0) {
         $this->offset = $offset;
     }
     // filter
     $where = [];
     if (!empty($this->options['input']['filter'])) {
         $where = numbers_frontend_html_list_filter::where($this);
         if (!empty($where)) {
             $this->filtered = true;
         }
     }
     // sort
     if (!empty($this->options['input']['sort'])) {
         $this->orderby = [];
         foreach ($this->options['input']['sort'] as $k => $v) {
             if (!empty($v['column']) && !empty($this->columns[$v['column']])) {
                 $this->orderby[$v['column']] = $v['order'] ?? SORT_ASC;
             } else {
                 if (!empty($v['column']) && $v['column'] == 'full_text_search' && !empty($this->filter['full_text_search'])) {
                     $this->orderby['full_text_search'] = $v['order'] ?? SORT_ASC;
                 }
             }
         }
     }
     // datasources, count first
     if (empty($this->datasources['count']) && !empty($this->model)) {
         $this->datasources['count'] = ['model' => 'numbers_frontend_html_list_model_datasource_count', 'options' => ['model' => $this->model, 'where' => $where]];
     } else {
         if (!empty($this->datasources['count'])) {
             $this->datasources['count'] = ['model' => $this->datasources['count'], 'options' => ['where' => $where]];
         }
     }
     // datasources, data second
     if (empty($this->datasources['data']) && !empty($this->model)) {
         $this->datasources['data'] = ['model' => 'numbers_frontend_html_list_model_datasource_data', 'options' => ['model' => $this->model, 'offset' => $this->offset, 'limit' => $this->limit, 'orderby' => $this->orderby, 'where' => $where]];
     } else {
         if (!empty($this->datasources['data'])) {
             $this->datasources['data'] = ['model' => $this->datasources['data'], 'options' => ['offset' => $this->offset, 'limit' => $this->limit, 'orderby' => $this->orderby, 'where' => $where]];
         }
     }
     // actions
     if (!empty($this->options['actions'])) {
         $this->actions = array_merge($this->actions, $this->options['actions']);
     }
 }