# charitable/1.8.13/includes/logger/class-charitable-log.php

Charitable – Donation &amp; Fundraising Platform (Donation Forms, Recurring Donations &amp; Fundraising Campaigns), version 1.8.13. 498 lines.

- Page: https://pluginprobe.com/plugins/charitable/1.8.13/code/includes/logger/class-charitable-log.php
- Raw: https://pluginprobe.com/plugins/charitable/1.8.13/raw/includes/logger/class-charitable-log.php
- Modified: 2026-05-21T18:12:06+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/charitable/1.8.13/code/includes/logger/class-charitable-log.php#L10-L20`.

```php
<?php
/**
 * Charitable Log orchestrator class.
 *
 * Main singleton that manages the logging system.
 *
 * @package   Charitable/Classes/Charitable_Log
 * @author    David Bisset
 * @copyright Copyright (c) 2023, WP Charitable LLC
 * @license   http://opensource.org/licenses/gpl-2.0.php GNU Public License
 * @since     1.8.11
 */

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

if ( ! class_exists( 'Charitable_Log' ) ) :

	/**
	 * Charitable_Log
	 *
	 * @since 1.8.11
	 */
	class Charitable_Log {

		/**
		 * The single instance of this class.
		 *
		 * @since 1.8.11
		 *
		 * @var Charitable_Log|null
		 */
		private static $instance = null;

		/**
		 * The database instance.
		 *
		 * @since 1.8.11
		 *
		 * @var Charitable_Log_DB
		 */
		private $db;

		/**
		 * The query instance.
		 *
		 * @since 1.8.11
		 *
		 * @var Charitable_Log_Query
		 */
		private $query;

		/**
		 * Queue of records to be saved on shutdown.
		 *
		 * @since 1.8.11
		 *
		 * @var array
		 */
		private $queue = array();

		/**
		 * Whether the shutdown hook has been registered.
		 *
		 * @since 1.8.11
		 *
		 * @var bool
		 */
		private $shutdown_registered = false;

		/**
		 * Create object instance.
		 *
		 * @since 1.8.11
		 */
		private function __construct() {
		}

		/**
		 * Returns and/or create the single instance of this class.
		 *
		 * @since 1.8.11
		 *
		 * @return Charitable_Log
		 */
		public static function get_instance() {
			if ( is_null( self::$instance ) ) {
				self::$instance = new self();
			}

			return self::$instance;
		}

		/**
		 * Get registered log types.
		 *
		 * @since 1.8.11
		 *
		 * @return array Associative array of type_slug => label.
		 */
		public static function get_log_types() {
			$types = array(
				'donation'      => __( 'Donation', 'charitable' ),
				'payment'       => __( 'Payment', 'charitable' ),
				'gateway'       => __( 'Gateway', 'charitable' ),
				'error'         => __( 'Error', 'charitable' ),
				'log'           => __( 'Log', 'charitable' ),
				'security'      => __( 'Security', 'charitable' ),
				'spam'          => __( 'Spam', 'charitable' ),
				'recurring'     => __( 'Recurring', 'charitable' ),
				'email'         => __( 'Email', 'charitable' ),
				'addon'         => __( 'Addon', 'charitable' ),
				'webhook'       => __( 'Webhook', 'charitable' ),
				'exchange_rate' => __( 'Exchange Rate', 'charitable' ),
				'form'          => __( 'Form', 'charitable' ),
				'migration'     => __( 'Migration', 'charitable' ),
				'notification'  => __( 'Notification', 'charitable' ),
				'newsletter'    => __( 'Newsletter', 'charitable' ),
			);

			/**
			 * Filter the registered log types.
			 *
			 * @since 1.8.11
			 *
			 * @param array $types Associative array of type_slug => label.
			 */
			return apply_filters( 'charitable_log_types', $types );
		}

		/**
		 * Get registered log levels.
		 *
		 * @since 1.8.11
		 *
		 * @return array Associative array of level_slug => label.
		 */
		public static function get_log_levels() {
			return array(
				'error'   => __( 'Error', 'charitable' ),
				'warning' => __( 'Warning', 'charitable' ),
				'info'    => __( 'Info', 'charitable' ),
				'debug'   => __( 'Debug', 'charitable' ),
			);
		}

		/**
		 * Get registered log sources.
		 *
		 * @since 1.8.11
		 *
		 * @return array Associative array of source_slug => label.
		 */
		public static function get_sources() {
			$sources = array(
				'core'       => __( 'Core', 'charitable' ),
				'stripe'     => __( 'Stripe', 'charitable' ),
				'paypal'     => __( 'PayPal', 'charitable' ),
				'square'     => __( 'Square', 'charitable' ),
				'braintree'  => __( 'Braintree', 'charitable' ),
				'mollie'     => __( 'Mollie', 'charitable' ),
				'authorize'  => __( 'Authorize.net', 'charitable' ),
				'windcave'   => __( 'Windcave', 'charitable' ),
				'recurring'  => __( 'Recurring', 'charitable' ),
				'email'      => __( 'Email', 'charitable' ),
				'spam'       => __( 'Spam Blocker', 'charitable' ),
				'form'       => __( 'Form', 'charitable' ),
			);

			/**
			 * Filter the registered log sources.
			 *
			 * @since 1.8.11
			 *
			 * @param array $sources Associative array of source_slug => label.
			 */
			return apply_filters( 'charitable_log_sources', $sources );
		}

		/**
		 * Get the database instance.
		 *
		 * @since 1.8.11
		 *
		 * @return Charitable_Log_DB
		 */
		public function get_db() {
			if ( ! isset( $this->db ) ) {
				$this->db = new Charitable_Log_DB();
			}

			return $this->db;
		}

		/**
		 * Get the query instance.
		 *
		 * @since 1.8.11
		 *
		 * @return Charitable_Log_Query
		 */
		public function get_query() {
			if ( ! isset( $this->query ) ) {
				$this->query = new Charitable_Log_Query( $this->get_db() );
			}

			return $this->query;
		}

		/**
		 * Add a record to the queue.
		 *
		 * @since 1.8.11
		 *
		 * @param array $record Record data array from Charitable_Log_Record::create().
		 */
		public function add( $record ) {
			$this->queue[] = $record;

			if ( ! $this->shutdown_registered ) {
				add_action( 'shutdown', array( $this, 'save_queued_records' ) );
				$this->shutdown_registered = true;
			}
		}

		/**
		 * Save all queued records to the database.
		 *
		 * @since 1.8.11
		 */
		public function save_queued_records() {
			if ( empty( $this->queue ) ) {
				return;
			}

			$this->ensure_table_exists();

			$db = $this->get_db();

			if ( count( $this->queue ) === 1 ) {
				$db->insert( $this->queue[0] );
			} else {
				$db->insert_batch( $this->queue );
			}

			$this->queue = array();
		}

		/**
		 * Ensure the logs table exists (with transient-cached check).
		 *
		 * @since 1.8.11
		 */
		public function ensure_table_exists() {
			$transient_key = 'charitable_logs_table_exists';

			if ( get_transient( $transient_key ) ) {
				return;
			}

			$db = $this->get_db();

			if ( ! $db->table_exists() ) {
				$db->create_table();
			}

			set_transient( $transient_key, true, DAY_IN_SECONDS );
		}

		/**
		 * Check if logging is enabled.
		 *
		 * @since 1.8.11
		 *
		 * @return bool
		 */
		public static function is_enabled() {
			$tools = get_option( 'charitable_tools', array() );

			// Default to enabled.
			if ( ! isset( $tools['logs_enable'] ) ) {
				return true;
			}

			return (bool) $tools['logs_enable'];
		}

		/**
		 * Get the retention days setting.
		 *
		 * @since 1.8.11
		 *
		 * @return int
		 */
		public static function get_retention_days() {
			$tools   = get_option( 'charitable_tools', array() );
			$default = 30;

			return isset( $tools['logs_retention_days'] ) ? absint( $tools['logs_retention_days'] ) : $default;
		}

		/**
		 * AJAX handler: Get a single log record for the modal.
		 *
		 * @since 1.8.11
		 */
		public static function ajax_get_record() {
			check_ajax_referer( 'charitable_logs_nonce', 'nonce' );

			if ( ! current_user_can( 'manage_charitable_settings' ) ) {
				wp_send_json_error( array( 'message' => __( 'Permission denied.', 'charitable' ) ) );
				return;
			}

			$id     = isset( $_POST['record_id'] ) ? absint( $_POST['record_id'] ) : 0;
			$record = self::get_instance()->get_query()->get_by_id( $id );

			if ( ! $record ) {
				wp_send_json_error( array( 'message' => __( 'Record not found.', 'charitable' ) ) );
				return;
			}

			$all_types  = self::get_log_types();
			$all_levels = self::get_log_levels();
			$all_sources = self::get_sources();

			$type_labels = array();
			foreach ( $record->get_types( 'key' ) as $type_key ) {
				$type_labels[] = isset( $all_types[ $type_key ] ) ? $all_types[ $type_key ] : $type_key;
			}

			wp_send_json_success( array(
				'id'          => $record->id,
				'title'       => $record->title,
				'message'     => $record->message,
				'types'       => implode( ', ', $type_labels ),
				'level'       => isset( $all_levels[ $record->level ] ) ? $all_levels[ $record->level ] : $record->level,
				'level_key'   => $record->level,
				'source'      => isset( $all_sources[ $record->source ] ) ? $all_sources[ $record->source ] : $record->source,
				'date'        => $record->get_date( 'full' ),
				'campaign_id' => $record->campaign_id,
				'donation_id' => $record->donation_id,
				'donor_id'    => $record->donor_id,
				'user_id'     => $record->user_id,
				'object_id'   => $record->object_id,
				'object_type' => $record->object_type,
			) );
		}

		/**
		 * AJAX handler: Delete all log records.
		 *
		 * @since 1.8.11
		 */
		public static function ajax_delete_all_logs() {
			check_ajax_referer( 'charitable_logs_nonce', 'nonce' );

			if ( ! current_user_can( 'manage_charitable_settings' ) ) {
				wp_send_json_error( array( 'message' => __( 'Permission denied.', 'charitable' ) ) );
				return;
			}

			$db = self::get_instance()->get_db();
			$db->truncate_table();

			wp_send_json_success( array( 'message' => __( 'All logs have been deleted.', 'charitable' ) ) );
		}

		/**
		 * AJAX handler: Export logs as CSV.
		 *
		 * @since 1.8.11
		 */
		public static function ajax_export_csv() {
			check_ajax_referer( 'charitable_logs_nonce', 'nonce' );

			if ( ! current_user_can( 'manage_charitable_settings' ) ) {
				wp_die( esc_html__( 'Permission denied.', 'charitable' ) );
			}

			$export = new Charitable_Log_Export();
			$export->run();
		}

		/**
		 * AJAX handler: Toggle logging on/off.
		 *
		 * @since 1.8.11
		 */
		public static function ajax_toggle_logging() {
			check_ajax_referer( 'charitable_logs_nonce', 'nonce' );

			if ( ! current_user_can( 'manage_charitable_settings' ) ) {
				wp_send_json_error( array( 'message' => __( 'Permission denied.', 'charitable' ) ) );
				return;
			}

			$enabled = isset( $_POST['enabled'] ) ? absint( $_POST['enabled'] ) : 0;
			$tools   = get_option( 'charitable_tools', array() );

			if ( ! is_array( $tools ) ) {
				$tools = array();
			}

			$tools['logs_enable'] = $enabled;
			update_option( 'charitable_tools', $tools );

			wp_send_json_success( array(
				'message' => $enabled
					? __( 'Logging enabled.', 'charitable' )
					: __( 'Logging disabled.', 'charitable' ),
			) );
		}

		/**
		 * AJAX handler: Save log retention days.
		 *
		 * @since 1.8.11
		 */
		public static function ajax_save_retention() {
			check_ajax_referer( 'charitable_logs_nonce', 'nonce' );

			if ( ! current_user_can( 'manage_charitable_settings' ) ) {
				wp_send_json_error( array( 'message' => __( 'Permission denied.', 'charitable' ) ) );
				return;
			}

			$days  = isset( $_POST['days'] ) ? absint( $_POST['days'] ) : 30;
			$tools = get_option( 'charitable_tools', array() );

			if ( ! is_array( $tools ) ) {
				$tools = array();
			}

			$tools['logs_retention_days'] = $days;
			update_option( 'charitable_tools', $tools );

			wp_send_json_success( array(
				'message' => sprintf(
					/* translators: %d: number of days */
					__( 'Retention set to %d days.', 'charitable' ),
					$days
				),
			) );
		}

		/**
		 * Enqueue scripts for the logs admin page.
		 *
		 * @since 1.8.11
		 */
		public static function enqueue_scripts() {
			if ( ! charitable_is_tools_view() || ! isset( $_GET['tab'] ) || 'logs' !== $_GET['tab'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
				return;
			}

			$version = charitable()->get_version();
			$suffix  = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';

			wp_enqueue_script(
				'charitable-logger',
				charitable()->get_path( 'assets', false ) . 'js/admin/charitable-logger.js',
				array( 'jquery', 'wp-util' ),
				$version,
				true
			);

			wp_localize_script(
				'charitable-logger',
				'charitableLogger',
				array(
					'ajaxUrl' => admin_url( 'admin-ajax.php' ),
					'nonce'   => wp_create_nonce( 'charitable_logs_nonce' ),
					'i18n'    => array(
						'deleteConfirm' => __( 'Are you sure you want to delete ALL log records? This cannot be undone.', 'charitable' ),
						'deleting'      => __( 'Deleting...', 'charitable' ),
						'deleted'       => __( 'All logs deleted.', 'charitable' ),
						'saving'        => __( 'Saving...', 'charitable' ),
						'saved'         => __( 'Saved.', 'charitable' ),
						'error'         => __( 'An error occurred.', 'charitable' ),
						'close'         => __( 'Close', 'charitable' ),
					),
				)
			);

			wp_enqueue_style(
				'charitable-logger',
				charitable()->get_path( 'assets', false ) . 'css/admin/charitable-logger.css',
				array(),
				$version
			);
		}
	}

endif;

```
