Files
2026-04-13 15:50:16 +02:00

689 lines
31 KiB
PHP

<?php
/**
* Easy Updates Manager log controller
*
* Initializes the log table and sets up actions/events
*
* @package WordPress
* @since 6.0.0
*/
class MPSUM_Logs {
/**
* Holds the class instance.
*
* @since 6.0.0
* @access static
* @var MPSUM_Logs $instance
*/
public static $instance = null;
/**
* Holds log messages
*
* @var array An array of log messages
*/
protected $log_messages = array();
/**
* Holds update method information
*
* @var bool Determines whether auto update or manual
*/
protected $auto_update = false;
// Format: key=<version>, value=array of method names to call
private static $db_updates = array(
'1.1.6' => array(
'build_table',
)
);
/**
* Holds version number of the table
*
* @since 6.0.0
* @access private
* @var string $slug
*/
private static $version = '1.1.6';
/**
* Holds a variable for checkin the logs table
*
* @since 8.0.1
* @access private
* @var bool $log_table_exists
*/
private static $log_table_exists = false;
/**
* Set a class instance.
*
* Set a class instance.
*
* @since 5.0.0
* @access static
*/
public static function run() {
if (null == self::$instance) {
self::$instance = new self;
}
return self::$instance;
} //end get_instance
/**
* Class constructor.
*
* Initialize the class
*
* @since 6.0.0
* @access private
*/
protected function __construct() {
register_shutdown_function(array($this, 'perform_shutdown_task'));
$this->check_updates();
add_action('pre_auto_update', array($this, 'pre_auto_update'));
add_filter('eum_i18n', array($this, 'logs_i18n'));
add_filter('upgrader_package_options', array($this, 'initialize_log_messages'), 10, 1);
add_action('automatic_updates_complete', array($this, 'log_automatic_updates'));
add_action('upgrader_process_complete', array($this, 'log_updates'), 1, 2);
add_filter('upgrader_pre_download', array($this, 'initialize_core_log_messages'), 10, 3);
} //end constructor
/**
* Make sure PHP fatal errors are caught during an manual/automatic update and try to log anything that gets updated
*/
public function perform_shutdown_task() {
self::maybe_log_updates();
}
/**
* Write remaining update information in the $log_messages variable into the database (if any)
*/
private function maybe_log_updates() {
if (!is_array($this->log_messages) || empty($this->log_messages)) return;
try {
$stacktrace = maybe_serialize(apply_filters('eum_normalized_call_stack_args', $this->normalise_call_stack_args(debug_backtrace(false)))); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace, PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection -- Using debug_backtrace() to normalize call stack for logging purposes.
} catch (Exception $e) {
$stacktrace = serialize(array()); // if an exception still happens even after the call stack is already normalised then we won't provide stacktrace for a log entry
// @codingStandardsIgnoreLine
} catch (Error $e) {
$stacktrace = serialize(array());
}
foreach ($this->log_messages as $type => $entities) {
if (!is_array($entities) || empty($entities)) continue;
foreach ($entities as $data) {
$this->insert_log($data['name'], $type, $data['from'], $data['to'], $this->auto_update ? 'automatic' : 'manual', doing_action('upgrader_process_complete') || doing_action('automatic_updates_complete') ? 1 : 0, get_current_user_id(), '', $stacktrace);
}
}
$this->log_messages = array();
}
/**
* Initialize information of an update for each plugin/theme/translation before running the update operation. The information will be stored in the $log_messages variable keyed by the name of what entity is being updated
*
* @param array $options Package options used by the upgrader
* @return array An array of associative arrays keyed by some strings
*/
public function initialize_log_messages($options) {
if (isset($options['hook_extra'], $options['hook_extra']['action']) && 'update' !== $options['hook_extra']['action']) return $options;
if (isset($options['hook_extra'], $options['hook_extra']['plugin'])) {
$current = get_site_transient('update_plugins');
if (isset($current->response[$options['hook_extra']['plugin']])) {
$plugins = get_plugins(); // It's not expensive calling this API as WordPress has already cached the result in a WP Cache object
if (!isset($this->log_messages['plugin'])) $this->log_messages['plugin'] = array();
$this->log_messages['plugin'][$options['hook_extra']['plugin']] = array(
'name' => isset($plugins[$options['hook_extra']['plugin']]) && isset($plugins[$options['hook_extra']['plugin']]['Name']) ? $plugins[$options['hook_extra']['plugin']]['Name'] : 'Unknown',
'from' => isset($plugins[$options['hook_extra']['plugin']]) && isset($plugins[$options['hook_extra']['plugin']]['Version']) ? $plugins[$options['hook_extra']['plugin']]['Version'] : 0,
'to' => isset($current->response[$options['hook_extra']['plugin']]) && is_object($current->response[$options['hook_extra']['plugin']]) && isset($current->response[$options['hook_extra']['plugin']]->new_version) ? $current->response[$options['hook_extra']['plugin']]->new_version : 0,
'status' => 0
);
}
} elseif (isset($options['hook_extra'], $options['hook_extra']['theme'])) {
$current = get_site_transient('update_themes');
if (isset($current->response[$options['hook_extra']['theme']])) {
$themes = wp_get_themes(); // It's not expensive calling this API as WordPress has already cached the result as a static variable inside the function
if (!isset($this->log_messages['theme'])) $this->log_messages['theme'] = array();
$this->log_messages['theme'][$options['hook_extra']['theme']] = array(
'name' => isset($themes[$options['hook_extra']['theme']]) && is_a($themes[$options['hook_extra']['theme']], 'WP_Theme') ? $themes[$options['hook_extra']['theme']]->get('Name') : 'Unknown',
'from' => isset($themes[$options['hook_extra']['theme']]) && is_a($themes[$options['hook_extra']['theme']], 'WP_Theme') ? $themes[$options['hook_extra']['theme']]->get('Version') : 0,
'to' => isset($current->response[$options['hook_extra']['theme']]) && isset($current->response[$options['hook_extra']['theme']]['new_version']) ? $current->response[$options['hook_extra']['theme']]['new_version'] : 0,
'status' => 0
);
}
} elseif (isset($options['hook_extra']) && isset($options['hook_extra']['language_update_type']) && isset($options['hook_extra']['language_update'])) { // translation
if (!isset($this->log_messages['translation'])) $this->log_messages['translation'] = array();
switch ($options['hook_extra']['language_update_type']) {
case 'plugin':
$current = get_site_transient('update_plugins');
if (empty($current->translations)) break;
$plugins = get_plugins();
$plugins_by_slug = wp_parse_args(wp_list_pluck($current->no_update, 'plugin', 'slug'), wp_list_pluck($current->response, 'plugin', 'slug'));
foreach ($current->translations as $translation) {
if (isset($options['hook_extra']['language_update']->slug) && $translation['slug'] !== $options['hook_extra']['language_update']->slug) continue;
if (isset($plugins[$plugins_by_slug[$translation['slug']]])) {
$this->log_messages['translation'][$translation['slug']] = array(
'name' => isset($plugins[$plugins_by_slug[$translation['slug']]]['Name']) ? $plugins[$plugins_by_slug[$translation['slug']]]['Name']. ' ('.$translation['language'].')' : 'Unknown ('.$translation['language'].')',
'from' => isset($options['hook_extra']['language_update']->version) ? $options['hook_extra']['language_update']->version : 0,
'to' => isset($translation['version']) ? $translation['version'] : 0,
'status' => 0
);
break;
}
}
break;
case 'theme':
$current = get_site_transient('update_themes');
if (empty($current->translations)) break;
$themes = wp_get_themes();
foreach ($current->translations as $translation) {
if (isset($options['hook_extra']['language_update']->slug) && $translation['slug'] !== $options['hook_extra']['language_update']->slug) continue;
if (isset($themes[$translation['slug']])) {
$this->log_messages['translation'][$translation['slug']] = array(
'name' => is_a($themes[$translation['slug']], 'WP_Theme') ? $themes[$translation['slug']]->get('Name'). ' ('.$translation['language'].')' : 'Unknown ('.$translation['language'].')',
'from' => isset($options['hook_extra']['language_update']->version) ? $options['hook_extra']['language_update']->version : 0,
'to' => isset($translation['version']) ? $translation['version'] : 0,
'status' => 0
);
break;
}
}
break;
default:
// core
$current = get_site_transient('update_core');
if (empty($current->translations)) break;
foreach ($current->translations as $translation) {
$this->log_messages['translation']['wordpress_default_'.$translation['language']] = array(
'name' => 'WordPress ('.$translation['language'].')',
'from' => isset($options['hook_extra']['language_update']->version) ? $options['hook_extra']['language_update']->version : 0,
'to' => isset($translation['version']) ? $translation['version'] : 0,
'status' => 0
);
}
break;
}
}
return $options;
}
/**
* Initialize information of a core update before downloading its package. The information will be stored in the $log_messages variable keyed by 'core' string
*
* @param bool $reply Whether to bail without returning the package. Default false
* @param string $package The package file name
* @param WP_Upgrader $upgrader The WP_Upgrader instance
*/
public function initialize_core_log_messages($reply, $package, $upgrader) {
add_filter('upgrader_post_install', array($this, 'set_update_status_by_result'), 10, 3);
if (is_a($upgrader, 'Core_Upgrader')) {
global $wp_version;
$current = get_site_transient('update_core');
if (!isset($this->log_messages['core']) || !is_array($this->log_messages['core'])) $this->log_messages['core'] = array();
$item = array(
'name' => 'WordPress',
'from' => $wp_version,
'to' => 0,
'status' => 0,
'notes' => ''
);
add_action('_core_updated_successfully', array($this, 'set_core_update_success_status'));
add_filter('update_feedback', array($this, 'set_core_update_notes'), 1, 1);
if (!$current || empty($current->updates)) return $reply;
foreach ($current->updates as $update) {
if (($this->auto_update && 'autoupdate' !== $update->response) || (!$this->auto_update && 'autoupdate' === $update->response)) continue;
if (empty($update->packages)) continue;
foreach ($update->packages as $download_url) {
if ($download_url === $package) {
$item['to'] = $update->current;
$item['name'] .= " ({$update->locale})";
break;
}
}
}
if (version_compare($wp_version, $item['to'], '==') && empty($this->log_messages['core'])) {
$item['name'] .= ' (Reinstall)';
$this->log_messages['core']['reinstall'] = $item;
} elseif (version_compare($wp_version, $item['to'], '<') && empty($this->log_messages['core'])) {
$this->log_messages['core']['new'] = $item;
} elseif (!empty($this->log_messages['core'])) {
$item['name'] .= ' (Rollback)';
$item['from'] = isset($this->log_messages['core']['new']) ? $this->log_messages['core']['new']['to'] : $item['from'];
$item['to'] = isset($this->log_messages['core']['new']) ? $this->log_messages['core']['new']['from'] : $item['to'];
$this->log_messages['core']['rollback'] = $item;
}
}
return $reply;
}
/**
* Log the information of updates to the log table, it fires when the upgrader process is complete during either manual or automatic operation and it logs only if the $log_messages variable class is not empty
*
* @param WP_Upgrader $wp_upgrader WP_Upgrader instance. In other contexts, $this, might be a Theme_Upgrader, Plugin_Upgrader, Core_Upgrade, or Language_Pack_Upgrader instance
* @param array $hook_extra Extra arguments passed to hooked filters
*/
public function log_updates($wp_upgrader, $hook_extra) {
if (isset($hook_extra['action']) && 'update' !== $hook_extra['action']) return;
try {
$stacktrace = maybe_serialize(apply_filters('eum_normalized_call_stack_args', $this->normalise_call_stack_args(debug_backtrace(false)))); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace, PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection -- Using debug_backtrace() to normalize call stack for logging purposes.
} catch (Exception $e) {
$stacktrace = serialize(array()); // if an exception still happens even after the call stack is already normalised then we won't provide stacktrace for a log entry
// @codingStandardsIgnoreLine
} catch (Error $e) {
$stacktrace = serialize(array());
}
$notes = '';
if (is_a($wp_upgrader->skin, 'Automatic_Upgrader_Skin')) {
foreach ($wp_upgrader->skin->get_upgrade_messages() as $message) {
$notes .= $message . "\n\r\n\r";
}
}
switch ($hook_extra['type']) {
case 'translation':
foreach ($hook_extra['translations'] as $translation) {
$key = 'core' !== $translation['type'] && isset($this->log_messages['translation'][$translation['slug']]) ? $translation['slug'] : ('core' === $translation['type'] && isset($this->log_messages['translation']['wordpress_default_'.$translation['language']]) ? 'wordpress_default_'.$translation['language'] : '');
if ('' === $key) continue;
if (!isset($this->log_messages['translation'][$key])) continue;
$this->insert_log($this->log_messages['translation'][$key]['name'], 'translation', $this->log_messages['translation'][$key]['from'], $this->log_messages['translation'][$key]['to'], $this->auto_update ? 'automatic' : 'manual', $this->log_messages['translation'][$key]['status'], get_current_user_id(), $notes, $stacktrace);
unset($this->log_messages['translation'][$key]);
}
if (empty($this->log_messages['translation'])) unset($this->log_messages['translation']);
break;
case 'core':
foreach ($this->log_messages['core'] as $core_type => $data) {
$this->insert_log($data['name'], $hook_extra['type'], $data['from'], $data['to'], $this->auto_update ? 'automatic' : 'manual', $data['status'], get_current_user_id(), $notes ? $notes : $data['notes'], $stacktrace);
unset($this->log_messages['core'][$core_type]);
}
if (empty($this->log_messages['core'])) unset($this->log_messages['core']);
break;
case 'plugin':
$plugins = isset($hook_extra['plugins']) && is_array($hook_extra['plugins']) ? $hook_extra['plugins'] : (isset($hook_extra['plugin']) ? $hook_extra['plugin'] : array());
foreach ((array) $plugins as $plugin) {
if (!isset($this->log_messages['plugin'][$plugin])) continue;
$this->insert_log($this->log_messages['plugin'][$plugin]['name'], $hook_extra['type'], $this->log_messages['plugin'][$plugin]['from'], $this->log_messages['plugin'][$plugin]['to'], $this->auto_update ? 'automatic' : 'manual', $this->log_messages['plugin'][$plugin]['status'], get_current_user_id(), $notes, $stacktrace);
unset($this->log_messages['plugin'][$plugin]);
}
if (empty($this->log_messages['plugin'])) unset($this->log_messages['plugin']);
break;
case 'theme':
$themes = isset($hook_extra['themes']) && is_array($hook_extra['themes']) ? $hook_extra['themes'] : (isset($hook_extra['theme']) ? $hook_extra['theme'] : array());
foreach ((array) $themes as $theme) {
if (!isset($this->log_messages['theme'][$theme])) continue;
$this->insert_log($this->log_messages['theme'][$theme]['name'], $hook_extra['type'], $this->log_messages['theme'][$theme]['from'], $this->log_messages['theme'][$theme]['to'], $this->auto_update ? 'automatic' : 'manual', $this->log_messages['theme'][$theme]['status'], get_current_user_id(), $notes, $stacktrace);
unset($this->log_messages['theme'][$theme]);
}
if (empty($this->log_messages['theme'])) unset($this->log_messages['theme']);
break;
}
self::maybe_log_updates();
}
/**
* Set notes to the $log_messages variable class during update of the WP core
*
* @param string $note The core update feedback messages
*/
public function set_core_update_notes($note) {
if (!is_array($this->log_messages['core']) || empty($this->log_messages['core']) || !is_string($note)) return $note; // yes, the docblock states that $note is in string type, but that's not always true as somehow it can be WP_Error object
end($this->log_messages['core']);
if (!isset($this->log_messages['core'][key($this->log_messages['core'])]['notes']) || !is_string($this->log_messages['core'][key($this->log_messages['core'])]['notes'])) $this->log_messages['core'][key($this->log_messages['core'])]['notes'] = '';
$this->log_messages['core'][key($this->log_messages['core'])]['notes'] .= $note . "\n\r\n\r";
reset($this->log_messages['core']);
return $note;
}
/**
* Set success to the core update status after WordPress core has been successfully updated
*
* Hooked to the {@see '_core_updated_successfully'} action, added when initializing core log info
*/
public function set_core_update_success_status() {
remove_filter('update_feedback', array($this, 'set_core_update_notes'), 1, 1);
if (!is_array($this->log_messages['core']) || empty($this->log_messages['core'])) return;
end($this->log_messages['core']);
$this->log_messages['core'][key($this->log_messages['core'])]['status'] = 1;
reset($this->log_messages['core']);
}
/**
* Use result data to set update status of plugin/theme/translation after an installation has finished
*
* @param bool $response Installation response
* @param array $hook_extra Extra arguments passed to hooked filters
* @param array $result Installation result data
* @return bool Installation response
*/
public function set_update_status_by_result($response, $hook_extra, $result) {
if (isset($hook_extra['action']) && 'update' !== $hook_extra['action']) return $response;
if (isset($hook_extra['plugin']) && isset($this->log_messages['plugin'][$hook_extra['plugin']])) {
$this->log_messages['plugin'][$hook_extra['plugin']]['status'] = $result && !is_wp_error($result) ? 1 : 0;
} elseif (isset($hook_extra['theme']) && isset($this->log_messages['theme'][$hook_extra['theme']])) {
$this->log_messages['theme'][$hook_extra['theme']]['status'] = $result && !is_wp_error($result) ? 1 : 0;
} elseif (isset($hook_extra['language_update_type']) && isset($hook_extra['language_update'])) {
switch ($hook_extra['language_update_type']) {
case 'core':
$this->log_messages['translation']['wordpress_default_'.$hook_extra['language_update']->language]['status'] = $result && !is_wp_error($result) ? 1 : 0;
break;
default:
// plugins and themes
$this->log_messages['translation'][$hook_extra['language_update']->slug]['status'] = $result && !is_wp_error($result) ? 1 : 0;
break;
}
}
return $response;
}
/**
* Log update information to the log table when automatic updates is complete, and if the $log_messages variable class is not empty. This is final attempt to log updates to the database but this could also mean that nothing has to be done as in the previous action (upgrader_process_complete) the information has already been logged and has been removed from the $log_messages variable
* The reason why this method is hooked to the `automatic_updates_complete` and why we still need it is that in some cases when an error occurs during automatic updates the `upgrader_process_complete` action won't get executed, but this action `automatic_updates_complete`/method will. Actually, without having this method we can still log the info on shutdown, but it lacks of notes
*
* @param array $update_results The results of all attempted updates
*/
public function log_automatic_updates($update_results) {
if (empty($update_results)) return;
try {
$stacktrace = maybe_serialize(apply_filters('eum_normalized_call_stack_args', $this->normalise_call_stack_args(debug_backtrace(false)))); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace, PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection -- Using debug_backtrace() to normalize call stack for logging purposes.
} catch (Exception $e) {
$stacktrace = serialize(array()); // if an exception still happens even after the call stack is already normalised then we won't provide stacktrace for a log entry
// @codingStandardsIgnoreLine
} catch (Error $e) {
$stacktrace = serialize(array());
}
foreach ($update_results as $type => $results) {
foreach ($results as $result) {
$notes = '';
if (!isset($result->messages)) $result->messages = array();
foreach ($result->messages as $message) {
$notes .= $message . "\n\r\n\r";
}
if ('core' === $type) {
if (!isset($this->log_messages[$type])) continue;
foreach ($this->log_messages[$type] as $core_type => $data) {
$this->insert_log($data['name'], $type, $data['from'], $data['to'], 'automatic', $data['status'], get_current_user_id(), $notes ? $notes : $data['notes'], $stacktrace);
unset($this->log_messages[$type][$core_type]);
}
if (empty($this->log_messages[$type])) unset($this->log_messages[$type]);
} elseif ('translation' === $type) {
if (!isset($this->log_messages[$type], $this->log_messages[$type][$result->item->slug])) continue;
$this->insert_log($this->log_messages[$type][$result->item->slug]['name'], $type, $this->log_messages[$type][$result->item->slug]['from'], $this->log_messages[$type][$result->item->slug]['to'], 'automatic', isset($result->result) && $result->result && !is_wp_error($result->result) ? 1 : 0, get_current_user_id(), $notes, $stacktrace);
unset($this->log_messages[$type][$result->item->slug]);
if (empty($this->log_messages[$type])) unset($this->log_messages[$type]);
} else {
if (!isset($this->log_messages[$type], $this->log_messages[$type][$result->item->$type])) continue;
$this->insert_log($this->log_messages[$type][$result->item->$type]['name'], $type, $this->log_messages[$type][$result->item->$type]['from'], $this->log_messages[$type][$result->item->$type]['to'], 'automatic', isset($result->result) && $result->result && !is_wp_error($result->result) ? 1 : 0, get_current_user_id(), $notes, $stacktrace);
unset($this->log_messages[$type][$result->item->$type]);
if (empty($this->log_messages[$type])) unset($this->log_messages[$type]);
}
}
}
self::maybe_log_updates();
}
/**
* See if any database schema updates are needed, and perform them if so.
*
* @return void
*/
public static function check_updates() {
$our_version = self::$version;
$db_version = get_site_option('mpsum_log_table_version', '0');
if (version_compare($our_version, $db_version, '>')) {
foreach (self::$db_updates as $version => $updates) {
if (version_compare($version, $db_version, '>')) {
foreach ($updates as $update) {
call_user_func(array(__CLASS__, $update));
}
}
}
MPSUM_Updates_Manager::update_option('mpsum_log_table_version', $our_version);
}
}
/**
* Add webhook i18n
*
* @param array $i18n Array of internationalized strings
* @return array Updated array of internationalized strings
*/
public function logs_i18n($i18n) {
$i18n['logs_no_items'] = __('No items found.', 'stops-core-theme-and-plugin-updates');
return $i18n;
}
/**
* Set update method as automatic
*/
public function pre_auto_update() {
$this->auto_update = true;
}
/**
* Finds and returns name of give update object
*
* @param object $translation Translation object
*
* @return array An array of name and version of updates
*/
public function get_update_name($translation) {
$translation = (object) $translation;
$type = $translation->type;
$result = array();
switch ($type) {
case 'core':
$result[$type]['name'] = 'WordPress';
break;
case 'theme':
$theme = wp_get_theme($translation->slug);
if ($theme->exists())
$result[$type]['name'] = $theme->get('Name') . ' (' . $translation->language . ')';
$result[$type]['new_version'] = $theme->get('Version');
break;
case 'plugin':
if (! function_exists('get_plugins')) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
$plugin = get_plugins('/' . $translation->slug);
$plugin = reset($plugin);
if ($plugin)
$result[$type]['name'] = $plugin['Name'] . ' (' . $translation->language . ')';
$result[$type]['new_version'] = $plugin['Version'];
break;
}
$result[$type]['version'] = $translation->version;
return $result;
}
/**
* Inserts result of upgrade process message to log table
*
* @param string $name Upgrade name
* @param string $type Type of upgrade
* @param string $version_from Upgrade from version number
* @param string $version Upgrade to version number
* @param string $action Action type, manual or automatic
* @param int $status Status of upgrade
* @param int $user_id User responsible for the upgrade
*/
public function insert_log($name, $type, $version_from, $version, $action, $status, $user_id = 0, $notes = '', $stacktrace = '' ) {
global $wpdb;
$table_name = $wpdb->base_prefix . 'eum_logs';
if ('' == $version_from) $version_from = '0.00';
$notes = str_replace('&#8230;', '', $notes);
// Strip URLs from notes
$notes = preg_replace('/\?.*/', '', $notes);
$wpdb->insert( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Direct database query is required for inserting log data.
$table_name,
array(
'user_id' => $user_id,
'name' => $name,
'type' => $type,
'version_from' => $version_from,
'version' => $version,
'action' => $action,
'status' => $status,
'date' => current_time('mysql'),
'notes' => $notes,
'stacktrace' => $stacktrace,
),
array(
'%d',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
)
);
}
/**
* Get the name of an translation item being updated.
*
* @since 6.0.3
* @access private
* @param string $type type of translation update
* @param string $slug Slug of item
* @return string The name of the item being updated.
*/
public function get_name_for_update($type, $slug) {
if (! function_exists('get_plugins')) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
switch ($type) {
case 'core':
return 'WordPress'; // Not translated
case 'theme':
$theme = wp_get_theme($slug);
if ($theme->exists())
return $theme->Get('Name');
break;
case 'plugin':
$plugin_data = get_plugins('/' . $slug);
$plugin_data = reset($plugin_data);
if ($plugin_data)
return $plugin_data['Name'];
break;
}
return '';
}
/**
* Creates the log table
*
* Creates the log table
*
* @since 6.0.0
* @access public
*/
public static function build_table() {
global $wpdb;
$tablename = $wpdb->base_prefix . 'eum_logs';
// Get collation - From /wp-admin/includes/schema.php
$charset_collate = '';
if (! empty($wpdb->charset))
$charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
if (! empty($wpdb->collate))
$charset_collate .= " COLLATE $wpdb->collate";
$sql = "CREATE TABLE {$tablename} (
log_id BIGINT(20) NOT NULL AUTO_INCREMENT,
user_id BIGINT(20) NOT NULL DEFAULT 0,
name VARCHAR(255) NOT NULL,
type VARCHAR(255) NOT NULL,
version_from VARCHAR(255) NOT NULL,
version VARCHAR(255) NOT NULL,
action VARCHAR(255) NOT NULL,
status VARCHAR(255) NOT NULL,
notes TEXT NOT NULL,
stacktrace TEXT DEFAULT NULL,
date DATETIME NOT NULL,
PRIMARY KEY (log_id)
) {$charset_collate};";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}
/**
* Checks whether log table exists or not
*
* @return boolean True if table exists, otherwise false
*/
private function log_table_exists() {
if (true === self::$log_table_exists) {
return;
}
global $wpdb;
$table_name = $wpdb->prefix.'eum_logs';
self::$log_table_exists = (bool) $wpdb->get_var("SHOW TABLES LIKE '$table_name'"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Query is safe as it does not involve user input and is used for checking table existence.
return self::$log_table_exists;
}
/**
* Clears the log table
*
* Clears the log table
*
* @since 6.0.0
* @access static
*/
public static function clear() {
global $wpdb;
$tablename = $wpdb->base_prefix . 'eum_logs';
$sql = "delete from $tablename";
$wpdb->query($sql); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Query is safe as it does not include user input and is intended for direct execution.
}
/**
* Drops the log table
*
* Drops the log table
*
* @since 6.0.0
* @access static
*/
public static function drop() {
global $wpdb;
$tablename = $wpdb->base_prefix . 'eum_logs';
$sql = "drop table if exists $tablename";
$wpdb->query($sql); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Query is safe as it does not involve user input and is intended for direct execution.
delete_site_option('mpsum_log_table_version');
}
/**
* Normalise call stacks by clearing out unnecessary objects from their arguments list, leaving the arguments as a string if they're not in an array/object type. The call stacks should be one that is generated by debug_backtrace() function.
*
* @param array $backtrace The output of the debug_backtrace() function
* @return array An array of associative arrays after being normalised
*/
public function normalise_call_stack_args($backtrace) {
$normalised_backtrace = $backtrace;
foreach ((array) $backtrace as $index => $element) {
$normalised_backtrace[$index]['args'] = array();
foreach ($backtrace[$index]['args'] as $arg_index => $arg) {
if (is_object($arg)) {
$normalised_backtrace[$index]['args'][$arg_index] = "Object(".get_class($backtrace[$index]['args'][$arg_index]).")";
} elseif (is_array($arg)) {
$normalised_backtrace[$index]['args'][$arg_index] = "Array(".count($backtrace[$index]['args'][$arg_index]).")";
} elseif (!is_string($backtrace[$index]['args'][$arg_index])) {
$normalised_backtrace[$index]['args'][$arg_index] = '';
} else {
$normalised_backtrace[$index]['args'][$arg_index] = $backtrace[$index]['args'][$arg_index];
}
}
}
return $normalised_backtrace;
}
}