public function log($level, $message, array $context = array())
{
global $wpdb;
/*
* Filter that makes it possible to shortcut this log.
* Return bool false to cancel.
*
* @since 2.3.1
*/
$do_log = apply_filters("simple_history/log/do_log", true, $level, $message, $context, $this);
if ($do_log === false) {
return $this;
}
// Check if $message is a translated message, and if so then fetch original
$sh_latest_translations = $this->simpleHistory->gettextLatestTranslations;
if (!empty($sh_latest_translations)) {
if (isset($sh_latest_translations[$message])) {
// Translation of this phrase was found, so use original phrase instead of translated one
// Store textdomain since it's required to translate
$context["_gettext_domain"] = $sh_latest_translations[$message]["domain"];
// These are good to keep when debugging
// $context["_gettext_org_message"] = $sh_latest_translations[$message]["text"];
// $context["_gettext_translated_message"] = $sh_latest_translations[$message]["translation"];
$message = $sh_latest_translations[$message]["text"];
}
}
/**
* Filter arguments passed to log funtion
*
* @since 2.0
*
* @param string $level
* @param string $message
* @param array $context
* @param object SimpleLogger object
*/
apply_filters("simple_history/log_arguments", $level, $message, $context, $this);
$context = apply_filters("simple_history/log_argument/context", $context, $level, $message, $this);
$level = apply_filters("simple_history/log_argument/level", $level, $context, $message, $this);
$message = apply_filters("simple_history/log_argument/message", $message, $level, $context, $this);
/* Store date as GMT date, i.e. not local date/time
* Some info here:
* http://www.skyverge.com/blog/down-the-rabbit-hole-wordpress-and-timezones/
*/
$localtime = current_time("mysql", 1);
$db_table = $wpdb->prefix . SimpleHistory::DBTABLE;
/**
* Filter db table used for simple history events
*
* @since 2.0
*
* @param string $db_table
*/
$db_table = apply_filters("simple_history/db_table", $db_table);
$data = array("logger" => $this->slug, "level" => $level, "date" => $localtime, "message" => $message);
// Allow date to be override
// Date must be in format 'Y-m-d H:i:s'
if (isset($context["_date"])) {
$data["date"] = $context["_date"];
unset($context["_date"]);
}
// Add occasions id
$occasions_id = null;
if (isset($context["_occasionsID"])) {
// Minimize risk of similar loggers logging same messages and such and resulting in same occasions id
// by always adding logger slug
$occasions_data = array("_occasionsID" => $context["_occasionsID"], "_loggerSlug" => $this->slug);
$occasions_id = md5(json_encode($occasions_data));
unset($context["_occasionsID"]);
} else {
// No occasions id specified, create one bases on the data array
$occasions_data = $data + $context;
// error_log(simpleHistory::json_encode($occasions_data));
// Don't include date in context data
unset($occasions_data["date"]);
//sf_d($occasions_data);exit;
$occasions_id = md5(json_encode($occasions_data));
}
$data["occasionsID"] = $occasions_id;
// Log event type, defaults to other if not set
/*
if ( isset( $context["_type"] ) ) {
$data["type"] = $context["_type"];
unset( $context["_type"] );
} else {
$data["type"] = SimpleLoggerLogTypes::OTHER;
}
*/
// Log initiator, defaults to current user if exists, or other if not user exist
if (isset($context["_initiator"])) {
// Manually set in context
$data["initiator"] = $context["_initiator"];
unset($context["_initiator"]);
} else {
// No initiator set, try to determine
// Default to other
$data["initiator"] = SimpleLoggerLogInitiators::OTHER;
// Check if user is responsible.
if (function_exists("wp_get_current_user")) {
$current_user = wp_get_current_user();
if (isset($current_user->ID) && $current_user->ID) {
$data["initiator"] = SimpleLoggerLogInitiators::WP_USER;
$context["_user_id"] = $current_user->ID;
$context["_user_login"] = $current_user->user_login;
$context["_user_email"] = $current_user->user_email;
}
}
// If cron then set WordPress as responsible
if (defined('DOING_CRON') && DOING_CRON) {
// Seems to be wp cron running and doing this
$data["initiator"] = SimpleLoggerLogInitiators::WORDPRESS;
$context["_wp_cron_running"] = true;
}
// If running as CLI and WP_CLI_PHP_USED is set then it is WP CLI that is doing it
// How to log this? Is this a user, is it WordPress, or what?
// I'm thinking:
// - it is a user that is manually doing this, on purpose, with intent, so not auto wordpress
// - it is a specific user, but we don't know who
// - sounds like a special case, set initiator to wp_cli
// Can be used by plugins/themes to check if WP-CLI is running or not
if (defined("WP_CLI") && WP_CLI) {
$data["initiator"] = SimpleLoggerLogInitiators::WP_CLI;
}
}
// Detect XML-RPC calls and append to context, if not already there
if (defined("XMLRPC_REQUEST") && XMLRPC_REQUEST && !isset($context["_xmlrpc_request"])) {
$context["_xmlrpc_request"] = true;
}
// Trim message
$data["message"] = trim($data["message"]);
/**
* Filter data to be saved to db
*
* @since 2.0
*
* @param array $data
*/
$data = apply_filters("simple_history/log_insert_data", $data);
// Insert data into db
// sf_d($db_table, '$db_table');exit;
$result = $wpdb->insert($db_table, $data);
$data_parent_row = null;
// Only save context if able to store row
if (false === $result) {
$history_inserted_id = null;
} else {
$history_inserted_id = $wpdb->insert_id;
$db_table_contexts = $wpdb->prefix . SimpleHistory::DBTABLE_CONTEXTS;
/**
* Filter table name for contexts
*
* @since 2.0
*
* @param string $db_table_contexts
*/
$db_table_contexts = apply_filters("simple_history/logger_db_table_contexts", $db_table_contexts);
if (!is_array($context)) {
$context = array();
}
// Append user id to context, if not already added
if (!isset($context["_user_id"])) {
// wp_get_current_user is ont available early
// http://codex.wordpress.org/Function_Reference/wp_get_current_user
// https://core.trac.wordpress.org/ticket/14024
if (function_exists("wp_get_current_user")) {
$current_user = wp_get_current_user();
if (isset($current_user->ID) && $current_user->ID) {
$context["_user_id"] = $current_user->ID;
$context["_user_login"] = $current_user->user_login;
$context["_user_email"] = $current_user->user_email;
}
}
}
// Add remote addr to context
// Good to always have
if (!isset($context["_server_remote_addr"])) {
$context["_server_remote_addr"] = empty($_SERVER["REMOTE_ADDR"]) ? "" : $_SERVER["REMOTE_ADDR"];
// If web server is behind a load balancer then the ip address will always be the same
// See bug report: https://wordpress.org/support/topic/use-x-forwarded-for-http-header-when-logging-remote_addr?replies=1#post-6422981
// Note that the x-forwarded-for header can contain multiple ips, comma separated
// Also note that the header can be faked
// Ref: http://stackoverflow.com/questions/753645/how-do-i-get-the-correct-ip-from-http-x-forwarded-for-if-it-contains-multiple-ip
// Ref: http://blackbe.lt/advanced-method-to-obtain-the-client-ip-in-php/
// Check for IP in lots of headers
// Based on code found here:
// http://blackbe.lt/advanced-method-to-obtain-the-client-ip-in-php/
$ip_keys = $this->get_ip_number_header_keys();
foreach ($ip_keys as $key) {
if (array_key_exists($key, $_SERVER) === true) {
// Loop through all IPs
$ip_loop_num = 0;
foreach (explode(',', $_SERVER[$key]) as $ip) {
// trim for safety measures
$ip = trim($ip);
// attempt to validate IP
if ($this->validate_ip($ip)) {
// valid, add to context, with loop index appended so we can store many IPs
$key_lower = strtolower($key);
$context["_server_{$key_lower}_{$ip_loop_num}"] = $ip;
}
$ip_loop_num++;
}
}
}
}
// Append http referer
// Also good to always have!
if (!isset($context["_server_http_referer"]) && isset($_SERVER["HTTP_REFERER"])) {
$context["_server_http_referer"] = $_SERVER["HTTP_REFERER"];
}
/**
* Filter the context to store for this event/row
*
* @since 2.0.29
*
* @param array $context Array with all context data to store. Modify and return this.
* @param array $data Array with data used for parent row.
* @param array $this Reference to this logger instance
*/
$context = apply_filters("simple_history/log_insert_context", $context, $data, $this);
$data_parent_row = $data;
// Insert all context values into db
foreach ($context as $key => $value) {
// If value is array or object then use json_encode to store it
//if ( is_object( $value ) || is_array( $value ) ) {
// $value = simpleHistory::json_encode($value);
//}
// Any reason why the check is not the other way around?
// Everything except strings should be json_encoded
if (!is_string($value)) {
$value = simpleHistory::json_encode($value);
}
$data = array("history_id" => $history_inserted_id, "key" => $key, "value" => $value);
$result = $wpdb->insert($db_table_contexts, $data);
}
}
$this->lastInsertID = $history_inserted_id;
$this->simpleHistory->get_cache_incrementor(true);
/**
* Action that is called after an event has been logged
*
* @since 2.5.1
*
* @param array $context Array with all context data to store. Modify and return this.
* @param array $data Array with data used for parent row.
* @param array $this Reference to this logger instance
*/
do_action("simple_history/log/inserted", $context, $data_parent_row, $this);
// Return $this so we can chain methods
return $this;
}