feat: Update log creation to support schema changes and enhance compatibility

- Updated the `create_log` method to handle the new `time` (DATETIME) and `time_only` (TIME) columns.
- Ensured sanitized input for `license_key`, `action`, and `origin`.
- Improved error handling with detailed success and failure logs.
- Streamlined origin determination for better readability.
- Added example usage and database insertion validation.
This commit is contained in:
Michel 2025-01-05 11:49:59 -05:00
parent bf5734be57
commit 4f0405bf2d
6 changed files with 196 additions and 102 deletions

View file

@ -192,45 +192,84 @@ function slm_add_licenses_menu()

case 'activity': ?>

<?php
// Retrieve the license key for the current record
$license_key = esc_attr($data['license_key']);

// Fetch the log data using a utility function to handle the database query
$log_entries = SLM_Helper_Class::get_license_logs($license_key);

// Display the log table if there are any log entries
if ($log_entries) {
?>
<div class="wrap">
<h2><?php esc_html_e('Activity Log', 'slm-plus'); ?></h2>
<table class="widefat striped">
<thead>
<tr>
<th><?php esc_html_e('ID', 'slm-plus'); ?></th>
<th><?php esc_html_e('Action', 'slm-plus'); ?></th>
<th><?php esc_html_e('Time', 'slm-plus'); ?></th>
<th><?php esc_html_e('Source', 'slm-plus'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($log_entries as $entry): ?>
<tr>
<td><?php echo esc_html($entry['id']); ?></td>
<td><?php echo esc_html($entry['slm_action']); ?></td>
<td><?php echo esc_html($entry['time']); ?></td>
<td><?php echo esc_html($entry['source']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php
global $wpdb;
$table_name = SLM_TBL_LIC_LOG;

// Preserve existing query parameters
$current_url_params = $_GET;
unset($current_url_params['orderby'], $current_url_params['order']); // Remove old sorting params
$current_url = http_build_query($current_url_params); // Build the remaining query string

// Fetch sorting parameters with defaults
$orderby = isset($_GET['orderby']) ? esc_sql($_GET['orderby']) : 'time';
$order = isset($_GET['order']) && in_array(strtoupper($_GET['order']), ['ASC', 'DESC']) ? esc_sql($_GET['order']) : 'DESC';

// Validate $orderby to ensure it's a valid column
$valid_columns = ['id', 'slm_action', 'time', 'time_only', 'source'];
if (!in_array($orderby, $valid_columns)) {
$orderby = 'time'; // Fallback to default
}

// Fetch data with sorting
$query = $wpdb->prepare(
"SELECT * FROM $table_name WHERE license_key = %s ORDER BY $orderby $order LIMIT 10 OFFSET %d",
esc_sql($data['license_key']),
(isset($_GET['paged']) ? intval($_GET['paged'] - 1) * 10 : 0) // Calculate offset for pagination
);
$log_entries = $wpdb->get_results($query, ARRAY_A);
?>

<div class="wrap">
<h2><?php esc_html_e('Activity Log', 'slm-plus'); ?></h2>
<?php if (!empty($log_entries)): ?>
<table class="widefat striped">
<thead>
<tr>
<?php
} else {
// Show a message if there are no log entries
echo '<p>' . esc_html__('No activity log found for this license.', 'slm-plus') . '</p>';
}
// Define all columns to be displayed and sortable
$columns = [
'id' => __('ID', 'slm-plus'),
'slm_action' => __('Action', 'slm-plus'),
'time' => __('Date & Time', 'slm-plus'),
'source' => __('Source', 'slm-plus'),
];

// Render table headers with sorting links
foreach ($columns as $column_key => $column_name):
$next_order = ($orderby === $column_key && $order === 'ASC') ? 'DESC' : 'ASC';
?>
<th>
<a href="?<?php echo $current_url; ?>&orderby=<?php echo esc_attr($column_key); ?>&order=<?php echo esc_attr($next_order); ?>">
<?php echo esc_html($column_name); ?>
<?php if ($orderby === $column_key): ?>
<?php echo $order === 'ASC' ? '&#9650;' : '&#9660;'; ?> <!-- Arrow for sorting -->
<?php endif; ?>
</a>
</th>
<?php endforeach; ?>
</tr>
</thead>
<tbody>
<?php foreach ($log_entries as $entry): ?>
<tr>
<td><?php echo esc_html($entry['id']); ?></td>
<td><?php echo esc_html($entry['slm_action']); ?></td>
<td><?php echo esc_html($entry['time']); ?></td>
<td><?php echo esc_html($entry['source']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else: ?>
<p><?php esc_html_e('No activity log found for this license.', 'slm-plus'); ?></p>
<?php endif; ?>
</div>




<?php
break;

View file

@ -37,6 +37,48 @@ if (version_compare($used_db_version, $new_db_version, '<')) {
// Check if the 'associated_orders' column exists
$column_exists = $wpdb->get_results("SHOW COLUMNS FROM $lic_key_table LIKE 'associated_orders'");

// Ensure the 'time' column is DATETIME
$check_time_column = $wpdb->get_results("SHOW COLUMNS FROM $lic_log_tbl LIKE 'time'");
if (!empty($check_time_column)) {
$time_column_info = $wpdb->get_row("SHOW COLUMNS FROM $lic_log_tbl WHERE Field = 'time'");
if ($time_column_info->Type !== 'datetime') {
error_log("SLM: Updating 'time' column to DATETIME in $lic_log_tbl.");
$update_time_column_query = "
ALTER TABLE $lic_log_tbl
MODIFY COLUMN time DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00';
";
$update_time_column_result = $wpdb->query($update_time_column_query);
if ($update_time_column_result === false) {
error_log("SLM: Error updating 'time' column - " . $wpdb->last_error);
} else {
error_log("SLM: 'time' column updated successfully to DATETIME.");
}
} else {
error_log("SLM: 'time' column is already DATETIME.");
}
} else {
error_log("SLM: 'time' column does not exist in $lic_log_tbl. Skipping update.");
}

// Add the 'time_only' column if it doesn't exist
$check_time_only_column = $wpdb->get_results("SHOW COLUMNS FROM $lic_log_tbl LIKE 'time_only'");
if (empty($check_time_only_column)) {
error_log("SLM: Adding missing column 'time_only' to $lic_log_tbl.");
$add_time_only_column_query = "
ALTER TABLE $lic_log_tbl
ADD COLUMN time_only TIME NOT NULL DEFAULT '00:00:00';
";
$add_time_only_column_result = $wpdb->query($add_time_only_column_query);
if ($add_time_only_column_result === false) {
error_log("SLM: Error adding 'time_only' column - " . $wpdb->last_error);
} else {
error_log("SLM: 'time_only' column added successfully.");
}
} else {
error_log("SLM: Column 'time_only' already exists in $lic_log_tbl.");
}


if (empty($column_exists)) {
error_log("SLM: Adding missing column 'associated_orders' to $lic_key_table.");

@ -200,16 +242,18 @@ $slm_emails_tbl = "CREATE TABLE IF NOT EXISTS " . $lic_emails_table . " (
dbDelta($slm_emails_tbl);

// Create log table if not exists
$log_tbl_sql = "CREATE TABLE IF NOT EXISTS " . $lic_log_tbl . " (
id INT NOT NULL AUTO_INCREMENT,
license_key varchar(255) NOT NULL,
slm_action varchar(255) NOT NULL,
time date NOT NULL DEFAULT '0000-00-00',
source varchar(255) NOT NULL,
PRIMARY KEY (id)
)" . $charset_collate . ";";
$log_tbl_sql = "CREATE TABLE IF NOT EXISTS" . $lic_log_tbl . " (
id INT NOT NULL AUTO_INCREMENT,
license_key VARCHAR(255) NOT NULL,
slm_action VARCHAR(255) NOT NULL,
time DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', -- Store combined date and time
time_only TIME NOT NULL DEFAULT '00:00:00', -- Store time only
source VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
) $charset_collate;";
dbDelta($log_tbl_sql);


// Create devices table if not exists
$ldv_tbl_sql = "CREATE TABLE IF NOT EXISTS " . $lic_devices_table . " (
id INT NOT NULL AUTO_INCREMENT,

View file

@ -140,7 +140,7 @@ function slmplus_init_handler()
function slmplus_plugins_loaded_handler()
{
if (is_admin() && get_option('slm_db_version') != SLM_DB_VERSION) {
require_once(SLM_LIB . 'class-slm-installer.php');
require_once SLM_LIB . 'class-slm-installer.php';
// TODO - Implement DB update logic here
}
}
@ -208,8 +208,8 @@ function wc_log($msg)

// WooCommerce integration
if (SLM_Helper_Class::slm_get_option('slm_woo') == 1 && is_plugin_active('woocommerce/woocommerce.php')) {
require_once(SLM_WOO . 'includes/wc_licenses_class.php');
require_once(SLM_WOO . 'includes/slm-meta-boxes.php');
require_once SLM_WOO . 'includes/wc_licenses_class.php';
require_once SLM_WOO . 'includes/slm-meta-boxes.php';
require_once SLM_WOO . 'includes/register-template.php';
require_once SLM_WOO . 'includes/purchase.php';
require_once SLM_WOO . 'includes/create-license-orders.php';

View file

@ -1182,36 +1182,47 @@ class SLM_Utility
{
global $wpdb;
$slm_log_table = SLM_TBL_LIC_LOG;

// Sanitize inputs
$license_key = sanitize_text_field($license_key);
$action = sanitize_text_field($action);

// Determine the request origin
$origin = '';
if (!empty($_SERVER['HTTP_ORIGIN'])) {
$origin = sanitize_text_field($_SERVER['HTTP_ORIGIN']);
} elseif (!empty($_SERVER['HTTP_REFERER'])) {
$origin = sanitize_text_field($_SERVER['HTTP_REFERER']);
} else {
} elseif (!empty($_SERVER['REMOTE_ADDR'])) {
$origin = sanitize_text_field($_SERVER['REMOTE_ADDR']);
}

// Get current date and time
$current_date_time = current_time('mysql'); // Returns 'Y-m-d H:i:s' in WordPress timezone
$current_time_only = date('H:i:s', strtotime($current_date_time)); // Extract time portion
// Prepare log data
$log_data = array(
$log_data = [
'license_key' => $license_key,
'slm_action' => $action,
'time' => current_time('mysql'), // Standardized date-time format
'time' => $current_date_time, // Combined date and time
'time_only' => $current_time_only, // Time only
'source' => $origin,
);

];
// Insert log data into the database
$inserted = $wpdb->insert($slm_log_table, $log_data);

// Check for insertion errors
if ($inserted === false) {
error_log("Failed to insert log for license key: $license_key, action: $action. Error: " . $wpdb->last_error);
} else {
error_log("Log inserted successfully for license key: $license_key, action: $action.");
}
}


public static function create_email_log($lic_key, $sent_to, $status, $sent, $date_sent = null)
@ -1420,3 +1431,4 @@ class SLM_Utility
}
}
}


View file

@ -1,7 +1,7 @@
<?php
/*
Plugin Name: SLM Plus
Version: 6.3.5
Version: 6.3.6
Plugin URI: https://github.com/michelve/software-license-manager/
Author: Michel Velis
Author URI: https://github.com/michelve/
@ -23,8 +23,7 @@ if (!defined('ABSPATH')) {
}

// Load plugin textdomain for multilingual support
function slmplus_load_textdomain()
{
function slmplus_load_textdomain() {
load_plugin_textdomain('slm-plus', false, dirname(plugin_basename(__FILE__)) . '/i18n/languages');
}
add_action('plugins_loaded', 'slmplus_load_textdomain');
@ -33,53 +32,57 @@ add_action('plugins_loaded', 'slmplus_load_textdomain');
global $wpdb, $slm_debug_logger;

// Define constants for plugin paths, URLs, and database tables
define('SLM_VERSION', '6.3.5');
define('SLM_DB_VERSION', '5.8.10');
define('SLM_REWRITE_VERSION', '3.1.3');
define('SLM_FOLDER', dirname(plugin_basename(__FILE__)));
define('SLM_URL', plugins_url('', __FILE__));
define('SLM_ASSETS_URL', SLM_URL . '/public/assets/');
define('SLM_PATH', plugin_dir_path(__FILE__));
define('SLM_LIB', SLM_PATH . 'includes/');
define('SLM_WOO', SLM_PATH . 'woocommerce/');
define('SLM_ADDONS', SLM_PATH . 'addons/');
define('SLM_ADMIN', SLM_PATH . 'admin/');
define('SLM_ADMIN_ADDONS', SLM_ADMIN . 'includes/');
define('SLM_CRONS', SLM_ADMIN_ADDONS . 'cronjobs/');
define('SLM_PUBLIC', SLM_PATH . 'public/');
define('SLM_TEMPLATES', SLM_PATH . 'templates/');
define('SLM_SITE_HOME_URL', get_home_url());
define('SLM_SITE_URL', get_site_url() . '/');
define('SLM_TBL_LICENSE_KEYS', $wpdb->prefix . "lic_key_tbl");
define('SLM_TBL_EMAILS', $wpdb->prefix . "lic_emails_tbl");
define('SLM_TBL_LIC_DOMAIN', $wpdb->prefix . "lic_reg_domain_tbl");
define('SLM_TBL_LIC_DEVICES', $wpdb->prefix . "lic_reg_devices_tbl");
define('SLM_TBL_LIC_LOG', $wpdb->prefix . "lic_log_tbl");
define('SLM_TBL_LICENSE_STATUS', $wpdb->prefix . "lic_status_tbl");
define('SLM_VERSION', '6.3.6');
define('SLM_DB_VERSION', '5.9.1');
define('SLM_REWRITE_VERSION', '3.1.3');

define('SLM_FOLDER', dirname(plugin_basename(__FILE__)));
define('SLM_URL', plugins_url('', __FILE__));
define('SLM_ASSETS_URL', SLM_URL . '/public/assets/');
define('SLM_PATH', plugin_dir_path(__FILE__));
define('SLM_LIB', SLM_PATH . 'includes/');
define('SLM_WOO', SLM_PATH . 'woocommerce/');
define('SLM_ADDONS', SLM_PATH . 'addons/');
define('SLM_ADMIN', SLM_PATH . 'admin/');
define('SLM_ADMIN_ADDONS', SLM_ADMIN . 'includes/');
define('SLM_CRONS', SLM_ADMIN_ADDONS . 'cronjobs/');
define('SLM_PUBLIC', SLM_PATH . 'public/');
define('SLM_TEMPLATES', SLM_PATH . 'templates/');

define('SLM_SITE_HOME_URL', get_home_url());
define('SLM_SITE_URL', get_site_url() . '/');

define('SLM_TBL_LICENSE_KEYS', $wpdb->prefix . "lic_key_tbl");
define('SLM_TBL_EMAILS', $wpdb->prefix . "lic_emails_tbl");
define('SLM_TBL_LIC_DOMAIN', $wpdb->prefix . "lic_reg_domain_tbl");
define('SLM_TBL_LIC_DEVICES', $wpdb->prefix . "lic_reg_devices_tbl");
define('SLM_TBL_LIC_LOG', $wpdb->prefix . "lic_log_tbl");
define('SLM_TBL_LICENSE_STATUS', $wpdb->prefix . "lic_status_tbl");

define('SLM_MANAGEMENT_PERMISSION', 'manage_options');
define('SLM_MAIN_MENU_SLUG', 'slm_overview');
define('SLM_MENU_ICON', 'dashicons-lock');
define('SLM_API_URL', SLM_SITE_URL);
define('SLM_MAIN_MENU_SLUG', 'slm_overview');
define('SLM_MENU_ICON', 'dashicons-lock');
define('SLM_API_URL', SLM_SITE_URL);

// Load core plugin functionalities
if (file_exists(SLM_LIB . 'slm-plugin-core.php')) {
require_once SLM_LIB . 'slm-plugin-core.php';
}

function slm_settings_link($links)
{
// Add settings link to plugin action links
function slm_settings_link($links) {
$settings_link = '<a href="' . esc_url(admin_url('admin.php?page=slm_settings')) . '">' . __('Settings', 'slm-plus') . '</a>';
$links[] = $settings_link;
return $links;
}
add_filter('plugin_action_links_' . plugin_basename(__FILE__), 'slm_settings_link');


// Define default max domains and devices
define('SLM_DEFAULT_MAX_DOMAINS', SLM_API_Utility::get_slm_option('default_max_domains'));
define('SLM_DEFAULT_MAX_DEVICES', SLM_API_Utility::get_slm_option('default_max_devices'));

// Use native WordPress function for setting options
define('WOO_SLM_API_SECRET', SLM_API_Utility::get_slm_option('lic_creation_secret'));
define('KEY_API', SLM_API_Utility::get_slm_option('lic_creation_secret'));
define('VERIFY_KEY_API', SLM_API_Utility::get_slm_option('lic_verification_secret'));
define('KEY_API_PREFIX', SLM_API_Utility::get_slm_option('lic_prefix'));
define('WOO_SLM_API_SECRET', SLM_API_Utility::get_slm_option('lic_creation_secret'));
define('KEY_API', SLM_API_Utility::get_slm_option('lic_creation_secret'));
define('VERIFY_KEY_API', SLM_API_Utility::get_slm_option('lic_verification_secret'));
define('KEY_API_PREFIX', SLM_API_Utility::get_slm_option('lic_prefix'));

View file

@ -50,7 +50,7 @@ foreach ($tables_to_drop as $table) {
// Check if the table exists before attempting to drop it
if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table)) !== null) {
// Drop the table if it exists
$wpdb->query($wpdb->prepare("DROP TABLE IF EXISTS %s", $table));
$wpdb->query("DROP TABLE IF EXISTS {$table}");
}
}

@ -70,20 +70,16 @@ foreach ($post_types as $post_type) {

// Clean orphaned postmeta entries using `DELETE` queries
$wpdb->query(
$wpdb->prepare(
"DELETE pm FROM {$wpdb->postmeta} pm
LEFT JOIN {$wpdb->posts} p ON pm.post_id = p.ID
WHERE p.ID IS NULL"
)
"DELETE pm FROM {$wpdb->postmeta} pm
LEFT JOIN {$wpdb->posts} p ON pm.post_id = p.ID
WHERE p.ID IS NULL"
);

// Clean orphaned term relationships if there are custom taxonomies involved
$wpdb->query(
$wpdb->prepare(
"DELETE tr FROM {$wpdb->term_relationships} tr
LEFT JOIN {$wpdb->posts} p ON tr.object_id = p.ID
WHERE p.ID IS NULL"
)
"DELETE tr FROM {$wpdb->term_relationships} tr
LEFT JOIN {$wpdb->posts} p ON tr.object_id = p.ID
WHERE p.ID IS NULL"
);

// Delete custom user meta related to the plugin (if applicable)