feat: WooCommerce Business Central integration plugin
Native PHP plugin (no Composer) that syncs: - Product stock and pricing from BC to WooCommerce (scheduled cron) - Orders from WooCommerce to BC (on payment received) - Auto-creates customers in BC from WooCommerce billing data Product matching: WooCommerce SKU → BC Item Number, fallback to GTIN (EAN). OAuth2 client credentials auth with encrypted secret storage. Admin settings page with connection test, manual sync, and log viewer. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
325
woo-business-central/includes/class-wbc-logger.php
Normal file
325
woo-business-central/includes/class-wbc-logger.php
Normal file
@@ -0,0 +1,325 @@
|
||||
<?php
|
||||
/**
|
||||
* Logger class for WooCommerce Business Central Integration
|
||||
*
|
||||
* @package WooBusinessCentral
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class WBC_Logger
|
||||
*
|
||||
* Handles logging to database with different severity levels.
|
||||
*/
|
||||
class WBC_Logger {
|
||||
|
||||
/**
|
||||
* Log level constants
|
||||
*/
|
||||
const DEBUG = 'DEBUG';
|
||||
const INFO = 'INFO';
|
||||
const WARNING = 'WARNING';
|
||||
const ERROR = 'ERROR';
|
||||
|
||||
/**
|
||||
* Database table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private static $table_name = null;
|
||||
|
||||
/**
|
||||
* Create the logs database table
|
||||
*/
|
||||
public static function create_table() {
|
||||
global $wpdb;
|
||||
|
||||
$table_name = $wpdb->prefix . 'wbc_logs';
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
|
||||
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
timestamp datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
level varchar(20) NOT NULL,
|
||||
context varchar(100) NOT NULL,
|
||||
message text NOT NULL,
|
||||
data longtext,
|
||||
PRIMARY KEY (id),
|
||||
KEY level (level),
|
||||
KEY context (context),
|
||||
KEY timestamp (timestamp)
|
||||
) $charset_collate;";
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
||||
dbDelta( $sql );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the table name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function get_table_name() {
|
||||
if ( self::$table_name === null ) {
|
||||
global $wpdb;
|
||||
self::$table_name = $wpdb->prefix . 'wbc_logs';
|
||||
}
|
||||
return self::$table_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a debug message
|
||||
*
|
||||
* @param string $context The context/source of the log.
|
||||
* @param string $message The log message.
|
||||
* @param mixed $data Optional. Additional data to log.
|
||||
*/
|
||||
public static function debug( $context, $message, $data = null ) {
|
||||
self::log( self::DEBUG, $context, $message, $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an info message
|
||||
*
|
||||
* @param string $context The context/source of the log.
|
||||
* @param string $message The log message.
|
||||
* @param mixed $data Optional. Additional data to log.
|
||||
*/
|
||||
public static function info( $context, $message, $data = null ) {
|
||||
self::log( self::INFO, $context, $message, $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a warning message
|
||||
*
|
||||
* @param string $context The context/source of the log.
|
||||
* @param string $message The log message.
|
||||
* @param mixed $data Optional. Additional data to log.
|
||||
*/
|
||||
public static function warning( $context, $message, $data = null ) {
|
||||
self::log( self::WARNING, $context, $message, $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an error message
|
||||
*
|
||||
* @param string $context The context/source of the log.
|
||||
* @param string $message The log message.
|
||||
* @param mixed $data Optional. Additional data to log.
|
||||
*/
|
||||
public static function error( $context, $message, $data = null ) {
|
||||
self::log( self::ERROR, $context, $message, $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message to the database
|
||||
*
|
||||
* @param string $level The log level.
|
||||
* @param string $context The context/source of the log.
|
||||
* @param string $message The log message.
|
||||
* @param mixed $data Optional. Additional data to log.
|
||||
*/
|
||||
private static function log( $level, $context, $message, $data = null ) {
|
||||
global $wpdb;
|
||||
|
||||
$table_name = self::get_table_name();
|
||||
|
||||
// Prepare data for storage
|
||||
$data_json = null;
|
||||
if ( $data !== null ) {
|
||||
$data_json = wp_json_encode( $data, JSON_PRETTY_PRINT );
|
||||
}
|
||||
|
||||
// Insert log entry
|
||||
$wpdb->insert(
|
||||
$table_name,
|
||||
array(
|
||||
'timestamp' => current_time( 'mysql' ),
|
||||
'level' => $level,
|
||||
'context' => sanitize_text_field( $context ),
|
||||
'message' => sanitize_textarea_field( $message ),
|
||||
'data' => $data_json,
|
||||
),
|
||||
array( '%s', '%s', '%s', '%s', '%s' )
|
||||
);
|
||||
|
||||
// Also log to WooCommerce logger if available
|
||||
if ( function_exists( 'wc_get_logger' ) ) {
|
||||
$wc_logger = wc_get_logger();
|
||||
$log_level = strtolower( $level );
|
||||
if ( method_exists( $wc_logger, $log_level ) ) {
|
||||
$wc_message = "[$context] $message";
|
||||
if ( $data !== null ) {
|
||||
$wc_message .= ' | Data: ' . wp_json_encode( $data );
|
||||
}
|
||||
$wc_logger->$log_level( $wc_message, array( 'source' => 'woo-business-central' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get log entries
|
||||
*
|
||||
* @param array $args Query arguments.
|
||||
* @return array Array of log entries.
|
||||
*/
|
||||
public static function get_logs( $args = array() ) {
|
||||
global $wpdb;
|
||||
|
||||
$defaults = array(
|
||||
'level' => '',
|
||||
'context' => '',
|
||||
'limit' => 100,
|
||||
'offset' => 0,
|
||||
'orderby' => 'timestamp',
|
||||
'order' => 'DESC',
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
$table_name = self::get_table_name();
|
||||
|
||||
// Build WHERE clause
|
||||
$where = array( '1=1' );
|
||||
$values = array();
|
||||
|
||||
if ( ! empty( $args['level'] ) ) {
|
||||
$where[] = 'level = %s';
|
||||
$values[] = $args['level'];
|
||||
}
|
||||
|
||||
if ( ! empty( $args['context'] ) ) {
|
||||
$where[] = 'context = %s';
|
||||
$values[] = $args['context'];
|
||||
}
|
||||
|
||||
$where_clause = implode( ' AND ', $where );
|
||||
|
||||
// Sanitize orderby and order
|
||||
$allowed_orderby = array( 'id', 'timestamp', 'level', 'context' );
|
||||
$orderby = in_array( $args['orderby'], $allowed_orderby, true ) ? $args['orderby'] : 'timestamp';
|
||||
$order = strtoupper( $args['order'] ) === 'ASC' ? 'ASC' : 'DESC';
|
||||
|
||||
// Build query
|
||||
$limit = absint( $args['limit'] );
|
||||
$offset = absint( $args['offset'] );
|
||||
|
||||
$sql = "SELECT * FROM $table_name WHERE $where_clause ORDER BY $orderby $order LIMIT $limit OFFSET $offset";
|
||||
|
||||
if ( ! empty( $values ) ) {
|
||||
$sql = $wpdb->prepare( $sql, $values );
|
||||
}
|
||||
|
||||
return $wpdb->get_results( $sql, ARRAY_A );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total log count
|
||||
*
|
||||
* @param array $args Query arguments.
|
||||
* @return int Total count.
|
||||
*/
|
||||
public static function get_log_count( $args = array() ) {
|
||||
global $wpdb;
|
||||
|
||||
$table_name = self::get_table_name();
|
||||
|
||||
// Build WHERE clause
|
||||
$where = array( '1=1' );
|
||||
$values = array();
|
||||
|
||||
if ( ! empty( $args['level'] ) ) {
|
||||
$where[] = 'level = %s';
|
||||
$values[] = $args['level'];
|
||||
}
|
||||
|
||||
if ( ! empty( $args['context'] ) ) {
|
||||
$where[] = 'context = %s';
|
||||
$values[] = $args['context'];
|
||||
}
|
||||
|
||||
$where_clause = implode( ' AND ', $where );
|
||||
|
||||
$sql = "SELECT COUNT(*) FROM $table_name WHERE $where_clause";
|
||||
|
||||
if ( ! empty( $values ) ) {
|
||||
$sql = $wpdb->prepare( $sql, $values );
|
||||
}
|
||||
|
||||
return (int) $wpdb->get_var( $sql );
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all logs
|
||||
*
|
||||
* @return bool|int Number of rows deleted or false on error.
|
||||
*/
|
||||
public static function clear_logs() {
|
||||
global $wpdb;
|
||||
$table_name = self::get_table_name();
|
||||
return $wpdb->query( "TRUNCATE TABLE $table_name" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete old logs
|
||||
*
|
||||
* @param int $days Number of days to keep logs.
|
||||
* @return int Number of rows deleted.
|
||||
*/
|
||||
public static function cleanup_old_logs( $days = 30 ) {
|
||||
global $wpdb;
|
||||
|
||||
$table_name = self::get_table_name();
|
||||
$cutoff_date = gmdate( 'Y-m-d H:i:s', strtotime( "-{$days} days" ) );
|
||||
|
||||
return $wpdb->query(
|
||||
$wpdb->prepare(
|
||||
"DELETE FROM $table_name WHERE timestamp < %s",
|
||||
$cutoff_date
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Export logs to CSV format
|
||||
*
|
||||
* @param array $args Query arguments.
|
||||
* @return string CSV content.
|
||||
*/
|
||||
public static function export_to_csv( $args = array() ) {
|
||||
$logs = self::get_logs( array_merge( $args, array( 'limit' => 10000 ) ) );
|
||||
|
||||
$output = "ID,Timestamp,Level,Context,Message,Data\n";
|
||||
|
||||
foreach ( $logs as $log ) {
|
||||
$row = array(
|
||||
$log['id'],
|
||||
$log['timestamp'],
|
||||
$log['level'],
|
||||
self::sanitize_csv_field( $log['context'] ),
|
||||
self::sanitize_csv_field( $log['message'] ),
|
||||
self::sanitize_csv_field( $log['data'] ?? '' ),
|
||||
);
|
||||
$output .= implode( ',', $row ) . "\n";
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize a field value for CSV export to prevent formula injection
|
||||
*
|
||||
* @param string $value The value to sanitize.
|
||||
* @return string Sanitized and quoted CSV value.
|
||||
*/
|
||||
private static function sanitize_csv_field( $value ) {
|
||||
// Prevent formula injection by prefixing dangerous characters with a single quote
|
||||
if ( preg_match( '/^[=+\-@\t\r]/', $value ) ) {
|
||||
$value = "'" . $value;
|
||||
}
|
||||
return '"' . str_replace( '"', '""', $value ) . '"';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user