* $this->info( $this->messages["POST_UPDATED"] );
*
* or with the shortcut
*
* $this->infoMessage("POST_UPDATED");
*
* which results in the original, untranslated, string being added to the log and database
* the translated string are then only used when showing the log in the GUI
*/
public $messages;
/**
* ID of last inserted row. Used when chaining methods.
*/
public $lastInsertID;
/**
* Constructor. Remember to call this as parent constructor if making a childlogger
* @param $simpleHistory history class objectinstance
*/
public function __construct( $simpleHistory = null ) {
global $wpdb;
$this->db_table = $wpdb->prefix . SimpleHistory::DBTABLE;
$this->db_table_contexts = $wpdb->prefix . SimpleHistory::DBTABLE_CONTEXTS;
$this->simpleHistory = $simpleHistory;
}
/**
* Method that is called automagically when logger is loaded by Simple History
* Add your init stuff here
*/
public function loaded() {
}
/**
* Get array with information about this logger
*
* @return array
*/
function getInfo() {
$arr_info = array(
// The logger slug. Defaulting to the class name is nice and logical I think
"slug" => __CLASS__,
// Shown on the info-tab in settings, use these fields to tell
// an admin what your logger is used for
"name" => "SimpleLogger",
"description" => "The built in logger for Simple History",
// Capability required to view log entries from this logger
"capability" => "edit_pages",
"messages" => array(
// No pre-defined variants
// when adding messages __() or _x() must be used
),
);
return $arr_info;
}
/**
* Return single array entry from the array in getInfo()
* Returns the value of the key if value exists, or null
*
* @since 2.5.4
* @return Mixed
*/
function getInfoValueByKey( $key ) {
$arr_info = $this->getInfo();
return isset( $arr_info[ $key ] ) ? $arr_info[ $key ] : null;
}
/**
* Returns the capability required to read log rows from this logger
*
* @return $string capability
*/
public function getCapability() {
$arr_info = $this->getInfo();
$capability = "manage_options";
if ( ! empty( $arr_info["capability"] ) ) {
$capability = $arr_info["capability"];
}
return $capability;
}
/**
* Interpolates context values into the message placeholders.
*
* @param string $message
* @param array $context
* @param array $row Currently not always passed, because loggers need to be updated to support this...
*/
function interpolate( $message, $context = array(), $row = null ) {
if ( ! is_array( $context ) ) {
return $message;
}
/**
* Filter the context used to create the message from the message template
*
* @since 2.2.4
*/
$context = apply_filters( "simple_history/logger/interpolate/context", $context, $message, $row );
// Build a replacement array with braces around the context keys
$replace = array();
foreach ( $context as $key => $val ) {
// Both key and val must be strings or number (for vals)
if ( is_string( $key ) || is_numeric( $key ) ) {
// key ok
}
if ( is_string( $val ) || is_numeric( $val ) ) {
// val ok
} else {
// not a value we can replace
continue;
}
$replace['{' . $key . '}'] = $val;
}
// Interpolate replacement values into the message and return
/*
if ( ! is_string( $message )) {
echo "message:";
var_dump($message);exit;
}
//*/
/*
if ( ! is_string( $replace )) {
echo "replace: \n";
var_dump($replace);
}
// */
return strtr( $message, $replace );
}
/**
* Returns header output for a log row
* Format should be common for all log rows and should be like:
* Username (user role) ยท Date
* @return string HTML
*/
function getLogRowHeaderOutput($row) {
// HTML for initiator
$initiator_html = "";
$initiator = $row->initiator;
$context = $row->context;
switch ($initiator) {
case "wp":
$initiator_html .= 'WordPress ';
break;
case "wp_cli":
$initiator_html .= 'WP-CLI ';
break;
// wp_user = wordpress uses, but user may have been deleted since log entry was added
case "wp_user":
$user_id = isset($row->context["_user_id"]) ? $row->context["_user_id"] : null;
if ($user_id > 0 && $user = get_user_by("id", $user_id)) {
// Sender is user and user still exists
$is_current_user = ($user_id == get_current_user_id()) ? true : false;
// get user role, as done in user-edit.php
$wp_roles = $GLOBALS["wp_roles"];
$all_roles = (array) $wp_roles->roles;
$user_roles = array_intersect( array_values( (array) $user->roles ), array_keys( (array) $wp_roles->roles ));
$user_role = array_shift( $user_roles );
$user_display_name = $user->display_name;
/*
* If user who logged this is the currently logged in user
* skip name and email and use just "You"
*
* @param bool If you should be used
* @since 2.1
*/
$use_you = apply_filters("simple_history/header_initiator_use_you", true);
if ( $use_you && $is_current_user ) {
$tmpl_initiator_html = '
%5$s
';
} else {
$tmpl_initiator_html = '
%3$s
%2$s
';
}
/**
* Filter the format for the user output
*
* @since 2.0
*
* @param string $format.
*/
$tmpl_initiator_html = apply_filters("simple_history/header_initiator_html_existing_user", $tmpl_initiator_html);
$initiator_html .= sprintf(
$tmpl_initiator_html,
esc_html($user->user_login), // 1
esc_html($user->user_email), // 2
esc_html($user_display_name), // 3
$user_role, // 4
_x("You", "header output when initiator is the currently logged in user", "simple-history"), // 5
get_edit_user_link( $user_id ) // 6
);
} else if ($user_id > 0) {
// Sender was a user, but user is deleted now
// output all info we have
// _user_id
// _username
// _user_login
// _user_email
$initiator_html .= sprintf(
'' .
__('Deleted user (had id %1$s, email %2$s, login %3$s)', "simple-history") .
'',
esc_html($context["_user_id"]), // 1
esc_html($context["_user_email"]), // 2
esc_html($context["_user_login"]) // 3
);
}
break;
case "web_user":
/*
Note: server_remote_addr may not show visiting/attacking ip, if server is behind...stuff..
Can be behind varnish cashe, or browser can for example use compression in chrome mobile
then the real ip is behind _server_http_x_forwarded_for_0 or similar
_server_remote_addr 66.249.81.222
_server_http_x_forwarded_for_0 5.35.187.212
*/
// Check if additional IP addresses are stored, from http_x_forwarded_for and so on
$arr_found_additional_ip_headers = $this->get_event_ip_number_headers($row);
if ( empty( $context["_server_remote_addr"] ) ) {
$initiator_html .= "" . __("Anonymous web user", "simple-history") . " ";
} else {
$initiator_html .= "";
#if ( sizeof( $arr_found_additional_ip_headers ) ) {
#$iplookup_link = sprintf('https://ipinfo.io/%1$s', esc_attr($context["_server_remote_addr"]));
#$ip_numbers_joined = wp_sprintf_l('%l', array("_server_remote_addr" => $context["_server_remote_addr"]) + $arr_found_additional_ip_headers);
/*$initiator_html .= sprintf(
__('Anonymous user with multiple IP addresses detected: %1$s', "simple-history"),
"" . esc_html( $ip_numbers_joined ) . ""
);*/
/*
print_r($arr_found_additional_ip_headers);
Array
(
[_server_http_x_forwarded_for_0] => 5.35.187.212
[_server_http_x_forwarded_for_1] => 83.251.97.21
)
*/
#} else {
// single ip address
$iplookup_link = sprintf('https://ipinfo.io/%1$s', esc_attr($context["_server_remote_addr"]));
$initiator_html .= sprintf(
__('Anonymous user from %1$s', "simple-history"),
"" . esc_html($context["_server_remote_addr"]) . ""
);
#} // multiple ip
$initiator_html .= " ";
// $initiator_html .= "" . __("
Unknown user from {$context["_server_remote_addr"]}") . "";
// $initiator_html .= "" . __("
{$context["_server_remote_addr"]}") . "";
// $initiator_html .= "" . __("
User from IP {$context["_server_remote_addr"]}") . "";
// $initiator_html .= "" . __("
Non-logged in user from IP {$context["_server_remote_addr"]}") . "";
}
break;
case "other":
$initiator_html .= "" . _x("Other", "Event header output, when initiator is unknown", "simple-history") . "";
break;
// no initiator
case null:
// $initiator_html .= "Null";
break;
default:
$initiator_html .= "" . esc_html($initiator) . "";
}
/**
* Filter generated html for the initiator row header html
*
* @since 2.0
*
* @param string $initiator_html
* @param object $row Log row
*/
$initiator_html = apply_filters("simple_history/row_header_initiator_output", $initiator_html, $row);
// HTML for date
// Date (should...) always exist
// http://developers.whatwg.org/text-level-semantics.html#the-time-element
$date_html = "";
$str_when = "";
// $row->date is in GMT
$date_datetime = new DateTime( $row->date, new DateTimeZone('GMT') );
// Current datetime in GMT
$time_current = strtotime( current_time("mysql", 1) );
/**
* Filter how many seconds as most that can pass since an
* event occured to show "nn minutes ago" (human diff time-format) instead of exact date
*
* @since 2.0
*
* @param int $time_ago_max_time Seconds
*/
$time_ago_max_time = DAY_IN_SECONDS * 2;
$time_ago_max_time = apply_filters("simple_history/header_time_ago_max_time", $time_ago_max_time);
/**
* Filter how many seconds as most that can pass since an
* event occured to show "just now" instead of exact date
*
* @since 2.0
*
* @param int $time_ago_max_time Seconds
*/
$time_ago_just_now_max_time = 30;
$time_ago_just_now_max_time = apply_filters("simple_history/header_just_now_max_time", $time_ago_just_now_max_time);
if ( $time_current - $date_datetime->getTimestamp() <= $time_ago_just_now_max_time ) {
// show "just now" if event is very recent
$str_when = __("Just now", "simple-history");
} else if ( $time_current - $date_datetime->getTimestamp() > $time_ago_max_time ) {
/* translators: Date format for log row header, see http://php.net/date */
$datef = __('M j, Y \a\t G:i', "simple-history");
$str_when = date_i18n( $datef, strtotime( get_date_from_gmt( $row->date ) ) );
} else {
// Show "nn minutes ago" when event is xx seconds ago or earlier
$date_human_time_diff = human_time_diff($date_datetime->getTimestamp(), $time_current );
/* translators: 1: last modified date and time in human time diff-format */
$str_when = sprintf(__('%1$s ago', 'simple-history'), $date_human_time_diff);
}
$item_permalink = admin_url("index.php?page=simple_history_page");
if ( ! empty( $row->id ) ) {
$item_permalink .= "#item/{$row->id}";
}
$date_format = get_option('date_format') . ' - '. get_option('time_format');
$str_datetime_title = sprintf(
__('%1$s local time %3$s (%2$s GMT time)', "simple-history"),
get_date_from_gmt( $date_datetime->format('Y-m-d H:i:s'), $date_format ), // 1 local time
$date_datetime->format( $date_format ), // GMT time
PHP_EOL // 3, new line
);
$date_html = "";
$date_html .= "";
$date_html .= sprintf(
'',
esc_attr( $str_datetime_title ), // 1 datetime attribute
esc_html( $str_when ), // 2 date text, visible in log
$date_datetime->format( DateTime::RFC3339 ) // 3
);
$date_html .= "";
$date_html .= "";
/**
* Filter the output of the date section of the header.
*
* @since 2.5.1
*
* @param String $date_html
* @param array $row
*/
$date_html = apply_filters("simple_history/row_header_date_output", $date_html, $row);
// Logger "via" info in header, i.e. output some extra
// info next to the time to make it more clear what plugin etc.
// that "caused" this event
$via_html = "";
$logger_name_via = $this->getInfoValueByKey("name_via");
if ( $logger_name_via ) {
$via_html = "";
$via_html .= $logger_name_via;
$via_html .= "";
}
// Loglevel
// SimpleHistoryLogitem--loglevel-warning
/*
$level_html = sprintf(
'%1$s',
$row->level
);
*/
// Glue together final result
$template = '
%1$s
%2$s
%3$s
';
#if ( ! $initiator_html ) {
# $template = '%2$s';
#}
$html = sprintf(
$template,
$initiator_html, // 1
$date_html, // 2
$via_html // 3
);
/**
* Filter generated html for the log row header
*
* @since 2.0
*
* @param string $html
* @param object $row Log row
*/
$html = apply_filters("simple_history/row_header_output", $html, $row);
return $html;
}
/**
* Returns the plain text version of this entry
* Used in for example CSV-exports.
* Defaults to log message with context interpolated.
* Keep format as plain and simple as possible.
* Links are ok, for example to link to users or posts.
* Tags will be stripped when text is used for CSV-exports and so on.
* Keep it on a single line. No
or
and so on.
*
* Example output:
* Edited post "About the company"
*
* Message should sound like it's coming from the user.
* Image that the name of the user is added in front of the text:
* Jessie James: Edited post "About the company"
*/
public function getLogRowPlainTextOutput($row) {
$message = $row->message;
$message_key = isset( $row->context["_message_key"] ) ? $row->context["_message_key"] : null;
// Message is translated here, but translation must be added in
// plain text before
if ( empty( $message_key ) ) {
// Message key did not exist, so check if we should translate using textdomain
if ( ! empty( $row->context["_gettext_domain"] ) ) {
$message = __( $message, $row->context["_gettext_domain"] );
}
} else {
// Check that messages does exist
// If we for example disable a Logger we may have references
// to message keys that are unavailable. If so then fallback to message.
if ( isset( $this->messages[$message_key]["translated_text"] ) ) {
$message = $this->messages[$message_key]["translated_text"];
} else {
// Not message exists for message key. Just keep using message.
}
}
$html = $this->interpolate($message, $row->context, $row);
// All messages are escaped by default.
// If you need unescaped output override this method
// in your own logger
$html = esc_html($html);
/**
* Filter generated output for plain text output
*
* @since 2.0
*
* @param string $html
* @param object $row Log row
*/
$html = apply_filters("simple_history/row_plain_text_output", $html, $row);
return $html;
}
/**
* Get output for image
* Image can be for example gravar if sender is user,
* or other images if sender i system, wordpress, and so on
*/
public function getLogRowSenderImageOutput($row) {
$sender_image_html = "";
$sender_image_size = 32;
$initiator = $row->initiator;
switch ($initiator) {
// wp_user = wordpress uses, but user may have been deleted since log entry was added
case "wp_user":
$user_id = isset($row->context["_user_id"]) ? $row->context["_user_id"] : null;
if ($user_id > 0 && $user = get_user_by("id", $user_id)) {
// Sender was user
$sender_image_html = $this->simpleHistory->get_avatar($user->user_email, $sender_image_size);
} else if ($user_id > 0) {
// Sender was a user, but user is deleted now
$sender_image_html = $this->simpleHistory->get_avatar("", $sender_image_size);
} else {
$sender_image_html = $this->simpleHistory->get_avatar("", $sender_image_size);
}
break;
}
/**
* Filter generated output for row image (sender image)
*
* @since 2.0
*
* @param string $sender_image_html
* @param object $row Log row
*/
$sender_image_html = apply_filters("simple_history/row_sender_image_output", $sender_image_html, $row);
return $sender_image_html;
}
/**
* Use this method to output detailed output for a log row
* Example usage is if a user has uploaded an image then a
* thumbnail of that image can bo outputed here
*
* @param object $row
* @return string HTML-formatted output
*/
public function getLogRowDetailsOutput($row) {
$html = "";
/**
* Filter generated output for details
*
* @since 2.0
*
* @param string $html
* @param object $row Log row
*/
$html = apply_filters("simple_history/row_details_output", $html, $row);
return $html;
}
/**
* System is unusable.
*
* @param string $message
* @param array $context
* @return null
*/
public static function emergency($message, array $context = array()) {
return $this->log(SimpleLoggerLogLevels::EMERGENCY, $message, $context);
}
/**
* System is unusable.
*
* @param string $message key from getInfo messages array
* @param array $context
* @return null
*/
public function emergencyMessage($message, array $context = array()) {
return $this->logByMessageKey(SimpleLoggerLogLevels::EMERGENCY, $message, $context);
}
/**
* Log with message
* Called from infoMessage(), errorMessage(), and so on
*
* Call like this:
*
* return $this->logByMessageKey(SimpleLoggerLogLevels::EMERGENCY, $message, $context);
*/
private function logByMessageKey($SimpleLoggerLogLevelsLevel, $messageKey, $context) {
// When logging by message then the key must exist
if ( ! isset( $this->messages[ $messageKey ]["untranslated_text"] ) ) {
return;
}
/**
* Filter so plugins etc. can shortut logging
*
* @since 2.0.20
*
* @param true yes, we default to do the logging
* @param string logger slug
* @param string messageKey
* @param string log level
* @param array context
* @return bool false to abort logging
*/
$doLog = apply_filters("simple_history/simple_logger/log_message_key", true, $this->slug, $messageKey, $SimpleLoggerLogLevelsLevel, $context);
if ( ! $doLog ) {
return;
}
$context["_message_key"] = $messageKey;
$message = $this->messages[ $messageKey ]["untranslated_text"];
$this->log( $SimpleLoggerLogLevelsLevel, $message, $context );
}
/**
* Action must be taken immediately.
*
* @param string $message
* @param array $context
* @return null
*/
public static function alert($message, array $context = array()) {
return $this->log(SimpleLoggerLogLevels::ALERT, $message, $context);
}
/**
* Action must be taken immediately.
*
* @param string $message key from getInfo messages array
* @param array $context
* @return null
*/
public function alertMessage($message, array $context = array()) {
return $this->logByMessageKey(SimpleLoggerLogLevels::ALERT, $message, $context);
}
/**
* Critical conditions.
*
* Example: Application component unavailable, unexpected exception.
*
* @param string $message
* @param array $context
* @return null
*/
public static function critical($message, array $context = array()) {
return $this->log(SimpleLoggerLogLevels::CRITICAL, $message, $context);
}
/**
* Critical conditions.
*
* @param string $message key from getInfo messages array
* @param array $context
* @return null
*/
public function criticalMessage($message, array $context = array()) {
if (!isset($this->messages[$message]["untranslated_text"])) {
return;
}
$context["_message_key"] = $message;
$message = $this->messages[$message]["untranslated_text"];
$this->log(SimpleLoggerLogLevels::CRITICAL, $message, $context);
}
/**
* Runtime errors that do not require immediate action but should typically
* be logged and monitored.
*
* @param string $message
* @param array $context
* @return null
*/
public function error($message, array $context = array()) {
return $this->log(SimpleLoggerLogLevels::ERROR, $message, $context);
}
/**
* Runtime errors that do not require immediate action but should typically
* be logged and monitored.
*
* @param string $message key from getInfo messages array
* @param array $context
* @return null
*/
public function errorMessage($message, array $context = array()) {
return $this->logByMessageKey(SimpleLoggerLogLevels::ERROR, $message, $context);
}
/**
* Exceptional occurrences that are not errors.
*
* Example: Use of deprecated APIs, poor use of an API, undesirable things
* that are not necessarily wrong.
*
* @param string $message
* @param array $context
* @return null
*/
public function warning($message, array $context = array()) {
return $this->log(SimpleLoggerLogLevels::WARNING, $message, $context);
}
/**
* Exceptional occurrences that are not errors.
*
* @param string $message key from getInfo messages array
* @param array $context
* @return null
*/
public function warningMessage($message, array $context = array()) {
return $this->logByMessageKey(SimpleLoggerLogLevels::WARNING, $message, $context);
}
/**
* Normal but significant events.
*
* @param string $message
* @param array $context
* @return null
*/
public function notice($message, array $context = array()) {
return $this->log(SimpleLoggerLogLevels::NOTICE, $message, $context);
}
/**
* Normal but significant events.
*
* @param string $message key from getInfo messages array
* @param array $context
* @return null
*/
public function noticeMessage($message, array $context = array()) {
return $this->logByMessageKey(SimpleLoggerLogLevels::NOTICE, $message, $context);
}
/**
* Interesting events.
*
* Example: User logs in, SQL logs.
*
* @param string $message
* @param array $context
* @return null
*/
public function info($message, array $context = array()) {
return $this->log(SimpleLoggerLogLevels::INFO, $message, $context);
}
/**
* Interesting events.
*
* Example: User logs in, SQL logs.
*
* @param string $message key from getInfo messages array
* @param array $context
* @return null
*/
public function infoMessage($message, array $context = array()) {
return $this->logByMessageKey(SimpleLoggerLogLevels::INFO, $message, $context);
}
/**
* Detailed debug information.
*
* @param string $message
* @param array $context
* @return null
*/
public function debug($message, array $context = array()) {
return $this->log(SimpleLoggerLogLevels::DEBUG, $message, $context);
}
/**
* Detailed debug information.
*
* @param string $message key from getInfo messages array
* @param array $context
* @return null
*/
public function debugMessage($message, array $context = array()) {
return $this->logByMessageKey(SimpleLoggerLogLevels::DEBUG, $message, $context);
}
/**
* Logs with an arbitrary level.
*
* @param mixed $level
* @param string $message
* @param array $context
* @return null
*/
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;
} // log
/**
* Returns array with headers that may contain user IP
*
* @since 2.0.29
*/
public function get_ip_number_header_keys() {
$arr = array(
'HTTP_CLIENT_IP',
'HTTP_X_FORWARDED_FOR',
'HTTP_X_FORWARDED',
'HTTP_X_CLUSTER_CLIENT_IP',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED'
);
return $arr;
}
/**
* Returns additional headers with ip number from context
*
* @since 2.0.29
*/
function get_event_ip_number_headers($row) {
$ip_keys = $this->get_ip_number_header_keys();
$arr_found_additional_ip_headers = array();
$context = $row->context;
foreach ( $ip_keys as $one_ip_header_key ) {
$one_ip_header_key_lower = strtolower($one_ip_header_key);
foreach ( $context as $context_key => $context_val ) {
#$key_check_for = "_server_" . strtolower($one_ip_header_key) . "_0";
$match = preg_match("/^_server_{$one_ip_header_key_lower}_[\d+]/", $context_key, $matches);
if ( $match ) {
$arr_found_additional_ip_headers[ $context_key ] = $context_val;
}
} // foreach context key for this ip header key
} // foreach ip header key
return $arr_found_additional_ip_headers;
}
/**
* Ensures an ip address is both a valid IP and does not fall within
* a private network range.
*/
function validate_ip($ip) {
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4|FILTER_FLAG_NO_PRIV_RANGE|FILTER_FLAG_NO_RES_RANGE) === false) {
return false;
}
return true;
}
/**
* Override this to add CSS in