/** * Initializing i18n * * @param array $options */ public static function init($options = []) { $i18n = application::get('flag.global.i18n') ?? []; $i18n = array_merge_hard($i18n, $options ?? []); // determine final language $languages = factory::model('numbers_backend_i18n_languages_model_languages')->get(); $final_language = application::get('flag.global.__language_code') ?? session::get('numbers.entity.format.language_code') ?? $i18n['language_code'] ?? 'sys'; if (empty($languages[$final_language])) { $final_language = 'sys'; $i18n['rtl'] = 0; } // put settings into system if (!empty($languages[$final_language])) { foreach ($languages[$final_language] as $k => $v) { $k = str_replace('lc_language_', '', $k); if (in_array($k, ['code', 'inactive'])) { continue; } if (empty($v)) { continue; } $i18n[$k] = $v; } } $i18n['language_code'] = $final_language; self::$options = $i18n; session::set('numbers.entity.format.language_code', $final_language); application::set('flag.global.i18n', $i18n); self::$initialized = true; // initialize the module return factory::submodule('flag.global.i18n.submodule')->init($i18n); }
/** * Whether we can perform certain action * * @param mixed $action_code_or_id * @return boolean */ public static function can($action_code_or_id) { if (self::$cache_actions === null) { self::$cache_actions = factory::model('numbers_backend_system_controller_model_actions')->get(); } if (is_string($action_code_or_id)) { foreach (self::$cache_actions as $k => $v) { if ($v['sm_cntractn_code'] == $action_code_or_id) { $action_code_or_id = $k; break; } } } if (!isset(self::$cache_actions[$action_code_or_id])) { throw new Exception('Unknown action!'); } $permissions = application::get(['controller', 'acl', 'permissions']); $start = $action_code_or_id; do { // see if we have permission if (empty($permissions[$start])) { break; } // we need to check permission on a parent if (!empty(self::$cache_actions[$start]['sm_cntractn_parent_id'])) { $start = self::$cache_actions[$start]['sm_cntractn_parent_id']; } else { // exit if there's no parent return true; } } while (1); return false; }
/** * Process options * * @param string $model_and_method - model::method * @param object $existing_object * @param array $where * @param array $existing_values * @param array $skip_values * @param array $options * @return array */ public static function process_options($model_and_method, $existing_object = null, $where = [], $existing_values = [], $skip_values = [], $options = []) { // put changes into options $options['where'] = array_merge_hard($options['where'] ?? [], $where); $options['existing_values'] = $existing_values; $options['skip_values'] = $skip_values; // see if we have cached version $hash = sha1($model_and_method . serialize($options)); if (isset(self::$cached_options[$hash])) { return self::$cached_options[$hash]; } else { $temp = explode('::', $model_and_method); if (count($temp) == 1) { $model = $temp[0]; $method = 'options'; } else { $model = $temp[0]; $method = $temp[1]; } if ($model == 'this' && !empty($existing_object)) { $object = $existing_object; } else { $object = factory::model($model, true); } self::$cached_options[$hash] = $object->{$method}($options); return self::$cached_options[$hash]; } }
public function query($options = []) { $model = factory::model($options['model']); $db = $model->db_object(); $where = ''; if (!empty($options['where'])) { $where = 'AND ' . $db->prepare_condition($options['where']); } $ts = $db->full_text_search_query($options['fields'], $options['search_text'] . ''); $fields = $options['fields']; $sql_pk = ''; // we automatically include main pk into a query if (!in_array($options['pk'], $options['fields'])) { // in_array($options['pk'], $model->pk) && $fields[] = $options['pk']; // we need to include integer types to the query $temp = intval($options['search_text']); if ($model->columns[$options['pk']]['php_type'] == 'integer' && $temp != 0) { $sql_pk .= " OR {$options['pk']} = " . (int) $options['search_text']; } } $fields[] = $ts['rank']; $fields = implode(', ', $fields); $tmp = <<<TTT \t\t\tSELECT \t\t\t\t{$fields} \t\t\tFROM [table[{$options['model']}]] a \t\t\tWHERE 1=1 \t\t\t\t\t{$where} \t\t\t\t\tAND (({$ts['where']}){$sql_pk}) \t\t\tORDER BY {$ts['orderby']} DESC, {$options['fields'][0]} \t\t\tLIMIT 11 TTT; return $tmp; }
/** * 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); }
public function save(&$form) { $model = factory::model($form->options['other']['model']); $save = [$model->column_prefix . 'important' => !empty($form->values['important']) ? 1 : 0, $model->column_prefix . 'comment_value' => $form->values['comment'] . '', $model->column_prefix . 'who_entity_id' => session::get('numbers.entity.em_entity_id'), $model->column_prefix . 'inserted' => format::now('timestamp')]; foreach ($form->options['other']['map'] as $k => $v) { $save[$v] = $form->options['other']['pk'][$k]; } $save_result = $model->save($save, ['ignore_not_set_fields' => true]); if ($save_result['success']) { $form->error('success', 'Comment has been added successfully!'); } else { $form->error('danger', 'Could not add comment!'); } }
public function query($options = []) { $model = factory::model($options['model']); $db = $model->db_object(); $where = null; if (!empty($options['where'])) { $where = 'AND ' . $db->prepare_condition($options['where']); } $columns = []; foreach ($model->columns as $k => $v) { $columns[] = 'a.' . $k . ' ' . str_replace($model->column_prefix, '', $k); } return "SELECT " . implode(', ', $columns) . ", b.em_entity_name FROM [table[{$options['model']}]] a LEFT JOIN [table[numbers_data_entities_entities_model_entities]] b ON a.{$model->column_prefix}who_entity_id = b.em_entity_id WHERE 1=1 {$where} ORDER BY {$model->column_prefix}inserted DESC"; }
public function query($options = []) { $where = null; if (!empty($options['where'])) { $model = factory::model($options['model']); $db = $model->db_object(); $where = 'AND ' . $db->prepare_condition($options['where']); } return <<<TTT \t\t\tSELECT \t\t\t\tCOUNT(*) count \t\t\tFROM [table[{$options['model']}]] a \t\t\tWHERE 1=1 \t\t\t\t{$where} TTT; }
public function query($options = []) { $model = factory::model($options['model'], true); $this->db_object = $model->db_object; $column = $options['where']['column_name']; // adjust type based on value $where = null; if (empty($options['where']['column_value'])) { if ($options['type'] == 'previous') { $options['type'] = 'first'; } if ($options['type'] == 'next') { $options['type'] = 'last'; } } else { if ($options['type'] == 'previous') { $where = ' AND ' . $this->db_object->prepare_condition(["{$column},<" => $options['where']['column_value']]); } else { if ($options['type'] == 'next') { $where = ' AND ' . $this->db_object->prepare_condition(["{$column},>" => $options['where']['column_value']]); } else { if ($options['type'] == 'refresh') { $where = ' AND ' . $this->db_object->prepare_condition(["{$column}" => $options['where']['column_value']]); } } } } $depends = null; if (!empty($options['where']['depends'])) { $depends = ' AND (' . $this->db_object->prepare_condition($options['where']['depends']) . ')'; } $pk = implode(', ', $options['pk']); // generate query based on type switch ($options['type']) { case 'first': return "SELECT {$pk} FROM {$model->name} WHERE {$column} = (SELECT MIN({$column}) new_value FROM {$model->name} WHERE {$column} IS NOT NULL {$depends}) {$depends}"; case 'previous': return "SELECT {$pk} FROM {$model->name} WHERE 1=1 {$where} {$depends} ORDER BY {$column} DESC LIMIT 1"; case 'next': return "SELECT {$pk} FROM {$model->name} WHERE 1=1 {$where} {$depends} ORDER BY {$column} ASC LIMIT 1"; case 'last': return "SELECT {$pk} FROM {$model->name} WHERE {$column} = (SELECT MAX({$column}) new_value FROM {$model->name} WHERE {$column} IS NOT NULL {$depends}) {$depends}"; case 'refresh': default: return "SELECT {$pk} FROM {$model->name} WHERE 1=1 {$where} {$depends}"; } }
public function query($options = []) { $model = factory::model($options['model']); $db = $model->db_object(); $where = ''; if (!empty($options['where'])) { $where = 'AND ' . $db->prepare_condition($options['where']); } $fields = $options['fields']; if (!in_array($options['pk'], $options['fields'])) { $fields[] = $options['pk']; } $fields = implode(', ', $fields); return <<<TTT \t\t\tSELECT \t\t\t\t{$fields} \t\t\tFROM [table[{$options['model']}]] a \t\t\tWHERE 1=1 \t\t\t\t\t{$where} TTT; }
/** * Destroy */ public static function destroy() { if (empty(self::$missing)) { return; } //if (!chance(10)) return; // we would create temp table $db = factory::model('numbers_backend_i18n_basic_model_missing')->db_object(); $db->query("CREATE TEMPORARY TABLE temp_translations (sys text, counter integer, lang text)"); // insert data $data = []; foreach (self::$missing as $k => $v) { $data[] = ['sys' => $k, 'counter' => $v, 'lang' => self::$language_code]; } $db->insert('temp_translations', $data); // merge data $sql = <<<TTT \t\t\tINSERT INTO lc_missing ( \t\t\t\tlc_missing_id, \t\t\t\tlc_missing_language_code, \t\t\t\tlc_missing_text_sys, \t\t\t\tlc_missing_counter \t\t\t) \t\t\tSELECT \t\t\t\tnextval('lc_missing_lc_missing_id_seq'), \t\t\t\tlang lc_missing_language_code, \t\t\t\tsys lc_missing_text_sys, \t\t\t\t0 lc_missing_counter \t\t\tFROM temp_translations a \t\t\tLEFT JOIN lc_translations b ON a.sys = b.lc_translation_text_sys AND a.lang = b.lc_translation_language_code \t\t\tLEFT JOIN lc_missing c ON a.sys = c.lc_missing_text_sys AND a.lang = c.lc_missing_language_code \t\t\tWHERE b.lc_translation_language_code IS NULL AND c.lc_missing_language_code IS NULL TTT; $db->query($sql); // last step perform update $sql = "UPDATE lc_missing a SET lc_missing_counter = lc_missing_counter + coalesce((SELECT counter FROM temp_translations b WHERE b.sys = a.lc_missing_text_sys AND b.lang = a.lc_missing_language_code), 0)"; $db->query($sql); }
/** * 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(); } }
public function query($options = []) { $where = null; if (!empty($options['where'])) { $model = factory::model($options['model']); $db = $model->db_object(); $where = 'AND ' . $db->prepare_condition($options['where']); } if (!empty($options['orderby']['full_text_search']) && !empty($options['where']['full_text_search,fts'])) { $temp = []; foreach ($options['orderby'] as $k => $v) { if ($k != 'full_text_search') { $temp[$k] = $v; } else { $model = factory::model($options['model']); $db = $model->db_object(); $temp2 = $db->full_text_search_query($options['where']['full_text_search,fts']['fields'], $options['where']['full_text_search,fts']['str']); $temp[$temp2['orderby']] = $v; } } $options['orderby'] = $temp; } else { unset($options['orderby']['full_text_search']); } $options['orderby'] = !empty($options['orderby']) ? 'ORDER BY ' . array_key_sort_prepare_keys($options['orderby'], true) : ''; return <<<TTT \t\t\tSELECT \t\t\t\t* \t\t\tFROM [table[{$options['model']}]] a \t\t\tWHERE 1=1 \t\t\t\t{$where} \t\t\t{$options['orderby']} \t\t\tLIMIT {$options['limit']} \t\t\tOFFSET {$options['offset']} TTT; }
/** * Format filter string as human readable * * @param object $object * @return array */ public static function human($object) { $input = $object->options['input']; $filter = $object->filter; $full_text_search = $filter['full_text_search'] ?? null; unset($filter['full_text_search']); // generate values $result = []; foreach ($filter as $k => $v) { if (!empty($v['range'])) { $start = object_table_columns::process_single_column($k, $v, $input['filter'] ?? [], ['process_domains' => true, 'ignore_defaults' => true, 'ignore_not_set_fields' => true]); $end = object_table_columns::process_single_column($k . '2', $v, $input['filter'] ?? [], ['process_domains' => true, 'ignore_defaults' => true, 'ignore_not_set_fields' => true]); $result[i18n(null, $v['name'])] = '(' . ($start[$k] ?? null) . ') - (' . ($end[$k . '2'] ?? null) . ')'; } else { $start = object_table_columns::process_single_column($k, $v, $input['filter'] ?? [], ['process_domains' => true, 'ignore_defaults' => true, 'ignore_not_set_fields' => true]); // we need to process arrays if (isset($start[$k]) && is_array($start[$k])) { if (!empty($v['options_model'])) { $params = $v['options_params'] ?? []; $start[$k] = array_options_to_string(factory::model($v['options_model'])->options(['where' => $params, 'i18n' => true]), $start[$k]); } else { $start[$k] = implode(', ', $start[$k]); } } $result[i18n(null, $v['name'])] = $start[$k] ?? null; } } // full text search if (!empty($full_text_search)) { $names = []; foreach ($full_text_search as $v) { $names[] = i18n(null, $filter[$v]['name']); } $result[i18n(null, 'Text Search')] = ($input['filter']['full_text_search'] ?? null) . ' (' . implode(', ', $names) . ')'; } return $result; }
/** * Create temporary table * * @param string $table * @param array $columns * @param array $pk * @param array $options * skip_serials * @return array */ public function create_temp_table($table, $columns, $pk = null, $options = []) { $ddl_object = factory::model(str_replace('_base_123', '_ddl', get_called_class() . '_123')); $columns_sql = []; foreach ($columns as $k => $v) { $temp = $ddl_object->is_column_type_supported($v, $table); // default $default = $temp['column']['default'] ?? null; if (is_string($default) && $default != 'now()') { $default = "'" . $default . "'"; } // we need to cancel serial types if (!empty($options['skip_serials']) && strpos($temp['column']['type_original'] ?? $temp['column']['type'], 'serial') !== false) { $default = 0; } $columns_sql[] = $k . ' ' . $temp['column']['type'] . ($default !== null ? ' DEFAULT ' . $default : '') . (!($temp['column']['null'] ?? false) ? ' NOT NULL' : ''); } // pk if ($pk) { $columns_sql[] = "PRIMARY KEY (" . implode(', ', $pk) . ")"; } $columns_sql = implode(', ', $columns_sql); $sql = "CREATE TEMPORARY TABLE {$table} ({$columns_sql})"; return $this->query($sql); }
/** * Compare single row * * @param array $data_row * @param array $original_row * @param array $collection * @param array $options * @param array $parent_pk * @param array $parent_row * @return array */ public final function compare_one_row($data_row, $original_row, $collection, $options, $parent_pk = null, $parent_row = []) { $result = ['success' => false, 'error' => [], 'data' => ['history' => [], 'audit' => [], 'total' => 0, 'updated' => false, 'deleted' => false, 'inserted' => false], 'new_serials' => []]; $model = $collection['model_object']; // important to reset cache $model->reset_cache(); // step 1, clenup data $data_row_final = $data_row; // we need to manualy inject parents keys if (!empty($parent_pk)) { foreach ($collection['map'] as $k => $v) { // if we are dealing with relations if (strpos($k, 'relation_id') !== false) { $data_row_final[$v] = $parent_row[$k]; } else { $data_row_final[$v] = $parent_pk[$k]; } } } $model->process_columns($data_row_final, ['ignore_not_set_fields' => true, 'skip_type_validation' => true]); // step 2 process row $delete = $update = $audit = $audit_details = $pk = []; $action = null; if (!empty($options['flag_delete_row']) || empty($data_row)) { // if we delete // if we have data if (!empty($original_row)) { $pk = extract_keys($collection['pk'], $original_row); $delete = ['table' => $model->name, 'pk' => $pk]; // audit $action = 'delete'; $audit = $original_row; } } else { if (empty($original_row)) { // if we insert // process who columns $model->process_who_columns(['inserted', 'optimistic_lock'], $data_row_final, $this->timestamp); // handle serial types foreach ($model->columns as $k => $v) { if (strpos($v['type'], 'serial') !== false && empty($v['null'])) { $temp = $this->primary_model->db_object->sequence($model->name . '_' . $k . '_seq'); $result['new_serials'][$k] = $data_row_final[$k] = $temp['rows'][0]['counter']; } } $temp = $this->primary_model->db_object->insert($model->name, [$data_row_final], null); if (!$temp['success']) { $result['error'] = $temp['error']; return $result; } $result['data']['total']++; // flag for main record if (!empty($options['flag_main_record'])) { $result['data']['inserted'] = true; } // pk $pk = extract_keys($collection['pk'], $data_row_final); // audit $action = 'insert'; $audit = $data_row_final; } else { // if we update foreach ($data_row_final as $k => $v) { // hard comparison if ($v !== $original_row[$k]) { $update[$k] = $v; } if (in_array($k, $collection['pk'])) { $pk[$k] = $v; } } // audit $action = 'update'; } } // step 3 process details if (!empty($collection['details'])) { foreach ($collection['details'] as $k => $v) { // create new object $v['model_object'] = factory::model($k, true); if ($v['type'] == '11') { $details_result = $this->compare_one_row($data_row[$k] ?? [], $original_row[$k] ?? [], $v, ['flag_delete_row' => !empty($delete)], $pk, $data_row_final); if (!empty($details_result['error'])) { $result['error'] = $details_result['error']; return $result; } else { $result['data']['total'] += $details_result['data']['total']; } // audit if (!empty($details_result['data']['audit'])) { $audit_details[$k] = $details_result['data']['audit']; } } else { if ($v['type'] == '1M') { $keys = []; if (isset($original_row[$k]) && is_array($original_row[$k])) { $keys = array_keys($original_row[$k]); } if (isset($data_row[$k]) && is_array($data_row[$k])) { $keys = array_merge($keys, array_keys($data_row[$k])); } $keys = array_unique($keys); if (!empty($keys)) { foreach ($keys as $v2) { $details_result = $this->compare_one_row($data_row[$k][$v2] ?? [], $original_row[$k][$v2] ?? [], $v, ['flag_delete_row' => !empty($delete)], $pk, $data_row_final); if (!empty($details_result['error'])) { $result['error'] = $details_result['error']; return $result; } else { $result['data']['total'] += $details_result['data']['total']; } // audit if (!empty($details_result['data']['audit'])) { $audit_details[$k][$v2] = $details_result['data']['audit']; } } } } } } } // step 4 update record if (!empty($update) || $action == 'update' && $result['data']['total'] > 0) { // process who columns $model->process_who_columns(['updated', 'optimistic_lock'], $update, $this->timestamp); if (!empty($update)) { // update record $temp = $this->primary_model->db_object->update($model->name, $update, [], ['where' => $pk]); if (!$temp['success']) { $result['error'] = $temp['error']; return $result; } $result['data']['total']++; } // flag for main record if (!empty($options['flag_main_record'])) { $result['data']['updated'] = true; } // audit $audit = $update; } // step 5 delete record after we deleted all childrens if (!empty($delete)) { $temp = $this->primary_model->db_object->delete($delete['table'], [], [], ['where' => $delete['pk']]); if (!$temp['success']) { $result['error'] = $temp['error']; return $result; } $result['data']['total']++; // flag for main record if (!empty($options['flag_main_record'])) { $result['data']['deleted'] = true; } } // step 6 history only if we updated or deleted if ($model->history && (!empty($delete) || !empty($update))) { $temp = $original_row; $model->process_who_columns(['updated'], $temp, $this->timestamp); $result['data']['history'][$model->history_name][] = $temp; } // step 7 audit if ($this->primary_model->audit && !empty($audit)) { $result['data']['audit'] = ['action' => $action, 'pk' => $pk, 'columns' => []]; foreach ($audit as $k => $v) { $old = $original_row[$k] ?? null; if ($v !== $old) { if (($model->columns[$k]['domain'] ?? '') == 'password') { $v = '*** *** ***'; } $result['data']['audit']['columns'][$k] = [$v, $old]; } } // details if (!empty($audit_details)) { $result['data']['audit']['details'] = $audit_details; } } // success if (!empty($result['data']['total'])) { $result['success'] = true; } return $result; }
/** * Validate multiple options/autocompletes at the same time * * @param array $options * @return array */ public function validate_options_multiple($options = []) { $result = ['success' => false, 'error' => [], 'discrepancies' => []]; $mass_sql = []; foreach ($options as $k => $v) { $model = factory::model($v['model'], true); $values = [$v['field'] => $v['values']]; $where2 = []; // values and options_active $temp2 = []; $temp2[] = $model->db_object->prepare_condition($values, 'AND'); if (!empty($v['options_active'])) { $temp2[] = $model->db_object->prepare_condition($v['options_active'], 'AND'); } $where2[] = '(' . implode(' OR ', $temp2) . ')'; // processing existing values if (!empty($v['existing_values'])) { $existing_values = [$v['field'] => $v['existing_values']]; $where2[] = $model->db_object->prepare_condition($existing_values, 'AND'); } //print_r2($where2); // params must be there $where3 = $model->db_object->prepare_condition($v['params'], 'AND'); if (empty($where3)) { $where3 = '1=1'; } $where = '((' . $where3 . ') AND (' . implode(' OR ', $where2) . '))'; //$where = $this->db_object->prepare_condition(array_merge_hard($v['params'] ?? [], $temp), 'AND'); $fields = "concat_ws('', " . implode(', ', array_keys($values)) . ")"; $mass_sql[] = <<<TTT \t\t\t\tSELECT \t\t\t\t\t'{$k}' validate_name, \t\t\t\t\t{$fields} validate_value \t\t\t\tFROM {$model->name} \t\t\t\tWHERE 1=1 \t\t\t\t\tAND {$where} TTT; } $mass_sql = implode("\n\nUNION ALL\n\n", $mass_sql); $temp = $model->db_object->query($mass_sql); if ($temp['success']) { // generate array of unique values $unique = []; foreach ($temp['rows'] as $k => $v) { if (!isset($unique[$v['validate_name']])) { $unique[$v['validate_name']] = []; } $unique[$v['validate_name']][] = $v['validate_value']; } // find differencies foreach ($options as $k => $v) { // see if we found values if (!isset($unique[$k])) { $result['discrepancies'][$k] = count($v['values']); } else { foreach ($v['values'] as $v2) { if (!in_array($v2 . '', $unique[$k])) { if (!isset($result['discrepancies'][$k])) { $result['discrepancies'][$k] = 0; } $result['discrepancies'][$k]++; } } } } $result['success'] = true; } return $result; }
public function process($data, $options = []) { $temp = []; // we need to precess items that are controller and suboptions at the same time $subgroups = []; foreach ($data as $k => $v) { // determine acl if (!empty($v['sm_menuitm_acl_controller_id']) && !helper_acl::can_see_this_controller($v['sm_menuitm_acl_controller_id'], $v['sm_menuitm_acl_action_id'])) { unset($data[$k]); continue; } // go though each group for ($i = 1; $i <= 4; $i++) { if (!empty($v["g{$i}_code"])) { $subgroups[$v["g{$i}_code"]] = true; } } } $subgroup_items = []; foreach ($data as $k => $v) { if (isset($subgroups[$v['sm_menuitm_code']])) { $subgroup_items[$v['sm_menuitm_code']] = $v; unset($data[$k]); } } // loop though data foreach ($data as $k => $v) { // loop though groups and add them to menu $key = []; for ($i = 1; $i <= 4; $i++) { if (!empty($v['g' . $i . '_code'])) { $key[] = $v['g' . $i . '_code']; // we need to set all groups $temp2 = array_key_get($temp, $key); if (is_null($temp2)) { // if we have a controller that acts as submenu if (!empty($subgroup_items[$v['g' . $i . '_code']])) { $v9 = $subgroup_items[$v['g' . $i . '_code']]; array_key_set($temp, $key, ['code' => $v9['sm_menuitm_code'], 'name' => $v9['sm_menuitm_name'], 'name_extension' => null, 'icon' => $v9['sm_menuitm_icon'], 'url' => $v9['sm_menuitm_url'], 'order' => $v9['sm_menuitm_order'], 'options' => []]); } else { // if we do not have url we assume visitor wants to see extended menu if (empty($v['g' . $i . '_url'])) { $params = []; for ($j = 1; $j <= $i; $j++) { $params['group' . $j . '_code'] = $v['g' . $j . '_code']; } $v['g' . $i . '_url'] = '/numbers/backend/system/menu/controller/menu?' . http_build_query2($params); } array_key_set($temp, $key, ['code' => $v['g' . $i . '_code'], 'name' => $v['g' . $i . '_name'], 'icon' => $v['g' . $i . '_icon'], 'order' => $v['g' . $i . '_order'], 'url' => $v['g' . $i . '_url'], 'options' => []]); } } $key[] = 'options'; } } // some replaces $name_extension = null; if ($v['sm_menuitm_code'] == 'entites.authorization.__entity_name') { $name_extension = '<b>' . session::get(['numbers', 'entity', 'em_entity_name']) . '</b>'; } // finally we need to add menu item to the array $key[] = $v['sm_menuitm_code']; array_key_set($temp, $key, ['code' => $v['sm_menuitm_code'], 'name' => $v['sm_menuitm_name'], 'name_extension' => $name_extension, 'icon' => $v['sm_menuitm_icon'], 'url' => $v['sm_menuitm_url'], 'order' => $v['sm_menuitm_order'], 'options' => []]); // options generator if (!empty($v['sm_menuitm_options_generator'])) { $temp3 = explode('::', $v['sm_menuitm_options_generator']); $temp_data = factory::model($temp3[0])->{$temp3[1]}(); $temp_key = $key; $temp_key[] = 'options'; foreach ($temp_data as $k2 => $v2) { $temp_key2 = $temp_key; $temp_key2[] = $k2; array_key_set($temp, $temp_key2, $v2); } } } // sorting foreach ($temp as $k => $v) { if (!empty($v['options'])) { foreach ($v['options'] as $k2 => $v2) { if (!empty($v2['options'])) { foreach ($v2['options'] as $k3 => $v3) { if (!empty($v3['options'])) { foreach ($v3['options'] as $k4 => $v4) { if (!empty($v4['options'])) { array_key_sort($temp[$k]['options'][$k2]['options'][$k3]['options'][$k4]['options'], ['order' => SORT_ASC], ['order' => SORT_NUMERIC]); } } array_key_sort($temp[$k]['options'][$k2]['options'][$k3]['options'], ['order' => SORT_ASC], ['order' => SORT_NUMERIC]); } } array_key_sort($temp[$k]['options'][$k2]['options'], ['order' => SORT_ASC], ['order' => SORT_NUMERIC]); } } array_key_sort($temp[$k]['options'], ['order' => SORT_ASC], ['order' => SORT_NUMERIC]); } } // sort root array_key_sort($temp, ['order' => SORT_ASC], ['order' => SORT_NUMERIC]); return $temp; }
/** * Convert method string to an array for future execution * * @param string $method * @param string $base_class * @return array */ public static function method($method, $base_class = null, $model = false) { $temp = explode('::', $method); if (count($temp) > 1) { $temp_model = $temp[0]; $temp_method = $temp[1]; } else { $temp_model = $base_class; $temp_method = $temp[0]; } if ($model) { $temp_model = factory::model($temp_model, true); } return [&$temp_model, $temp_method]; }
/** * Constructor * * @param array $options * input - form input * form - form options * segment - segment options * type * header * footer */ public function __construct($options = []) { // we need to handle overrrides parent::override_handle($this); // step 0: apply data fixes if (method_exists($this, 'overrides')) { $this->overrides($this); } // we need to merge override input if (!empty($this->values)) { $options['input'] = array_merge_hard($options['input'] ?? [], $this->values); } if (isset($options['form_link'])) { $this->form_link = $options['form_link']; } // step 1: create form object $this->form_object = new numbers_frontend_html_form_base($this->form_link, array_merge_hard($this->options, $options)); // class $this->form_object->form_class = get_called_class(); $this->form_object->form_parent =& $this; $this->form_object->acl = $this->acl; // add collection $this->form_object->collection = $this->collection; $this->form_object->preload_collection_object(); // must initialize it before calls to container/row/element $this->form_object->column_prefix = $this->column_prefix ?? $this->form_object->collection_object->primary_model->column_prefix ?? null; // master object if (!empty($this->master_options['model'])) { $this->master_options['type'] = $this->master_options['type'] ?? ''; $this->master_options['ledger'] = strtoupper($this->master_options['ledger']) ?? ''; $this->form_object->master_options = $this->master_options; $this->form_object->master_object = factory::model($this->master_options['model'], true); } // title if (!empty($this->title)) { $this->form_object->title = $this->title; } else { // we generate a title based on class name $temp = explode('_model_form_', get_called_class()); $temp = explode('_', $temp[1]); $this->title = $this->form_object->title = ucwords(implode(' ', $temp)); } // step 2: create all containers foreach ($this->containers as $k => $v) { if ($v === null) { continue; } $this->form_object->container($k, $v); } // step 3: create all rows foreach ($this->rows as $k => $v) { foreach ($v as $k2 => $v2) { if ($v2 === null) { continue; } $this->form_object->row($k, $k2, $v2); } } // step 3: create all elements foreach ($this->elements as $k => $v) { foreach ($v as $k2 => $v2) { foreach ($v2 as $k3 => $v3) { if ($v3 === null) { continue; } $this->form_object->element($k, $k2, $k3, $v3); } } } // step 3: methods foreach (['save', 'validate', 'refresh', 'success', 'pre_render', 'override_field_value', 'override_tabs', 'process_default_value'] as $v) { if (method_exists($this, $v)) { $this->form_object->wrapper_methods[$v]['main'] = [&$this, $v]; } } // extensions can have their own verify methods if (!empty($this->wrapper_methods)) { foreach ($this->wrapper_methods as $k => $v) { $index = 1; foreach ($v as $k2 => $v2) { $this->form_object->wrapper_methods[$k][$index] = [new $k2(), $v2]; $index++; } } } // last step: process form if (empty($options['skip_processing'])) { $this->form_object->process(); } }
/** * Render pagination * * @param object $object * @return string */ public function render($object, $type) { // fetched $fetched = i18n(null, 'Fetched') . ': ' . i18n(null, $object->num_rows) . ($object->total > 0 ? ' ' . i18n(null, 'of') . ' ' . i18n(null, $object->total) : ''); // sorting $sort = ''; if (!empty($object->orderby)) { $sort .= i18n(null, 'Sort') . ': '; $temp = []; foreach ($object->orderby as $k => $v) { if ($k == 'full_text_search') { $temp[] = i18n(null, 'Text Search') . ' ' . html::icon(['type' => 'sort-alpha-' . ($v == SORT_ASC ? 'asc' : 'desc')]); } else { $temp[] = i18n(null, $object->columns[$k]['name']) . ' ' . html::icon(['type' => 'sort-alpha-' . ($v == SORT_ASC ? 'asc' : 'desc')]); } } $sort .= implode(', ', $temp); } // displaying $displaying = i18n(null, 'Displaying') . ' '; $displaying .= '<div style="width: 80px; display: inline-block;">' . html::select(['id' => 'page_sizes_' . $type, 'options' => factory::model('numbers_frontend_html_list_pagination_pagesizes', true)->options(['i18n' => 'skip_sorting']), 'value' => $object->limit, 'no_choose' => true, 'onchange' => "\$('#offset').val(0); \$('#limit').val(this.value); numbers.frontend_list.trigger_submit(this.form);"]) . '</div>'; // navigation $navigation = []; $flag_next_row_exists = false; $flag_last_row_exists = false; $current_page = intval($object->offset / $object->limit); if ($current_page >= 1) { $navigation[] = html::button2(['value' => i18n(null, 'First'), 'onclick' => "\$('#offset').val(0); numbers.frontend_list.trigger_submit(this.form);"]); } if ($current_page >= 2) { $previous = ($current_page - 1) * $object->limit; $navigation[] = html::button2(['value' => i18n(null, 'Previous'), 'onclick' => "\$('#offset').val({$previous}); numbers.frontend_list.trigger_submit(this.form);"]); } // select with number of pages $pages = ceil($object->total / $object->limit); if ($object->num_rows) { $temp = []; for ($i = 0; $i < $pages; $i++) { $temp[$i * $object->limit] = ['name' => i18n(null, $i + 1)]; } $navigation2 = i18n(null, 'Page') . ': '; $previous = ($current_page - 1) * $object->limit; $navigation2 .= '<div style="width: 100px; display: inline-block;">' . html::select(['id' => 'pages_' . $type, 'options' => $temp, 'value' => $object->offset, 'no_choose' => true, 'onchange' => "\$('#offset').val(this.value); numbers.frontend_list.trigger_submit(this.form);"]) . '</div>'; $navigation[] = $navigation2; // checking for next and last pages $flag_next_row_exists = $pages - $current_page - 2 > 0 ? true : false; $flag_last_row_exists = $pages - $current_page - 1 > 0 ? true : false; } else { $navigation[] = i18n(null, 'Page') . ': ' . ($current_page + 1); } if ($flag_next_row_exists) { $next = ($current_page + 1) * $object->limit; $navigation[] = html::button2(['value' => i18n(null, 'Next'), 'onclick' => "\$('#offset').val({$next}); numbers.frontend_list.trigger_submit(this.form);"]); } if ($flag_last_row_exists) { $last = ($pages - 1) * $object->limit; $navigation[] = html::button2(['value' => i18n(null, 'Last'), 'onclick' => "\$('#offset').val({$last}); numbers.frontend_list.trigger_submit(this.form);"]); } // generating grid $grid = ['options' => [0 => ['Displaying' => ['Displaying' => ['value' => $displaying, 'options' => ['field_size' => 'col-xs-6 col-sm-6 col-lg-2', 'percent' => 15, 'style' => 'height: 40px; line-height: 40px;']]], 'Fetched' => ['Fetched' => ['value' => $fetched, 'options' => ['field_size' => 'col-xs-6 col-sm-6 col-lg-2', 'percent' => 15, 'style' => 'height: 40px; line-height: 40px;']]], 'Sort' => ['Sort' => ['class' => 'list_pagination_sort', 'value' => $sort, 'options' => ['field_size' => 'col-xs-12 col-sm-12 col-lg-3', 'percent' => 15, 'style' => 'height: 40px; line-height: 40px;']]], 'Navigation' => ['Navigation' => ['class' => 'list_pagination_navigation', 'value' => implode(' ', $navigation), 'options' => ['field_size' => 'col-xs-12 col-sm-12 col-lg-5', 'percent' => 50, 'style' => 'height: 40px; line-height: 40px;']]]]]]; return html::grid($grid); }
/** * Process model * * @param string $model_class * @return array * @throws Exception */ public function process_table_model($model_class) { $result = ['success' => false, 'error' => []]; do { // table model $model = is_object($model_class) ? $model_class : factory::model($model_class, true); $db = factory::get(['db', $model->db_link]); $ddl_object = $db['ddl_object']; $owner = $db['object']->connect_options['username']; $engine = $model->engine[$db['backend']] ?? null; // process columns if (empty($model->columns)) { $result['error'][] = 'Table ' . $model->name . ' must have atleast one column!'; break; } // columns would be here $columns = []; foreach ($model->columns as $k => $v) { $v['column_name'] = $k; $column_temp = $ddl_object->is_column_type_supported($v, $model->name); $columns[$k] = $column_temp['column']; // handle sequence if (!empty($column_temp['column']['sequence'])) { $this->object_add(['type' => 'sequence', 'schema' => '', 'name' => $column_temp['column']['sequence'], 'data' => ['owner' => $owner, 'full_sequence_name' => $column_temp['column']['sequence'], 'type' => 'simple', 'prefix' => null, 'length' => 0, 'suffix' => null]], $model->db_link); } } $this->object_add(['type' => 'table', 'schema' => '', 'name' => $model->name, 'data' => ['columns' => $columns, 'owner' => $owner, 'full_table_name' => $model->name, 'engine' => $engine]], $model->db_link); // history if ($model->history) { if (empty($model->who['inserted']) || empty($model->who['updated'])) { $result['error'][] = 'History table ' . $model->name . ' must have inserted and updated who timestamps!'; } // fix columns with serial types $columns_history = $columns; foreach ($model->pk as $v) { foreach (['serial' => 'integer', 'bigserial' => 'bigint', 'smallserial' => 'smallint'] as $k2 => $v2) { if ($columns_history[$v]['type'] == $k2) { $columns_history[$v]['type'] = $v2; } } } // add new history table $this->object_add(['type' => 'table', 'schema' => '', 'name' => $model->name . '__history', 'data' => ['columns' => $columns_history, 'owner' => $owner, 'full_table_name' => $model->history_name, 'engine' => $engine]], $model->db_link); } // processing constraints if (!empty($model->constraints)) { foreach ($model->constraints as $k => $v) { $v['full_table_name'] = $model->name; // additional processing for fk type constraints if ($v['type'] == 'fk') { $temp_object = factory::model($v['foreign_model'], true); $v['foreign_table'] = $temp_object->name; } $this->object_add(['type' => 'constraint', 'schema' => '', 'table' => $model->name, 'name' => $k, 'data' => $v], $model->db_link); } } // processing indexes if (!empty($model->indexes)) { foreach ($model->indexes as $k => $v) { // we skipp full text indexes for pgsql if ($db['backend'] == 'pgsql' && $v['type'] == 'fulltext') { continue; } $v['full_table_name'] = $model->name; $this->object_add(['type' => 'index', 'schema' => '', 'table' => $model->name, 'name' => $k, 'data' => $v], $model->db_link); } } // if we got here - we are ok $result['success'] = true; } while (0); return $result; }
/** * Process models * * @param array $options * @return array */ public static function process_models($options = []) { $result = ['success' => false, 'error' => [], 'hint' => [], 'data' => []]; do { // we need to process all dependencies first $dep = self::process_deps_all($options); if (!$dep['success']) { $result = $dep; $result['error'][] = 'You must fix all dependency related errors first before processing models.'; break; } // proccesing models if (empty($dep['data']['model_processed'])) { $result['error'][] = 'You do not have models to process!'; break; } $object_attributes = []; $object_relations = []; $object_forms = []; $flag_relation = application::get('dep.submodule.numbers.data.relations') ? true : false; $object_documentation = []; $object_import = []; $ddl = new numbers_backend_db_class_ddl(); // run 1 to deterine virtual tables $first = true; $virtual_models = $dep['data']['model_processed']; run_again: foreach ($virtual_models as $k => $v) { $k2 = str_replace('.', '_', $k); if ($v == 'object_table') { $model = factory::model($k2, true); foreach (['attributes', 'audit', 'addresses'] as $v0) { if ($model->{$v0}) { $v01 = $v0 . '_model'; $virtual_models[str_replace('_', '.', $model->{$v01})] = 'object_table'; } } } } if ($first) { $first = false; goto run_again; // some widgets have attributes } $dep['data']['model_processed'] = array_merge_hard($dep['data']['model_processed'], $virtual_models); $domains = object_data_domains::get_static(); // run 2 foreach ($dep['data']['model_processed'] as $k => $v) { $k2 = str_replace('.', '_', $k); if ($v == 'object_table') { $model = factory::model($k2, true); $temp_result = $ddl->process_table_model($model); if (!$temp_result['success']) { array_merge3($result['error'], $temp_result['error']); } $object_documentation[$v][$k2] = $k2; // relation if ($flag_relation) { if (!empty($model->relation)) { $domain = $model->columns[$model->relation['field']]['domain'] ?? null; if (!empty($domain)) { $domain = str_replace('_sequence', '', $domain); $type = $domains[$domain]['type']; } else { $type = $model->columns[$model->relation['field']]['type']; } $object_relations[$k2] = ['rn_relattr_code' => $model->relation['field'], 'rn_relattr_name' => $model->title, 'rn_relattr_model' => $k2, 'rn_relattr_domain' => $domain, 'rn_relattr_type' => $type, 'rn_relattr_inactive' => !empty($model->relation['inactive']) ? 1 : 0]; } if (!empty($model->attributes)) { $object_attributes[$k2] = ['rn_attrmdl_code' => $k2, 'rn_attrmdl_name' => $model->title, 'rn_attrmdl_inactive' => 0]; } } } else { if ($v == 'object_sequence') { $temp_result = $ddl->process_sequence_model($k2); if (!$temp_result['success']) { array_merge3($result['error'], $temp_result['error']); } $object_documentation[$v][$k2] = $k2; } else { if ($v == 'object_function') { $temp_result = $ddl->process_function_model($k2); if (!$temp_result['success']) { array_merge3($result['error'], $temp_result['error']); } $object_documentation[$v][$k2] = $k2; } else { if ($v == 'object_extension') { $temp_result = $ddl->process_function_extension($k2); if (!$temp_result['success']) { array_merge3($result['error'], $temp_result['error']); } $object_documentation[$v][$k2] = $k2; } else { if ($v == 'object_import') { $object_import[$k2] = ['model' => $k2]; } } } } } } // if we have erros if (!empty($result['error'])) { break; } // db factory $db_factory = factory::get('db'); // we load objects from database $loaded_objects = []; foreach ($ddl->db_links as $k => $v) { $ddl_object = $db_factory[$k]['ddl_object']; $temp_result = $ddl_object->load_schema($k); if (!$temp_result['success']) { array_merge3($result['error'], $temp_result['error']); } else { $loaded_objects[$k] = $temp_result['data']; } } // if we have erros if (!empty($result['error'])) { break; } // get a list of all db links $db_link_list = array_unique(array_merge(array_keys($ddl->objects), array_keys($loaded_objects))); // if we are dropping schema if ($options['mode'] == 'drop') { $ddl->objects = []; } // compare schemas per db link $schema_diff = []; $total_per_db_link = []; $total = 0; foreach ($db_link_list as $k) { // we need to have a back end for comparison $compare_options['backend'] = $db_factory[$k]['backend']; // comparing $temp_result = $ddl->compare_schemas(isset($ddl->objects[$k]) ? $ddl->objects[$k] : [], isset($loaded_objects[$k]) ? $loaded_objects[$k] : [], $compare_options); if (!$temp_result['success']) { array_merge3($result['hint'], $temp_result['error']); } else { $schema_diff[$k] = $temp_result['data']; if (!isset($total_per_db_link[$k])) { $total_per_db_link[$k] = 0; } $total_per_db_link[$k] += $temp_result['count']; $total += $temp_result['count']; } } // if there's no schema changes if ($total == 0) { if ($options['mode'] == 'commit') { goto import_data; } else { $result['success'] = true; } break; } // we need to provide a list of changes foreach ($total_per_db_link as $k => $v) { $result['hint'][] = ''; $result['hint'][] = "Db link {$k} requires {$v} changes!"; // printing summary $result['hint'][] = ' * Link ' . $k . ': '; foreach ($schema_diff[$k] as $k2 => $v2) { $result['hint'][] = ' * ' . $k2 . ': '; foreach ($v2 as $k3 => $v3) { $result['hint'][] = ' * ' . $k3 . ' - ' . $v3['type']; } } } // if we are in no commit mode we exit if (!in_array($options['mode'], ['commit', 'drop'])) { break; } // generating sql foreach ($total_per_db_link as $k => $v) { if ($v == 0) { continue; } $ddl_object = $db_factory[$k]['ddl_object']; foreach ($schema_diff[$k] as $k2 => $v2) { foreach ($v2 as $k3 => $v3) { // we need to make fk constraints last to sort MySQL issues if ($k2 == 'new_constraints' && $v3['type'] == 'constraint_new' && $v3['data']['type'] == 'fk') { $schema_diff[$k][$k2 . '_fks'][$k3]['sql'] = $ddl_object->render_sql($v3['type'], $v3); } else { $schema_diff[$k][$k2][$k3]['sql'] = $ddl_object->render_sql($v3['type'], $v3, ['mode' => $options['mode']]); } } } } // print_r($schema_diff); // exit; // executing sql foreach ($total_per_db_link as $k => $v) { if ($v == 0) { continue; } $db_object = new db($k); // if we are dropping we need to disable foregn key checks if ($options['mode'] == 'drop') { if ($db_object->backend == 'mysqli') { $db_object->query('SET foreign_key_checks = 0;'); // we also need to unset sequences unset($schema_diff[$k]['delete_sequences']); } } foreach ($schema_diff[$k] as $k2 => $v2) { foreach ($v2 as $k3 => $v3) { if (empty($v3['sql'])) { continue; } if (is_array($v3['sql'])) { $temp = $v3['sql']; } else { $temp = [$v3['sql']]; } foreach ($temp as $v4) { $temp_result = $db_object->query($v4); if (!$temp_result['success']) { array_merge3($result['error'], $temp_result['error']); goto error; } } } } } // if we got here - we are ok $result['success'] = true; } while (0); import_data: // we need to import data if (!empty($object_import) && $options['mode'] == 'commit') { $result['hint'][] = ''; foreach ($object_import as $k => $v) { $data_object = new $k(); $data_result = $data_object->process(); if (!$data_result['success']) { throw new Exception(implode("\n", $data_result['error'])); } $result['hint'] = array_merge($result['hint'], $data_result['hint']); } } // relation if ($flag_relation && $options['mode'] == 'commit') { $result['hint'][] = ''; $model2 = factory::model('numbers_data_relations_model_relation_attributes'); // insert new models if (!empty($object_relations)) { foreach ($object_relations as $k => $v) { $result_insert = $model2->save($v, ['pk' => ['rn_relattr_code'], 'ignore_not_set_fields' => true]); } $result['hint'][] = ' * Imported relation models!'; } // we need to process forms foreach ($dep['data']['submodule_dirs'] as $v) { $dir = $v . 'model/form/'; if (!file_exists($dir)) { continue; } $files = helper_file::iterate($dir, ['only_extensions' => ['php']]); foreach ($files as $v2) { $model_name = str_replace(['../libraries/vendor/', '.php'], '', $v2); $model_name = str_replace('/', '_', $model_name); $model = new $model_name(['skip_processing' => true]); if (empty($model->form_object->misc_settings['option_models'])) { continue; } // loop though fields foreach ($model->form_object->misc_settings['option_models'] as $k3 => $v3) { $object_forms[$model_name . '::' . $k3] = ['rn_relfrmfld_form_code' => $model_name, 'rn_relfrmfld_form_name' => $model->title, 'rn_relfrmfld_field_code' => $k3, 'rn_relfrmfld_field_name' => $v3['field_name'], 'rn_relfrmfld_relattr_id' => $v3['model'], 'rn_relfrmfld_inactive' => 0]; } } } if (!empty($object_forms)) { // load all relation models $data = $model2->get(['pk' => ['rn_relattr_model']]); $model = factory::model('numbers_data_relations_model_relation_formfields'); foreach ($object_forms as $k => $v) { if (empty($data[$v['rn_relfrmfld_relattr_id']])) { continue; } $v['rn_relfrmfld_relattr_id'] = $data[$v['rn_relfrmfld_relattr_id']]['rn_relattr_id']; $result_insert = $model->save($v, ['pk' => ['rn_relfrmfld_form_code', 'rn_relfrmfld_field_code'], 'ignore_not_set_fields' => true]); } $result['hint'][] = ' * Imported relation form fields!'; } // todo: import models //print_r2($object_attributes); if (!empty($object_attributes)) { $model = factory::model('numbers_data_relations_model_attribute_models'); foreach ($object_attributes as $k => $v) { $result_insert = $model->save($v, ['pk' => ['rn_attrmdl_code'], 'ignore_not_set_fields' => true]); } $result['hint'][] = ' * Imported attribute models!'; } } // we need to generate documentation $system_documentation = application::get('system_documentation'); if (!empty($system_documentation) && $options['mode'] == 'commit') { $model = factory::model($system_documentation['model']); /* print_r2($object_documentation); $documentation_result = $model->update($object_documentation, $system_documentation); if (!$documentation_result['success']) { $result['error'] = array_merge($result['error'], $documentation_result['error']); } */ } error: return $result; }
/** * Render list * * @return string */ public final function render() { $result = ''; // css & js layout::add_css('/numbers/media_submodules/numbers_frontend_html_list_media_css_base.css', 9000); layout::add_js('/numbers/media_submodules/numbers_frontend_html_list_media_js_base.js', 9000); // load mask numbers_frontend_media_libraries_loadmask_base::add(); // hidden fields $result .= html::hidden(['name' => 'offset', 'id' => 'offset', 'value' => $this->offset]); $result .= html::hidden(['name' => 'limit', 'id' => 'limit', 'value' => $this->limit]); // get total number of rows from count datasource if (!empty($this->datasources['count'])) { $temp = factory::model($this->datasources['count']['model'])->get($this->datasources['count']['options']); $this->total = $temp[0]['count'] ?? 0; } // get rows if (!empty($this->datasources['data'])) { $this->rows = factory::model($this->datasources['data']['model'])->get($this->datasources['data']['options']); $this->num_rows = count($this->rows); } // new record if (object_controller::can('record_new')) { $mvc = application::get('mvc'); $url = $mvc['controller'] . '/_edit'; $this->actions['list_new'] = ['value' => 'New', 'sort' => -32000, 'icon' => 'file-o', 'href' => $url]; } // filter if (!empty($this->filter)) { $this->actions['list_filter'] = ['value' => 'Filter', 'sort' => 1, 'icon' => 'filter', 'onclick' => "numbers.modal.show('list_{$this->list_link}_filter');"]; $result .= numbers_frontend_html_list_filter::render($this); } // order by $this->actions['list_sort'] = ['value' => 'Sort', 'sort' => 2, 'icon' => 'sort-alpha-asc', 'onclick' => "numbers.modal.show('list_{$this->list_link}_sort');"]; $result .= numbers_frontend_html_list_sort::render($this); // export, before pagination if (object_controller::can('list_export')) { // add export link to the panel $result .= numbers_frontend_html_list_export::render($this); $this->actions['list_export'] = ['value' => 'Export/Print', 'sort' => 3, 'icon' => 'print', 'onclick' => "numbers.modal.show('list_{$this->list_link}_export');"]; // if we are exporting if (!empty($this->options['input']['submit_export']) && !empty($this->options['input']['export']['format'])) { $result .= numbers_frontend_html_list_export::export($this, $this->options['input']['export']['format']); goto finish; } } // refresh $this->actions['form_refresh'] = ['value' => 'Refresh', 'sort' => 32000, 'icon' => 'refresh', 'href' => 'javascript:location.reload();', 'internal_action' => true]; // pagination top if (!empty($this->pagination['top'])) { $result .= factory::model($this->pagination['top'])->render($this, 'top'); } // data $result .= '<hr class="simple"/>'; if (method_exists($this, 'render_data')) { $result .= $this->render_data(); } else { $result .= $this->render_data_default(); } $result .= '<hr class="simple"/>'; // pagination bottom if (!empty($this->pagination['bottom'])) { $result .= factory::model($this->pagination['bottom'])->render($this, 'bottom'); } finish: $value = ''; if (!empty($this->actions)) { $value .= '<div style="text-align: right;">' . $this->render_actions() . '</div>'; $value .= '<hr class="simple" />'; } // we add hidden submit element $result .= html::submit(['name' => 'submit_hidden', 'value' => 1, 'style' => 'display: none;']); // build a form $value .= html::form(['name' => "list_{$this->list_link}_form", 'id' => "list_{$this->list_link}_form", 'value' => $result, 'onsubmit' => 'return numbers.frontend_list.on_form_submit(this);']); // if we came from ajax we return as json object if (!empty($this->options['input']['__ajax'])) { $result = ['success' => true, 'html' => $value, 'js' => layout::$onload]; layout::render_as($result, 'application/json'); } $value = "<div id=\"list_{$this->list_link}_form_mask\"><div id=\"list_{$this->list_link}_form_wrapper\">" . $value . '</div></div>'; $temp = ['type' => 'primary', 'value' => $value]; return html::segment($temp); }
/** * Get data as an array of rows * * @param array $options * no_cache - if we need to skip caching * search - array of search condition * where - array of where conditions * orderby - array of columns to sort by * pk - primary key to be used by query * columns - if we need to get certain columns * limit - set this integer if we need to limit query * @return array */ public function get($options = []) { $data = []; $this->acl_get_options = $options; // handle acl init if (!empty($options['acl'])) { $acl_key = get_called_class(); if (factory::model('object_acl_class', true)->acl_init($acl_key, $data, $this->acl_get_options) === false) { return $data; } $options = $this->acl_get_options; } $options_query = []; // if we are caching if (!empty($this->cache) && empty($options['no_cache'])) { $options_query['cache'] = true; } $options_query['cache_tags'] = !empty($this->cache_tags) ? array_values($this->cache_tags) : []; $options_query['cache_tags'][] = $this->name; $sql = ''; // pk $pk = array_key_exists('pk', $options) ? $options['pk'] : $this->pk; // preset columns if (!empty($options['__preset'])) { $columns = 'DISTINCT '; if (!empty($pk) && count($pk) > 1) { $temp = $pk; unset($temp[array_search('preset_value', $temp)]); $columns .= $this->db_object->prepare_expression($temp) . ', '; } $columns .= "concat_ws(' ', " . $this->db_object->prepare_expression($options['columns']) . ") preset_value"; $sql .= ' AND coalesce(' . $this->db_object->prepare_expression($options['columns']) . ') IS NOT NULL'; // if its a preset we cache $options_query['cache'] = true; } else { // regular columns if (!empty($options['columns'])) { $columns = $this->db_object->prepare_expression($options['columns']); } else { $columns = '*'; } } // where $sql .= !empty($options['where']) ? ' AND ' . $this->db_object->prepare_condition($options['where']) : ''; $sql .= !empty($options['search']) ? ' AND (' . $this->db_object->prepare_condition($options['search'], 'OR') . ')' : ''; // order by $orderby = $options['orderby'] ?? (!empty($this->orderby) ? $this->orderby : null); if (!empty($orderby)) { $sql .= ' ORDER BY ' . array_key_sort_prepare_keys($orderby, true); } // limit if (!empty($options['limit'])) { $sql .= ' LIMIT ' . $options['limit']; } else { if (!empty($this->limit)) { $sql .= ' LIMIT ' . $this->limit; } } // querying $sql_full = 'SELECT ' . $columns . ' FROM ' . $this->name . ' a WHERE 1=1' . $sql; // memory caching if ($this->cache_memory) { // hash is query + primary key $crypt = new crypt(); $sql_hash = $crypt->hash($sql_full . serialize($pk)); if (isset(cache::$memory_storage[$sql_hash])) { return cache::$memory_storage[$sql_hash]; } } $result = $this->db_object->query($sql_full, $pk, $options_query); if (!$result['success']) { throw new Exception(implode(", ", $result['error'])); } if ($this->cache_memory) { cache::$memory_storage[$sql_hash] =& $result['rows']; } // single row if (!empty($options['single_row'])) { $data = current($result['rows']); } else { $data = $result['rows']; } // handle acl init if (!empty($options['acl'])) { if (factory::model('object_acl_class', true)->acl_finish($acl_key, $data, $this->acl_get_options) === false) { return $data; } } return $data; }
/** * Get data from datasource * * @param array $options * @return array * @throws Exception */ public final function get($options = []) { // check if we have query method if (!method_exists($this, 'query')) { throw new Exception('You must specify sql in query method!'); } $data = []; $this->acl_get_options = $options; // handle acl init if (!empty($options['acl'])) { $acl_key = get_called_class(); if (factory::model('object_acl_class', true)->acl_init($acl_key, $data, $this->acl_get_options) === false) { return $data; } $options = $this->acl_get_options; } // get query $sql = $this->query($options); // process query $result = $this->process_query($sql, $this->pk, $options); if (method_exists($this, 'process')) { // process data $data = $this->process($result['rows'], $options); } else { $data = $result['rows']; } // handle acl init if (!empty($options['acl'])) { if (factory::model('object_acl_class', true)->acl_finish($acl_key, $data, $this->acl_get_options) === false) { return $data; } } return $data; }
/** * 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%"> </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%"> </td>'; $temp .= '<td width="90%">' . $value . '</td>'; $temp .= '<td width="1%"> </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%"> </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%"> </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; }