/** * Send an email * * @param array $options * @return array */ public function send($options) { $result = ['success' => false, 'error' => [], 'unique_id' => null]; // see if we need to validate if (empty($options['validated'])) { $temp = $this->validate($options); if (!$temp['success']) { return $temp; } else { if (!empty($temp['data']['requires_fetching'])) { // we error if we require fetching from database $result['error'][] = 'Fetching of email addresses is required!'; return $result; } else { $options = $temp['data']; } } } // to, cc, bcc $recepients = []; foreach (['to', 'cc', 'bcc'] as $r) { $recepients[$r] = []; foreach ($options[$r] as $v) { // todo: add recepient name here $recepients[$r][] = $v['email']; } $recepients[$r] = implode(',', $recepients[$r]); } // crypt object $crypt = new crypt(); // todo: use unique id for tracking $result['unique_id'] = $crypt->hash([$recepients, $options['subject'], microtime()]); // generating header if (isset($options['header'])) { $header = $options['header']; } else { $header = ''; } if (isset($options['from']['name'])) { $header .= "From: {$options['from']['name']} <{$options['from']['email']}>\n"; $header .= "Organization: {$options['from']['name']}\n"; } else { $header .= "From: {$options['from']['email']}\n"; } if (!empty($recepients['bcc'])) { $header .= "Bcc: " . $recepients['bcc'] . "\n"; } if (!empty($recepients['cc'])) { $header .= "Cc: " . $recepients['cc'] . "\n"; } $header .= "Reply-To: {$options['from']['email']}\n"; $header .= "Errors-To: {$options['from']['email']}\n"; $header .= "MIME-Version: 1.0\n"; $header .= "X-Mailer: PHP/" . phpversion() . "\n"; // generating body for no attachment and a single message if (empty($options['attachments']) && count($options['message']) == 1) { $part = reset($options['message']); $header .= "Content-Type: {$part['type']};\n charset=\"{$part['charset']}\"\n"; $header .= "Content-Transfer-Encoding: {$part['encoding']}\n"; $body = $part['data']; } else { // has attachments or multiple messages $body_text = ""; $unique_hash = $crypt->hash(mt_rand()); $body_boundary = "boundary." . $unique_hash; $body_header = ""; $body_header .= "Content-Type: multipart/alternative; boundary=\"{$body_boundary}\"\n"; $body_header .= "Content-Transfer-Encoding: 7bit\n"; $body_header .= "Content-Disposition: inline\n"; // going though messages foreach ($options['message'] as $part) { $body_text .= "--{$body_boundary}\n"; $body_text .= "Content-Type: {$part['type']}; charset=\"{$part['charset']}\"\n"; $body_text .= "Content-Transfer-Encoding: {$part['encoding']}\n\n"; $body_text .= $this->encode_part($part) . "\n\n"; } $body_text .= "\n--{$body_boundary}--\n"; // if we have attachments $text_part = "\nThis is a multi-part message in MIME format.\n\n"; if (!empty($options['attachments'])) { $attachment_boundary = "boundary." . $unique_hash . ".attachments"; $header .= "Content-Type: multipart/mixed; boundary=\"{$attachment_boundary}\""; $text_part .= "--{$attachment_boundary}\n"; $text_part .= "{$body_header}\n"; $text_part .= $body_text; // going though them foreach ($options['attachments'] as $v) { $text_part .= "--{$attachment_boundary}\n"; $text_part .= "Content-Type: {$v['type']}; name=\"{$v['name']}\"\n"; $text_part .= "Content-Transfer-Encoding: base64\n"; $text_part .= "Content-Disposition: attachment; filename=\"{$v['name']}\"\n\n"; $text_part .= $this->encode_part(['data' => $v['data'], 'encoding' => 'base64']); } $text_part .= "\n--{$attachment_boundary}--\n"; } else { $header .= $body_header; $text_part .= $body_text; } $body = $text_part; } // trying to deliver if (mail($recepients['to'], $options['subject'], $body, $header)) { $result['success'] = true; } else { $result['error'][] = 'Could not deliver mail!'; } return $result; }
/** * 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; }
/** * Data default renderer * * @return string */ private final function render_data_default() { $result = ''; // if we have no rows we display a messsage if ($this->num_rows == 0) { return html::message(['type' => 'warning', 'options' => [i18n(null, object_content_messages::no_rows_found)]]); } $counter = 1; $table = ['header' => [], 'options' => []]; // action flags $actions = []; if (object_controller::can('record_view')) { $actions['view'] = true; } // generate columns foreach ($this->columns as $k => $v) { // if we can not view we skip action column if (empty($actions) && $k == 'action') { continue; } $table['header'][$k] = ['value' => i18n(null, $v['name']), 'nowrap' => true, 'width' => $v['width'] ?? null]; } // generate rows foreach ($this->rows as $k => $v) { // process all columns first $row = []; foreach ($this->columns as $k2 => $v2) { // if we can not view we skip action column if (empty($actions) && $k2 == 'action') { continue; } $value = []; // create cell properties foreach (['width', 'align'] as $v3) { if (isset($v2[$v3])) { $value[$v3] = $v2[$v3]; } } // process rows if ($k2 == 'action') { $value['value'] = []; if (!empty($actions['view'])) { $mvc = application::get('mvc'); $pk = extract_keys($this->model_object->pk, $v); $url = $mvc['controller'] . '/_edit?' . http_build_query2($pk); $value['value'][] = html::a(['value' => i18n(null, 'View'), 'href' => $url]); } $value['value'] = implode(' ', $value['value']); } else { if ($k2 == 'row_number') { $value['value'] = format::id($counter) . '.'; } else { if ($k2 == 'offset_number') { $value['value'] = format::id($this->offset + $counter) . '.'; } else { if (!empty($v2['options_model'])) { if (strpos($v2['options_model'], '::') === false) { $v2['options_model'] .= '::options'; } $params = $v2['options_params'] ?? []; if (!empty($v2['options_depends'])) { foreach ($v2['options_depends'] as $k0 => $v0) { $params[$k0] = $v[$v0]; } } $crypt_object = new crypt(); $hash = $crypt_object->hash($v2['options_model'] . serialize($params)); if (!isset($this->cached_options[$hash])) { $method = factory::method($v2['options_model'], null, true); $this->cached_options[$hash] = call_user_func_array($method, [['where' => $params]]); } if (isset($this->cached_options[$hash][$v[$k2]])) { $value['value'] = $this->cached_options[$hash][$v[$k2]]['name']; } else { $value['value'] = null; } } else { if (!empty($v2['options']) && !is_array($v[$k2])) { if (isset($v2['options'][$v[$k2]])) { $value['value'] = $v2['options'][$v[$k2]]['name']; } else { $value['value'] = null; } } else { if (isset($v[$k2])) { $value['value'] = $v[$k2]; } else { $value['value'] = null; } } } } } } // put value into row if (!empty($v2['format'])) { $format_options = $v2['format_options'] ?? []; if (!empty($v2['format_depends'])) { $format_depends = $v2['format_depends']; $this->process_params_and_depends($format_depends, $v); $format_options = array_merge_hard($format_options, $format_depends); } $method = factory::method($v2['format'], 'format'); $value['value'] = call_user_func_array([$method[0], $method[1]], [$value['value'], $format_options]); } $row[$k2] = $value; } // put processed columns though user defined function if (method_exists($this, 'render_data_rows')) { $table['options'][$counter] = $this->render_data_rows($row, $v); } else { $table['options'][$counter] = $row; } $counter++; } return html::table($table); }
/** * This will run SQL query and return structured data * * @param string $sql * @param mixed $key * @param array $options * @return array */ public function query($sql, $key = null, $options = []) { $result = ['success' => false, 'sql' => $sql, 'error' => [], 'errno' => 0, 'num_rows' => 0, 'affected_rows' => 0, 'rows' => [], 'key' => $key, 'structure' => [], 'time' => null, 'last_insert_id' => 0]; // start time $result['time'] = debug::get_microtime(); // cache id $crypt_object = new crypt(); $cache_id = !empty($options['cache_id']) ? $options['cache_id'] : 'db_query_' . $crypt_object->hash($sql . serialize($key)); // if we cache this query if (!empty($options['cache'])) { $cache_object = new cache($this->connect_options['cache_link']); $cached_result = $cache_object->get($cache_id); if ($cached_result !== false) { return $cached_result; } } // quering regular query first if (empty($options['multi_query'])) { $resource = mysqli_query($this->db_resource, $sql); if (!$resource) { $this->error_overrides($result, mysqli_errno($this->db_resource), mysqli_error($this->db_resource)); } else { $result['affected_rows'] += mysqli_affected_rows($this->db_resource); if ($resource !== true) { $result['num_rows'] += mysqli_num_rows($resource); $result['structure'] = $this->field_structures($resource); } if ($result['num_rows'] > 0) { while ($rows = mysqli_fetch_assoc($resource)) { // casting types $data = []; foreach ($rows as $k => $v) { $data[$k] = $this->process_value_as_per_type($v, $result['structure'][$k]['type']); } // assigning keys if (!empty($key)) { array_key_set_by_key_name($result['rows'], $key, $data); } else { $result['rows'][] = $data; } } } if ($resource !== true) { mysqli_free_result($resource); } $result['success'] = true; } } else { // multi query $resource = mysqli_multi_query($this->db_resource, $sql); if (!$resource) { $result['error'][] = 'Db Link ' . $this->db_link . ': ' . mysqli_error($this->db_resource); $result['errno'] = mysqli_errno($this->db_resource); // we log this error message // todo: process log policy here error_log('Query error: ' . implode(' ', $result['error']) . ' [' . $sql . ']'); } else { $result['affected_rows'] += mysqli_affected_rows($this->db_resource); do { if ($result_multi = mysqli_store_result($this->db_resource)) { if ($result_multi) { $result['num_rows'] += $num_rows = mysqli_num_rows($result_multi); $result['structure'] = $this->field_structures($result_multi); } else { $num_rows = 0; $result['error'][] = 'Db Link ' . $this->db_link . ': Multi query error!'; $result['errno'] = 1; // we log this error message // todo: process log policy here error_log('Query error: ' . implode(' ', $result['error']) . ' [' . $sql . ']'); } if ($num_rows > 0) { while ($rows = mysqli_fetch_assoc($result_multi)) { // casting types $data = []; foreach ($rows as $k => $v) { $data[$k] = $this->process_value_as_per_type($v, $result['structure'][$k]['type']); } // assigning keys if (!empty($key)) { array_key_set_by_key_name($result['rows'], $key, $data); } else { $result['rows'][] = $data; } } } mysqli_free_result($result_multi); } } while (mysqli_more_results($this->db_resource) && mysqli_next_result($this->db_resource)); if (empty($result['error'])) { $result['success'] = true; } } } // last insert id for auto increment columns $result['last_insert_id'] = mysqli_insert_id($this->db_resource); // caching if no error if (!empty($options['cache']) && empty($result['error'])) { $cache_object->set($cache_id, $result, ['tags' => $options['cache_tags'] ?? null]); } // end time $result['time'] = debug::get_microtime() - $result['time']; // if we are debugging if (debug::$debug) { debug::$data['sql'][] = $result; } return $result; }
/** * This will run SQL query and return structured data * * @param string $sql * @param mixed $key * @param array $options * @return array */ public function query($sql, $key = null, $options = []) { $result = ['success' => false, 'sql' => $sql, 'error' => [], 'errno' => 0, 'num_rows' => 0, 'affected_rows' => 0, 'rows' => [], 'key' => $key, 'structure' => [], 'time' => null]; // start time $result['time'] = debug::get_microtime(); // cache id $crypt_object = new crypt(); $cache_id = !empty($options['cache_id']) ? $options['cache_id'] : 'db_query_' . $crypt_object->hash($sql . serialize($key)); // if we cache this query if (!empty($options['cache'])) { $cache_object = new cache($this->connect_options['cache_link']); $cached_result = $cache_object->get($cache_id); if ($cached_result !== false) { return $cached_result; } } // quering $resource = @pg_query($this->db_resource, $sql); $result['status'] = pg_result_status($resource); if (!$resource || $result['status'] > 4) { $last_error = pg_last_error($this->db_resource); if (empty($last_error)) { $errno = 1; $error = 'DB Link ' . $this->db_link . ': ' . 'Unspecified error!'; } else { preg_match("|ERROR:\\s(.*?):|i", $last_error, $matches); $errno = !empty($matches[1]) ? $matches[1] : 1; $error = $last_error; } $this->error_overrides($result, $errno, $error); } else { $result['affected_rows'] = pg_affected_rows($resource); $result['num_rows'] = pg_num_rows($resource); $result['structure'] = $this->field_structures($resource); if ($result['num_rows'] > 0) { while ($rows = pg_fetch_assoc($resource)) { // transforming pg arrays to php arrays and casting types foreach ($rows as $k => $v) { if ($result['structure'][$k]['type'][0] == '_') { $rows[$k] = $this->pg_parse_array($v); } else { if (in_array($result['structure'][$k]['type'], ['int2', 'int4', 'int8'])) { if (!is_null($v)) { $rows[$k] = (int) $v; } } else { if (in_array($result['structure'][$k]['type'], ['real', 'double precision'])) { if (!is_null($v)) { $rows[$k] = (double) $v; } } else { if ($result['structure'][$k]['type'] == 'bytea') { $rows[$k] = pg_unescape_bytea($v); } else { if ($result['structure'][$k]['type'] == 'jsonb') { // we must get json vallues to PHP format $rows[$k] = json_encode(json_decode($v, true)); } } } } } } // assigning keys if (!empty($key)) { array_key_set_by_key_name($result['rows'], $key, $rows); } else { $result['rows'][] = $rows; } } } pg_free_result($resource); $result['success'] = true; } // caching if no error if (!empty($options['cache']) && empty($result['error'])) { $cache_object->set($cache_id, $result, ['tags' => $options['cache_tags'] ?? []]); } // end time $result['time'] = debug::get_microtime() - $result['time']; // if we are debugging if (debug::$debug) { debug::$data['sql'][] = $result; } return $result; }