diff --git a/application/libraries/Datatables.php b/application/libraries/Datatables.php index c6de11f..531931d 100644 --- a/application/libraries/Datatables.php +++ b/application/libraries/Datatables.php @@ -1,640 +1,916 @@ - - * @author Vincent Bambico - * Yusuf Ozdemir - * @link http://ellislab.com/forums/viewthread/160896/ - */ - class Datatables - { - /** - * Global container variables for chained argument results - * - */ + + * @author Vincent Bambico + * Yusuf Ozdemir + * @link http://ellislab.com/forums/viewthread/160896/ + */ +class Datatables +{ + /** + * Global container variables for chained argument results + * + */ private $ci; private $table; private $distinct; - private $group_by = array(); - private $select = array(); - private $joins = array(); - private $columns = array(); - private $where = array(); - private $or_where = array(); - private $where_in = array(); - private $like = array(); - private $or_like = array(); - private $filter = array(); - private $add_columns = array(); - private $edit_columns = array(); - private $unset_columns = array(); - - /** - * Copies an instance of CI - */ - public function __construct() - { - $this->ci =& get_instance(); - } - - /** - * If you establish multiple databases in config/database.php this will allow you to - * set the database (other than $active_group) - more info: http://ellislab.com/forums/viewthread/145901/#712942 - */ - public function set_database($db_name) - { - $db_data = $this->ci->load->database($db_name, TRUE); - $this->ci->db = $db_data; - } - - /** - * Generates the SELECT portion of the query - * - * @param string $columns - * @param bool $backtick_protect - * @return mixed - */ - public function select($columns, $backtick_protect = TRUE) - { - foreach($this->explode(',', $columns) as $val) - { - $column = trim(preg_replace('/(.*)\s+as\s+(\w*)/i', '$2', $val)); - $column = preg_replace('/.*\.(.*)/i', '$1', $column); // get name after `.` - $this->columns[] = $column; - $this->select[$column] = trim(preg_replace('/(.*)\s+as\s+(\w*)/i', '$1', $val)); - } - - $this->ci->db->select($columns, $backtick_protect); - return $this; - } - - /** - * Generates the DISTINCT portion of the query - * - * @param string $column - * @return mixed - */ - public function distinct($column) - { - $this->distinct = $column; - $this->ci->db->distinct($column); - return $this; - } - - /** - * Generates a custom GROUP BY portion of the query - * - * @param string $val - * @return mixed - */ - public function group_by($val) - { - $this->group_by[] = $val; - $this->ci->db->group_by($val); - return $this; - } - - /** - * Generates the FROM portion of the query - * - * @param string $table - * @return mixed - */ - public function from($table) - { - $this->table = $table; - return $this; - } - - /** - * Generates the JOIN portion of the query - * - * @param string $table - * @param string $fk - * @param string $type - * @return mixed - */ - public function join($table, $fk, $type = NULL) - { - $this->joins[] = array($table, $fk, $type); - $this->ci->db->join($table, $fk, $type); - return $this; - } - - /** - * Generates the WHERE portion of the query - * - * @param mixed $key_condition - * @param string $val - * @param bool $backtick_protect - * @return mixed - */ - public function where($key_condition, $val = NULL, $backtick_protect = TRUE) - { - $this->where[] = array($key_condition, $val, $backtick_protect); - $this->ci->db->where($key_condition, $val, $backtick_protect); - return $this; - } - - /** - * Generates the WHERE portion of the query - * - * @param mixed $key_condition - * @param string $val - * @param bool $backtick_protect - * @return mixed - */ - public function or_where($key_condition, $val = NULL, $backtick_protect = TRUE) - { - $this->or_where[] = array($key_condition, $val, $backtick_protect); - $this->ci->db->or_where($key_condition, $val, $backtick_protect); - return $this; - } - - /** - * Generates the WHERE IN portion of the query - * - * @param mixed $key_condition - * @param string $val - * @param bool $backtick_protect - * @return mixed - */ - public function where_in($key_condition, $val = NULL) - { - $this->where_in[] = array($key_condition, $val); - $this->ci->db->where_in($key_condition, $val); - return $this; - } - - /** - * Generates the WHERE portion of the query - * - * @param mixed $key_condition - * @param string $val - * @param bool $backtick_protect - * @return mixed - */ - public function filter($key_condition, $val = NULL, $backtick_protect = TRUE) - { - $this->filter[] = array($key_condition, $val, $backtick_protect); - return $this; - } - - /** - * Generates a %LIKE% portion of the query - * - * @param mixed $key_condition - * @param string $val - * @param bool $backtick_protect - * @return mixed - */ - public function like($key_condition, $val = NULL, $side = 'both') - { - $this->like[] = array($key_condition, $val, $side); - $this->ci->db->like($key_condition, $val, $side); - return $this; - } - - /** - * Generates the OR %LIKE% portion of the query - * - * @param mixed $key_condition - * @param string $val - * @param bool $backtick_protect - * @return mixed - */ - public function or_like($key_condition, $val = NULL, $side = 'both') - { - $this->or_like[] = array($key_condition, $val, $side); - $this->ci->db->or_like($key_condition, $val, $side); - return $this; - } - - /** - * Sets additional column variables for adding custom columns - * - * @param string $column - * @param string $content - * @param string $match_replacement - * @return mixed - */ - public function add_column($column, $content, $match_replacement = NULL) - { - $this->add_columns[$column] = array('content' => $content, 'replacement' => $this->explode(',', $match_replacement)); - return $this; - } - - /** - * Sets additional column variables for editing columns - * - * @param string $column - * @param string $content - * @param string $match_replacement - * @return mixed - */ - public function edit_column($column, $content, $match_replacement) - { - $this->edit_columns[$column][] = array('content' => $content, 'replacement' => $this->explode(',', $match_replacement)); - return $this; - } - - /** - * Unset column - * - * @param string $column - * @return mixed - */ - public function unset_column($column) - { - $column=explode(',',$column); - $this->unset_columns=array_merge($this->unset_columns,$column); - return $this; - } - - /** - * Builds all the necessary query segments and performs the main query based on results set from chained statements - * - * @param string $output - * @param string $charset - * @return string - */ - public function generate($output = 'json', $charset = 'UTF-8') - { - if(strtolower($output) == 'json') - $this->get_paging(); - - $this->get_ordering(); - $this->get_filtering(); - return $this->produce_output(strtolower($output), strtolower($charset)); - } - - /** - * Generates the LIMIT portion of the query - * - * @return mixed - */ - private function get_paging() - { - $iStart = $this->ci->input->post('start'); - $iLength = $this->ci->input->post('length'); + private $group_by = []; + private $having = []; + private $select = []; + private $joins = []; + private $columns = []; + private $where = []; + private $or_where = []; + private $where_in = []; + private $like = []; + private $or_like = []; + private $filter = []; + private $add_columns = []; + private $edit_columns = []; + private $unset_columns = []; + private $preorder = []; + private $noBuiltinSearch = false; + private $debug = false; + private $offset = 0; + private $length = null; + private $hasFiltered = false; + private $fullstop = false; + private $limit = ""; + private $transport = "POST"; + private $db; + private $isDb2 = false; + + /** + * Copies an instance of CI + */ + public function __construct($config = null) { + $this->ci =& get_instance(); + + if (!empty($config['transport']) && $config['transport'] == 'GET') { + $this->params = $this->ci->input->get(null, false); + } else { + $this->params = $this->ci->input->post(null, false); + } + $this->transport = $config['transport']; + + if (!empty($config['preorder'])) { + $this->preorder = $config['preorder']; + } - if($iLength != '' && $iLength != '-1') - $this->ci->db->limit($iLength, ($iStart)? $iStart : 0); - } - - /** - * Generates the ORDER BY portion of the query - * - * @return mixed - */ - private function get_ordering() - { + if ($config['db2']) { + $this->isDb2 = TRUE; // requires explicit call to set_database for now + } else { + $this->db = &$this->ci->db; + } - $Data = $this->ci->input->post('columns'); + if (ENVIRONMENT != 'production') { + if (!empty($config['debug'])) { + $this->debug = (bool)$config['debug']; + } + } + if (!empty($config['noBuiltinSearch'])) { + $this->noBuiltinSearch = true; + } - if ($this->ci->input->post('order')) - foreach ($this->ci->input->post('order') as $key) - if($this->check_cType()) - $this->ci->db->order_by($Data[$key['column']]['data'], $key['dir']); - else - $this->ci->db->order_by($this->columns[$key['column']] , $key['dir']); + if (isset($config['offset'])) { + $this->offset = $config['offset']; + } + if (isset($config['fullstop'])) { + $this->fullstop = $config['fullstop']; + } } /** - * Generates a %LIKE% portion of the query - * - * @return mixed - */ - private function get_filtering() - { + * If you establish multiple databases in config/database.php this will allow you to + * set the database (other than $active_group) - more info: http://ellislab.com/forums/viewthread/145901/#712942 + */ + public function set_database($db_name) { + $this->db = &$this->ci->load->database($db_name, true); + } - $mColArray = $this->ci->input->post('columns'); + /** + * Specify the datatable response data + */ + public function set_params($params) { + $this->params = $params; + } - $sWhere = ''; - $search = $this->ci->input->post('search'); - $sSearch = $this->ci->db->escape_like_str(trim($search['value'])); - $columns = array_values(array_diff($this->columns, $this->unset_columns)); + /** + * Generates the SELECT portion of the query + * + * @param string $columns + * @param bool $backtick_protect + * @return mixed + */ + public function select($columns, $backtick_protect = true) { + foreach ($this->explode(',', $columns) as $val) { + $column = trim(preg_replace('/(.*)\s+as\s+(\w*)/i', '$2', $val)); + $column = preg_replace('/.*\.(.*)/i', '$1', $column); // get name after `.` + $this->columns[] = $column; + $this->select[$column] = trim(preg_replace('/(.*)\s+as\s+(\w*)/i', '$1', $val)); + } - if($sSearch != '') - for($i = 0; $i < count($mColArray); $i++) - if ($mColArray[$i]['searchable'] == 'true' && !array_key_exists($mColArray[$i]['data'], $this->add_columns)) - if($this->check_cType()) - $sWhere .= $this->select[$mColArray[$i]['data']] . " LIKE '%" . $sSearch . "%' OR "; - else - $sWhere .= $this->select[$this->columns[$i]] . " LIKE '%" . $sSearch . "%' OR "; + $this->db->select($columns, $backtick_protect); + return $this; + } - $sWhere = substr_replace($sWhere, '', -3); + /** + * Generates the DISTINCT portion of the query + * + * @param string $column + * @return mixed + */ + public function distinct($column) { + $this->distinct = $column; + $this->db->distinct($column); - if($sWhere != '') - $this->ci->db->where('(' . $sWhere . ')'); + return $this; + } - // TODO : sRangeSeparator + /** + * Generates a custom GROUP BY portion of the query + * + * @param string $val + * @return mixed + */ + public function group_by($val) { + $this->group_by[] = $val; + $this->db->group_by($val); - foreach($this->filter as $val) - $this->ci->db->where($val[0], $val[1], $val[2]); + return $this; } /** - * Compiles the select statement based on the other functions called and runs the query - * - * @return mixed - */ - private function get_display_result() - { - return $this->ci->db->get($this->table); + * Generates a custom HAVING portion of the query + * + * @param string $val + * @return mixed + */ + public function having($val) { + $this->having[] = $val; + $this->db->having($val); + + return $this; } /** - * Builds an encoded string data. Returns JSON by default, and an array of aaData if output is set to raw. - * - * @param string $output - * @param string $charset - * @return mixed - */ - private function produce_output($output, $charset) - { - $aaData = array(); - $rResult = $this->get_display_result(); + * Generates the FROM portion of the query + * + * @param string $table + * @return mixed + */ + public function from($table) { + $this->table = $table; - if($output == 'json') - { - $iTotal = $this->get_total_results(); - $iFilteredTotal = $this->get_total_results(TRUE); - } + return $this; + } - foreach($rResult->result_array() as $row_key => $row_val) - { - $aaData[$row_key] = ($this->check_cType())? $row_val : array_values($row_val); + /** + * Generates the JOIN portion of the query + * + * @param string $table + * @param string $fk + * @param string $type + * @return mixed + */ + public function join($table, $fk, $type = null) { + $this->joins[] = [$table, $fk, $type]; + $this->db->join($table, $fk, $type); - foreach($this->add_columns as $field => $val) - if($this->check_cType()) - $aaData[$row_key][$field] = $this->exec_replace($val, $aaData[$row_key]); - else - $aaData[$row_key][] = $this->exec_replace($val, $aaData[$row_key]); + return $this; + } + /** + * Generates the WHERE portion of the query + * + * @param mixed $key_condition + * @param string $val + * @param bool $backtick_protect + * @return mixed + */ + public function where($key_condition, $val = null, $backtick_protect = true) { - foreach($this->edit_columns as $modkey => $modval) - foreach($modval as $val) - $aaData[$row_key][($this->check_cType())? $modkey : array_search($modkey, $this->columns)] = $this->exec_replace($val, $aaData[$row_key]); + if (is_string($key_condition)) { + $key_condition = iconv('UTF-8', 'ASCII//TRANSLIT', $key_condition); + } - $aaData[$row_key] = array_diff_key($aaData[$row_key], ($this->check_cType())? $this->unset_columns : array_intersect($this->columns, $this->unset_columns)); + if (is_string($val)) { + $val = iconv('UTF-8', 'ASCII//TRANSLIT', $val); - if(!$this->check_cType()) - $aaData[$row_key] = array_values($aaData[$row_key]); + if ($this->isDb2) { + $val = "'" . $val . "'"; + } + } - } + if ($this->isDb2) { + $backtick_protect = false; + } - if($output == 'json') - { - $sOutput = array - ( - 'draw' => intval($this->ci->input->post('draw')), - 'recordsTotal' => $iTotal, - 'recordsFiltered' => $iFilteredTotal, - 'data' => $aaData - ); + $this->where[] = [$key_condition, $val, $backtick_protect]; - if($charset == 'utf-8') - return json_encode($sOutput); - else - return $this->jsonify($sOutput); - } - else - return array('aaData' => $aaData); + return $this; } /** - * Get result count - * - * @return integer - */ - private function get_total_results($filtering = FALSE) - { - if($filtering) - $this->get_filtering(); + * Generates the WHERE portion of the query + * + * @param mixed $key_condition + * @param string $val + * @param bool $backtick_protect + * @return mixed + */ + public function or_where($key_condition, $val = null, $backtick_protect = true) { + if (is_string($key_condition)) { + $key_condition = iconv('UTF-8', 'ASCII//TRANSLIT', $key_condition); + } - foreach($this->joins as $val) - $this->ci->db->join($val[0], $val[1], $val[2]); + if (is_string($val)) { + $val = iconv('UTF-8', 'ASCII//TRANSLIT', $val); - foreach($this->where as $val) - $this->ci->db->where($val[0], $val[1], $val[2]); + if ($this->isDb2) { + $val = "'" . $val . "'"; + } + } - foreach($this->or_where as $val) - $this->ci->db->or_where($val[0], $val[1], $val[2]); - - foreach($this->where_in as $val) - $this->ci->db->where_in($val[0], $val[1]); + if ($this->isDb2) { + $backtick_protect = false; + } - foreach($this->group_by as $val) - $this->ci->db->group_by($val); + $this->or_where[] = [$key_condition, $val, $backtick_protect]; - foreach($this->like as $val) - $this->ci->db->like($val[0], $val[1], $val[2]); - - foreach($this->or_like as $val) - $this->ci->db->or_like($val[0], $val[1], $val[2]); - - if(strlen($this->distinct) > 0) - { - $this->ci->db->distinct($this->distinct); - $this->ci->db->select($this->columns); - } - - $query = $this->ci->db->get($this->table, NULL, NULL, FALSE); - return $query->num_rows(); - } - - /** - * Runs callback functions and makes replacements - * - * @param mixed $custom_val - * @param mixed $row_data - * @return string $custom_val['content'] - */ - private function exec_replace($custom_val, $row_data) - { - $replace_string = ''; - - // Go through our array backwards, else $1 (foo) will replace $11, $12 etc with foo1, foo2 etc - $custom_val['replacement'] = array_reverse($custom_val['replacement'], true); - - if(isset($custom_val['replacement']) && is_array($custom_val['replacement'])) - { - //Added this line because when the replacement has over 10 elements replaced the variable "$1" first by the "$10" - $custom_val['replacement'] = array_reverse($custom_val['replacement'], true); - foreach($custom_val['replacement'] as $key => $val) - { - $sval = preg_replace("/(? $args_val) - { - $args_val = preg_replace("/(?columns))? ($row_data[($this->check_cType())? $args_val : array_search($args_val, $this->columns)]) : $args_val; + return $this; + } + + /** + * Generates the WHERE IN portion of the query + * + * @param mixed $key_condition + * @param string $val + * @param bool $backtick_protect + * @return mixed + */ + public function where_in($key_condition, $val = null) { + if (is_string($key_condition)) { + $key_condition = iconv('UTF-8', 'ASCII//TRANSLIT', $key_condition); + } + + if (is_string($val)) { + $val = iconv('UTF-8', 'ASCII//TRANSLIT', $val); + + if ($this->isDb2) { + $val = "'" . $val . "'"; + } + } + + if ($this->isDb2) { + $backtick_protect = false; + } + + $this->where_in[] = [$key_condition, $val]; + + return $this; + } + + /** + * Generates the WHERE portion of the query + * + * @param mixed $key_condition + * @param string $val + * @param bool $backtick_protect + * @return mixed + */ + public function filter($key_condition, $val = null, $backtick_protect = true) { + if (is_string($key_condition)) { + $key_condition = iconv('UTF-8', 'ASCII//TRANSLIT', $key_condition); + } + + if (is_string($val)) { + $val = iconv('UTF-8', 'ASCII//TRANSLIT', $val); + + if ($this->isDb2) { + $val = "'" . $val . "'"; + } + } + + if ($this->isDb2) { + $backtick_protect = false; + } + + $this->filter[] = [$key_condition, $val, $backtick_protect]; + + return $this; + } + + /** + * Generates a %LIKE% portion of the query + * + * @param mixed $key_condition + * @param string $val + * @param bool $backtick_protect + * @return mixed + */ + public function like($key_condition, $val = null, $side = 'both') { + if (is_string($key_condition)) { + $key_condition = iconv('UTF-8', 'ASCII//TRANSLIT', $key_condition); + } + + if (is_string($val)) { + $val = iconv('UTF-8', 'ASCII//TRANSLIT', $val); + + if ($this->isDb2) { + $val = "'" . $val . "'"; + } + } + + if ($this->isDb2) { + $backtick_protect = false; + } + + $this->like[] = [$key_condition, $val, $side]; + + return $this; + } + + /** + * Generates the OR %LIKE% portion of the query + * + * @param mixed $key_condition + * @param string $val + * @param bool $backtick_protect + * @return mixed + */ + public function or_like($key_condition, $val = null, $side = 'both') { + if (is_string($key_condition)) { + $key_condition = iconv('UTF-8', 'ASCII//TRANSLIT', $key_condition); + } + + if (is_string($val)) { + $val = iconv('UTF-8', 'ASCII//TRANSLIT', $val); + + if ($this->isDb2) { + $val = "'" . $val . "'"; + } + } + + if ($this->isDb2) { + $backtick_protect = false; + } + + $this->or_like[] = [$key_condition, $val, $side]; + + return $this; + } + + /** + * Sets additional column variables for adding custom columns + * + * @param string $column + * @param string $content + * @param string $match_replacement + * @return mixed + */ + public function add_column($column, $content, $match_replacement = null) { + $this->add_columns[$column] = ['content' => $content, 'replacement' => $this->explode(',', $match_replacement)]; + + return $this; + } + + /** + * Sets additional column variables for editing columns + * + * @param string $column + * @param string $content + * @param string $match_replacement + * @return mixed + */ + public function edit_column($column, $content, $match_replacement) { + $this->edit_columns[$column][] = ['content' => $content, 'replacement' => $this->explode(',', $match_replacement)]; + + return $this; + } + + /** + * Sets row index + * + * @param string $field + * @return mixed + */ + public function set_row_index($field) { + $this->row_index = $field; + + return $this; + } + + /** + * Unset column + * + * @param string $column + * @return mixed + */ + public function unset_column($column) { + $column = explode(',', $column); + $this->unset_columns = array_merge($this->unset_columns, $column); + + return $this; + } + + /** + * Builds all the necessary query segments and performs the main query based on results set from chained statements + * + * @param string $output + * @param string $charset + * @return string + */ + public function generate($output = 'json', $charset = 'UTF-8') { + if (strtolower($output) == 'json' || $output == 'array') { + $this->get_paging(); + } + + $this->get_ordering(); + $this->setupUserWhere(); + + return $this->produce_output(strtolower($output), strtolower($charset)); + } + + /** + * Generates the LIMIT portion of the query + * + * @return mixed + */ + private function get_paging() { + if ($this->params['length'] != '' && $this->params['length'] != '-1') { + $this->limit = "LIMIT " . (int)$this->params['length'] . ($this->params['start'] ? ', ' . (int)$this->params['start'] : ''); + } elseif ($this->isDb2) { + $this->limit = "LIMIT 250"; // workaround memory limit error in pdo + } + + $this->length = (int)$this->params['length']; + } + + /** + * Generates the ORDER BY portion of the query + * + * @return mixed + */ + private function get_ordering() { + if ($this->transport = "POST") { + $this->params['order'] = $this->ci->input->post('order', false); + } else { + $this->params['order'] = $this->ci->input->get('order', false); + } + + $orderBy = ""; + + if (!empty($this->preorder)) { + $orderBy .= $this->preorder . ($this->params['order'] ? ", " : ''); + } + + if ($this->params['order']) { + $i = 0; + foreach ($this->params['order'] as $odr) { + $colIdx = (int)$odr['column'] + $this->offset; + switch (strtolower($odr['dir'])) { + case "desc" : + $dir = "desc"; + break; + case "asc" : + default : + $dir = "asc"; + break; + } + + if ($colIdx >= 0) { + if ($i > 0) { + $orderBy .= ", "; + } + + $orderBy .= " " . $this->params['columns'][$colIdx]['data'] . " " . $dir; + + $i++; + } + } + } + + if ($orderBy != "") { + $this->db->order_by($orderBy); + } + } + + /** + * Generates a %LIKE% portion of the query + * + * @return mixed + */ + private function get_filtering() { + if (!$this->noBuiltinSearch && !$this->hasFiltered) { + $sWhere = ''; + + if ($this->transport = "POST") { + $search = $this->ci->input->post('search'); + } else { + $search = $this->ci->input->get('search'); + } + + if (!$this->isDb2) { + $sSearch = $this->db->escape_like_str(trim($search['value'])); + } else { + $sSearch = $this->db2_escape($search['value']); + } + + if (!empty($sSearch)) { + for ($i = 0; $i < count($this->params['columns']); $i++) { + if ($this->params['columns'][$i]['searchable'] == 'true' && !array_key_exists($this->params['columns'][$i]['data'] + $this->offset, $this->add_columns)) { + if (isset($this->columns[$i + $this->offset])) { + if ($this->check_cType()) { + $sWhere .= $this->select[$this->params['columns'][$i]['data'] + $this->offset] . " LIKE '%" . $sSearch . "%' OR "; + } else { + $sWhere .= $this->select[$this->columns[$i + $this->offset]] . " LIKE '%" . $sSearch . "%' OR "; + } + + } + } + } + } + + if ($sWhere != '') { + $sWhere = substr_replace($sWhere, '', -3); } - $replace_string = call_user_func_array($func, $args); - } - elseif(in_array($sval, $this->columns)) - $replace_string = $row_data[($this->check_cType())? $sval : array_search($sval, $this->columns)]; - else - $replace_string = $sval; + $this->hasFiltered = true; - $custom_val['content'] = str_ireplace('$' . ($key + 1), $replace_string, $custom_val['content']); + if ($sWhere != '') { + return ' WHERE (' . $sWhere . ') '; + } + } + + return ""; + } + + private function setupUserWhere() { + foreach ($this->like as $val) { + $this->db->like($val[0], $val[1], $val[2]); + } + + foreach ($this->or_like as $val) { + $this->db->or_like($val[0], $val[1], $val[2]); + } + + foreach ($this->filter as $val) { + $this->db->where($val[0], $val[1], $val[2]); + } + + foreach ($this->where as $val) { + $this->db->where($val[0], $val[1], $val[2]); + } + + foreach ($this->or_where as $val) { + $this->db->or_where($val[0], $val[1], $val[2]); } - } - return $custom_val['content']; + foreach ($this->where_in as $val) { + $this->db->where_in($val[0], $val[1]); + } } /** - * Check column type -numeric or column name - * - * @return bool - */ - private function check_cType() - { - $column = $this->ci->input->post('columns'); - if(is_numeric($column[0]['data'])) - return FALSE; - else - return TRUE; + * Builds an encoded string data. Returns JSON by default, and an array of aaData if output is set to raw. + * + * @param string $output + * @param string $charset + * @return mixed + */ + private function produce_output($output, $charset) { + $aaData = []; + + $baseSelect = $this->db->get_compiled_select($this->table); + $filter = $this->get_filtering(); + + if ($filter != "") { + if (strpos($baseSelect, 'WHERE ') !== false) { + $filteredSelect = str_replace('WHERE ', $filter . " AND ", $baseSelect); + } else { + if (strpos($baseSelect, 'ORDER BY') !== false) { + $filteredSelect = str_replace('ORDER BY', $filter . " ORDER BY", $baseSelect); + } elseif (strpos($baseSelect, 'LIMIT') !== false) { + $filteredSelect = str_replace('LIMIT', $filter . " LIMIT", $baseSelect); + } else { + $filteredSelect = $baseSelect . $filter; + } + } + } else { + $filteredSelect = $baseSelect; + } + + if ($this->fullstop) { + var_dump([ + $baseSelect, + $filteredSelect + ]); + exit(); + } + + $rResult = $this->db->query($filteredSelect . " " . $this->limit); // filtered results + + if (is_object($rResult)) { + $rResult = $rResult->result_array(); + } else { + $rResult = []; + } + + if ($output == 'json' || $output == 'array') { + $iTotal = $this->get_total_results($baseSelect); + $iFilteredTotal = $this->get_total_results($filteredSelect); + } + + foreach ($rResult as $row_key => $row_val) { + $aaData[$row_key] = ($this->check_cType()) ? $row_val : array_values($row_val); + + foreach ($this->add_columns as $field => $val) { + if ($this->check_cType()) { + $aaData[$row_key][$field] = $this->exec_replace($val, $aaData[$row_key]); + } else { + $aaData[$row_key][] = $this->exec_replace($val, $aaData[$row_key]); + } + } + + foreach ($this->edit_columns as $modkey => $modval) { + foreach ($modval as $val) { + $aaData[$row_key][($this->check_cType()) ? $modkey : array_search($modkey, $this->columns)] = $this->exec_replace($val, $aaData[$row_key]); + } + } + + if (!empty($this->row_index)) { + $aaData[$row_key][($this->check_cType()) ? $this->row_index : array_search($this->row_index, $this->columns)] = intval($this->params['start']) + $row_key + 1; + } + + $aaData[$row_key] = array_diff_key($aaData[$row_key], ($this->check_cType()) ? $this->unset_columns : array_intersect($this->columns, $this->unset_columns)); + + $aaData[$row_key] = array_values($aaData[$row_key]); + } + + if ($output == 'json' || $output == 'array') { + $sOutput = [ + 'draw' => intval($this->params['draw']), + 'recordsTotal' => $iTotal, + 'recordsFiltered' => $iFilteredTotal, + 'data' => $aaData + ]; + + if ($this->debug) { + $sOutput['baseSelect'] = trim(preg_replace('/\s+/', ' ', $baseSelect)); + $sOutput['filteredSelect'] = trim(preg_replace('/\s+/', ' ', $filteredSelect)); + } + + if ($charset == 'utf-8') { + return ($output == 'array') ? $sOutput : json_encode($sOutput); + } else { + return $this->jsonify($sOutput); + } + } else { + return ['aaData' => $aaData]; + } } + /** + * Get result count + * + * @return integer + */ + private function get_total_results($sql = "") { + if ($sql) { + $query = $this->db->query("SELECT COUNT(*) CNT FROM (" . $sql . ") x"); + + if (is_object($query)) { + $result = $query->result_array(); + $count = $result[0]['CNT']; + } else { + $query = $this->db->query($sql); + if ($query) { + $result = $query->result_array(); + + if ($result) { + return count($result); + } + } + + $count = 0; + } + } + + return $count; + } /** - * Return the difference of open and close characters - * - * @param string $str - * @param string $open - * @param string $close - * @return string $retval - */ - private function balanceChars($str, $open, $close) - { - $openCount = substr_count($str, $open); - $closeCount = substr_count($str, $close); - $retval = $openCount - $closeCount; - return $retval; + * Runs callback functions and makes replacements + * + * @param mixed $custom_val + * @param mixed $row_data + * @return string $custom_val['content'] + */ + private function exec_replace($custom_val, $row_data) { + $replace_string = ''; + + // Go through our array backwards, else $1 (foo) will replace $11, $12 etc with foo1, foo2 etc + $custom_val['replacement'] = array_reverse($custom_val['replacement'], true); + + if (isset($custom_val['replacement']) && is_array($custom_val['replacement'])) { + //Added this line because when the replacement has over 10 elements replaced the variable "$1" first by the "$10" + $custom_val['replacement'] = array_reverse($custom_val['replacement'], true); + foreach ($custom_val['replacement'] as $key => $val) { + $sval = preg_replace("/(? $args_val) { + $args_val = preg_replace("/(?columns)) ? ($row_data[($this->check_cType()) ? $args_val : array_search($args_val, $this->columns)]) : $args_val; + } + + $replace_string = call_user_func_array($func, $args); + } elseif (in_array($sval, $this->columns)) { + $replace_string = $row_data[($this->check_cType()) ? $sval : array_search($sval, $this->columns)]; + } else { + $replace_string = $sval; + } + + $custom_val['content'] = str_ireplace('$' . ($key + 1), $replace_string, $custom_val['content']); + } + } + + return $custom_val['content']; } /** - * Explode, but ignore delimiter until closing characters are found - * - * @param string $delimiter - * @param string $str - * @param string $open - * @param string $close - * @return mixed $retval - */ - private function explode($delimiter, $str, $open = '(', $close=')') - { - $retval = array(); - $hold = array(); - $balance = 0; - $parts = explode($delimiter, $str); + * Check column type -numeric or column name + * + * @return bool + */ + private function check_cType() { + if (is_numeric($this->params['columns'][0]['data'])) { + return false; + } else { + return true; + } + } - foreach($parts as $part) - { - $hold[] = $part; - $balance += $this->balanceChars($part, $open, $close); + /** + * Return the difference of open and close characters + * + * @param string $str + * @param string $open + * @param string $close + * @return string $retval + */ + private function balanceChars($str, $open, $close) { + $openCount = substr_count($str, $open); + $closeCount = substr_count($str, $close); + $retval = $openCount - $closeCount; + + return $retval; + } - if($balance < 1) - { - $retval[] = implode($delimiter, $hold); - $hold = array(); - $balance = 0; + /** + * Explode, but ignore delimiter until closing characters are found + * + * @param string $delimiter + * @param string $str + * @param string $open + * @param string $close + * @return mixed $retval + */ + private function explode($delimiter, $str, $open = '(', $close = ')') { + $retval = []; + $hold = []; + $balance = 0; + $parts = explode($delimiter, $str); + + foreach ($parts as $part) { + $hold[] = $part; + $balance += $this->balanceChars($part, $open, $close); + + if ($balance < 1) { + $retval[] = implode($delimiter, $hold); + $hold = []; + $balance = 0; + } } - } - if(count($hold) > 0) - $retval[] = implode($delimiter, $hold); + if (count($hold) > 0) { + $retval[] = implode($delimiter, $hold); + } - return $retval; + return $retval; } /** - * Workaround for json_encode's UTF-8 encoding if a different charset needs to be used - * - * @param mixed $result - * @return string - */ - private function jsonify($result = FALSE) - { - if(is_null($result)) - return 'null'; + * Workaround for json_encode's UTF-8 encoding if a different charset needs to be used + * + * @param mixed $result + * @return string + */ + private function jsonify($result = false) { + if (is_null($result)) { + return 'null'; + } + + if ($result === false) { + return 'false'; + } + + if ($result === true) { + return 'true'; + } + + if (is_scalar($result)) { + if (is_float($result)) { + return floatval(str_replace(',', '.', strval($result))); + } + + if (is_string($result)) { + static $jsonReplaces = [['\\', '/', '\n', '\t', '\r', '\b', '\f', '"'], ['\\\\', '\\/', '\\n', '\\t', '\\r', '\\b', '\\f', '\"']]; - if($result === FALSE) - return 'false'; + return '"' . str_replace($jsonReplaces[0], $jsonReplaces[1], $result) . '"'; + } else { + return $result; + } + } - if($result === TRUE) - return 'true'; + $isList = true; - if(is_scalar($result)) - { - if(is_float($result)) - return floatval(str_replace(',', '.', strval($result))); - - if(is_string($result)) - { - static $jsonReplaces = array(array('\\', '/', '\n', '\t', '\r', '\b', '\f', '"'), array('\\\\', '\\/', '\\n', '\\t', '\\r', '\\b', '\\f', '\"')); - return '"' . str_replace($jsonReplaces[0], $jsonReplaces[1], $result) . '"'; + for ($i = 0, reset($result); $i < count($result); $i++, next($result)) { + if (key($result) !== $i) { + $isList = false; + break; + } } - else - return $result; - } - $isList = TRUE; - - for($i = 0, reset($result); $i < count($result); $i++, next($result)) - { - if(key($result) !== $i) - { - $isList = FALSE; - break; - } - } - - $json = array(); - - if($isList) - { - foreach($result as $value) - $json[] = $this->jsonify($value); - - return '[' . join(',', $json) . ']'; - } - else - { - foreach($result as $key => $value) - $json[] = $this->jsonify($key) . ':' . $this->jsonify($value); - - return '{' . join(',', $json) . '}'; - } - } - - /** + $json = []; + + if ($isList) { + foreach ($result as $value) { + $json[] = $this->jsonify($value); + } + + return '[' . join(',', $json) . ']'; + } else { + foreach ($result as $key => $value) { + $json[] = $this->jsonify($key) . ':' . $this->jsonify($value); + } + + return '{' . join(',', $json) . '}'; + } + } + + /** * returns the sql statement of the last query run + * * @return type */ - public function last_query() - { - return $this->ci->db->last_query(); + public function last_query() { + return $this->db->last_query(); + } + + private function db2_escape($value) { + $value = trim($value); + if (empty($value)) { + return FALSE; + } + + if ($value !== 0 && is_int($value)) { + return ( int )$value; + } + + $value = ( string )$value; // cast as a string + + $return = ''; + for ($i = 0; $i < strlen($value); ++$i) { + $char = $value [$i]; + $ord = ord($char); + if ($char !== "'" && $char !== ";" && $char !== "\"" && $char !== '\\' && $ord >= 32 && $ord <= 126) + $return .= $char; + else + $return .= '\\x' . dechex($ord); + } + return $return; } - } +} /* End of file Datatables.php */ /* Location: ./application/libraries/Datatables.php */ diff --git a/readme.txt b/readme.txt index 2e7d63e..2fd4e4b 100644 --- a/readme.txt +++ b/readme.txt @@ -23,6 +23,17 @@ Install ======= To install the library, copy the libraries/datatables.php file into your application/libraries folder. +======= +DB2 Notes +======= + +DB2 in regard to datatables is finicky. One caveat of note is that if you're using any date columns, you MUST cast them appropriately as +chars in your select, ex: + + $data ['results'] = $this->datatables->from('TABLE')->select("COl, COL2,CHAR(DATECOL, USA)")->generate('array'); + +You can use USA or UTC in the CHAR() conversion. + ======= License =======