feat: WC-native logging + Add to phpList button on order page
Logging: - Replace custom file logger with wc_get_logger() (source: woolist-phplist) - Logs now appear in WooCommerce → Status → Logs, no filesystem access needed - Remove log viewer / Clear Log from settings page (WC UI handles this) - Keep "Enable debug logging" checkbox to control DEBUG-level verbosity Order page meta box: - New "phpList Sync" side meta box on every order edit page - Works with both classic (shop_order) and HPOS (woocommerce_page_wc-orders) - Shows billing email + dropdown of all configured lists - "Add to phpList" button triggers AJAX subscribe, shows inline result - Result and full API trace logged to WC logs under woolist-phplist source - woolist-admin.js handles button state and response display Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,14 +1,13 @@
|
||||
<?php
|
||||
/**
|
||||
* Simple file-based logger for WooList.
|
||||
* Logger for WooList — delegates to WooCommerce's built-in logging system.
|
||||
*
|
||||
* Writes to wp-content/uploads/woolist-logs/woolist.log.
|
||||
* The directory is protected from direct HTTP access via .htaccess.
|
||||
* Logs are visible at WooCommerce → Status → Logs, source: woolist-phplist.
|
||||
*
|
||||
* Levels:
|
||||
* INFO — always written (subscription events, connection test results)
|
||||
* ERROR — always written (API failures, config problems)
|
||||
* DEBUG — written only when "Enable debug logging" is on (full request/response)
|
||||
* DEBUG — written only when "Enable debug logging" is checked in settings
|
||||
*
|
||||
* @package WooList
|
||||
*/
|
||||
@@ -17,38 +16,19 @@ defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class WooList_Logger {
|
||||
|
||||
/** Absolute path to the log file. */
|
||||
private static string $log_file = '';
|
||||
private const SOURCE = 'woolist-phplist';
|
||||
|
||||
/** Whether verbose debug lines are recorded. */
|
||||
private static bool $debug_enabled = false;
|
||||
|
||||
/** Maximum log file size in bytes before rotation (1 MB). */
|
||||
private const MAX_SIZE = 1048576;
|
||||
/** Lazy-loaded WC_Logger instance. */
|
||||
private static ?WC_Logger_Interface $wc_logger = null;
|
||||
|
||||
/**
|
||||
* Initialise the logger: create the log directory and read the debug setting.
|
||||
* Must be called after WordPress options are available.
|
||||
* Initialise: read the debug setting.
|
||||
* Called on plugins_loaded after options are available.
|
||||
*/
|
||||
public static function init(): void {
|
||||
$upload = wp_upload_dir();
|
||||
$log_dir = $upload['basedir'] . '/woolist-logs';
|
||||
|
||||
if ( ! is_dir( $log_dir ) ) {
|
||||
wp_mkdir_p( $log_dir );
|
||||
}
|
||||
|
||||
// Block direct HTTP access and directory listing.
|
||||
$htaccess = $log_dir . '/.htaccess';
|
||||
if ( ! file_exists( $htaccess ) ) {
|
||||
file_put_contents( $htaccess, "Require all denied\ndeny from all\n" );
|
||||
}
|
||||
$index = $log_dir . '/index.php';
|
||||
if ( ! file_exists( $index ) ) {
|
||||
file_put_contents( $index, "<?php // Silence is golden.\n" );
|
||||
}
|
||||
|
||||
self::$log_file = $log_dir . '/woolist.log';
|
||||
self::$debug_enabled = ( get_option( 'woolist_enable_debug_log' ) === 'yes' );
|
||||
}
|
||||
|
||||
@@ -56,22 +36,21 @@ class WooList_Logger {
|
||||
|
||||
/** Always logged. Use for normal subscription / connection events. */
|
||||
public static function info( string $message ): void {
|
||||
self::write( 'INFO', $message );
|
||||
self::wc()->info( $message, [ 'source' => self::SOURCE ] );
|
||||
}
|
||||
|
||||
/** Always logged. Also echoes to php error_log as a fallback. */
|
||||
/** Always logged. */
|
||||
public static function error( string $message ): void {
|
||||
self::write( 'ERROR', $message );
|
||||
error_log( '[WooList ERROR] ' . $message );
|
||||
self::wc()->error( $message, [ 'source' => self::SOURCE ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Logged only when debug mode is enabled.
|
||||
* Use for full request URLs, raw response bodies, step-by-step flow.
|
||||
* Use for full request URLs (password redacted), raw responses, flow steps.
|
||||
*/
|
||||
public static function debug( string $message ): void {
|
||||
if ( self::$debug_enabled ) {
|
||||
self::write( 'DEBUG', $message );
|
||||
self::wc()->debug( $message, [ 'source' => self::SOURCE ] );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,55 +66,17 @@ class WooList_Logger {
|
||||
return preg_replace( '/(\bpassword=)[^&]+/', '$1***', $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last $lines lines of the log as a string.
|
||||
*
|
||||
* @param int $lines Number of lines to return.
|
||||
* @return string Log tail, or an empty string when the log doesn't exist yet.
|
||||
*/
|
||||
public static function read_recent( int $lines = 300 ): string {
|
||||
if ( empty( self::$log_file ) || ! file_exists( self::$log_file ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$content = file_get_contents( self::$log_file );
|
||||
if ( $content === false || $content === '' ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$all = explode( "\n", rtrim( $content ) );
|
||||
$recent = array_slice( $all, -$lines );
|
||||
return implode( "\n", $recent );
|
||||
}
|
||||
|
||||
/** Truncate the log file without deleting it. */
|
||||
public static function clear(): void {
|
||||
if ( ! empty( self::$log_file ) ) {
|
||||
file_put_contents( self::$log_file, '' );
|
||||
}
|
||||
}
|
||||
|
||||
public static function get_log_path(): string {
|
||||
return self::$log_file;
|
||||
}
|
||||
|
||||
public static function is_debug_enabled(): bool {
|
||||
return self::$debug_enabled;
|
||||
}
|
||||
|
||||
// ── Internal ─────────────────────────────────────────────────────────────
|
||||
|
||||
private static function write( string $level, string $message ): void {
|
||||
if ( empty( self::$log_file ) ) {
|
||||
return;
|
||||
/** Return (and lazy-load) the WC_Logger instance. */
|
||||
private static function wc(): WC_Logger_Interface {
|
||||
if ( self::$wc_logger === null ) {
|
||||
self::$wc_logger = wc_get_logger();
|
||||
}
|
||||
|
||||
// Rotate if the file exceeds MAX_SIZE.
|
||||
if ( file_exists( self::$log_file ) && filesize( self::$log_file ) >= self::MAX_SIZE ) {
|
||||
rename( self::$log_file, self::$log_file . '.old' );
|
||||
}
|
||||
|
||||
$line = '[' . gmdate( 'Y-m-d H:i:s' ) . ' UTC] [' . $level . '] ' . $message . "\n";
|
||||
file_put_contents( self::$log_file, $line, FILE_APPEND | LOCK_EX );
|
||||
return self::$wc_logger;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user