PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.12.2
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.12.2
1.8.13.1 1.8.13 1.8.12.3 1.8.12.2 1.8.12.1 1.8.12 1.8.11.3 1.8.11.2 1.8.11.1 1.8.11 1.6.6 1.6.60 1.6.7 1.6.8 1.6.9 1.7.0 1.7.0.1 1.7.0.11 1.7.0.12 1.7.0.14 1.7.0.2 1.7.0.3 1.7.0.5 1.7.0.6 1.7.0.7 All 212 releases
charitable / includes / logger / class-charitable-log.php

class-charitable-log.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.8.12.2, at includes/logger/class-charitable-log.php

498 lines 12.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Charitable Log orchestrator class.
4 *
5 * Main singleton that manages the logging system.
6 *
7 * @package Charitable/Classes/Charitable_Log
8 * @author David Bisset
9 * @copyright Copyright (c) 2023, WP Charitable LLC
10 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
11 * @since 1.8.11
12 */
13
14 // Exit if accessed directly.
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit;
17 }
18
19 if ( ! class_exists( 'Charitable_Log' ) ) :
20
21 /**
22 * Charitable_Log
23 *
24 * @since 1.8.11
25 */
26 class Charitable_Log {
27
28 /**
29 * The single instance of this class.
30 *
31 * @since 1.8.11
32 *
33 * @var Charitable_Log|null
34 */
35 private static $instance = null;
36
37 /**
38 * The database instance.
39 *
40 * @since 1.8.11
41 *
42 * @var Charitable_Log_DB
43 */
44 private $db;
45
46 /**
47 * The query instance.
48 *
49 * @since 1.8.11
50 *
51 * @var Charitable_Log_Query
52 */
53 private $query;
54
55 /**
56 * Queue of records to be saved on shutdown.
57 *
58 * @since 1.8.11
59 *
60 * @var array
61 */
62 private $queue = array();
63
64 /**
65 * Whether the shutdown hook has been registered.
66 *
67 * @since 1.8.11
68 *
69 * @var bool
70 */
71 private $shutdown_registered = false;
72
73 /**
74 * Create object instance.
75 *
76 * @since 1.8.11
77 */
78 private function __construct() {
79 }
80
81 /**
82 * Returns and/or create the single instance of this class.
83 *
84 * @since 1.8.11
85 *
86 * @return Charitable_Log
87 */
88 public static function get_instance() {
89 if ( is_null( self::$instance ) ) {
90 self::$instance = new self();
91 }
92
93 return self::$instance;
94 }
95
96 /**
97 * Get registered log types.
98 *
99 * @since 1.8.11
100 *
101 * @return array Associative array of type_slug => label.
102 */
103 public static function get_log_types() {
104 $types = array(
105 'donation' => __( 'Donation', 'charitable' ),
106 'payment' => __( 'Payment', 'charitable' ),
107 'gateway' => __( 'Gateway', 'charitable' ),
108 'error' => __( 'Error', 'charitable' ),
109 'log' => __( 'Log', 'charitable' ),
110 'security' => __( 'Security', 'charitable' ),
111 'spam' => __( 'Spam', 'charitable' ),
112 'recurring' => __( 'Recurring', 'charitable' ),
113 'email' => __( 'Email', 'charitable' ),
114 'addon' => __( 'Addon', 'charitable' ),
115 'webhook' => __( 'Webhook', 'charitable' ),
116 'exchange_rate' => __( 'Exchange Rate', 'charitable' ),
117 'form' => __( 'Form', 'charitable' ),
118 'migration' => __( 'Migration', 'charitable' ),
119 'notification' => __( 'Notification', 'charitable' ),
120 'newsletter' => __( 'Newsletter', 'charitable' ),
121 );
122
123 /**
124 * Filter the registered log types.
125 *
126 * @since 1.8.11
127 *
128 * @param array $types Associative array of type_slug => label.
129 */
130 return apply_filters( 'charitable_log_types', $types );
131 }
132
133 /**
134 * Get registered log levels.
135 *
136 * @since 1.8.11
137 *
138 * @return array Associative array of level_slug => label.
139 */
140 public static function get_log_levels() {
141 return array(
142 'error' => __( 'Error', 'charitable' ),
143 'warning' => __( 'Warning', 'charitable' ),
144 'info' => __( 'Info', 'charitable' ),
145 'debug' => __( 'Debug', 'charitable' ),
146 );
147 }
148
149 /**
150 * Get registered log sources.
151 *
152 * @since 1.8.11
153 *
154 * @return array Associative array of source_slug => label.
155 */
156 public static function get_sources() {
157 $sources = array(
158 'core' => __( 'Core', 'charitable' ),
159 'stripe' => __( 'Stripe', 'charitable' ),
160 'paypal' => __( 'PayPal', 'charitable' ),
161 'square' => __( 'Square', 'charitable' ),
162 'braintree' => __( 'Braintree', 'charitable' ),
163 'mollie' => __( 'Mollie', 'charitable' ),
164 'authorize' => __( 'Authorize.net', 'charitable' ),
165 'windcave' => __( 'Windcave', 'charitable' ),
166 'recurring' => __( 'Recurring', 'charitable' ),
167 'email' => __( 'Email', 'charitable' ),
168 'spam' => __( 'Spam Blocker', 'charitable' ),
169 'form' => __( 'Form', 'charitable' ),
170 );
171
172 /**
173 * Filter the registered log sources.
174 *
175 * @since 1.8.11
176 *
177 * @param array $sources Associative array of source_slug => label.
178 */
179 return apply_filters( 'charitable_log_sources', $sources );
180 }
181
182 /**
183 * Get the database instance.
184 *
185 * @since 1.8.11
186 *
187 * @return Charitable_Log_DB
188 */
189 public function get_db() {
190 if ( ! isset( $this->db ) ) {
191 $this->db = new Charitable_Log_DB();
192 }
193
194 return $this->db;
195 }
196
197 /**
198 * Get the query instance.
199 *
200 * @since 1.8.11
201 *
202 * @return Charitable_Log_Query
203 */
204 public function get_query() {
205 if ( ! isset( $this->query ) ) {
206 $this->query = new Charitable_Log_Query( $this->get_db() );
207 }
208
209 return $this->query;
210 }
211
212 /**
213 * Add a record to the queue.
214 *
215 * @since 1.8.11
216 *
217 * @param array $record Record data array from Charitable_Log_Record::create().
218 */
219 public function add( $record ) {
220 $this->queue[] = $record;
221
222 if ( ! $this->shutdown_registered ) {
223 add_action( 'shutdown', array( $this, 'save_queued_records' ) );
224 $this->shutdown_registered = true;
225 }
226 }
227
228 /**
229 * Save all queued records to the database.
230 *
231 * @since 1.8.11
232 */
233 public function save_queued_records() {
234 if ( empty( $this->queue ) ) {
235 return;
236 }
237
238 $this->ensure_table_exists();
239
240 $db = $this->get_db();
241
242 if ( count( $this->queue ) === 1 ) {
243 $db->insert( $this->queue[0] );
244 } else {
245 $db->insert_batch( $this->queue );
246 }
247
248 $this->queue = array();
249 }
250
251 /**
252 * Ensure the logs table exists (with transient-cached check).
253 *
254 * @since 1.8.11
255 */
256 public function ensure_table_exists() {
257 $transient_key = 'charitable_logs_table_exists';
258
259 if ( get_transient( $transient_key ) ) {
260 return;
261 }
262
263 $db = $this->get_db();
264
265 if ( ! $db->table_exists() ) {
266 $db->create_table();
267 }
268
269 set_transient( $transient_key, true, DAY_IN_SECONDS );
270 }
271
272 /**
273 * Check if logging is enabled.
274 *
275 * @since 1.8.11
276 *
277 * @return bool
278 */
279 public static function is_enabled() {
280 $tools = get_option( 'charitable_tools', array() );
281
282 // Default to enabled.
283 if ( ! isset( $tools['logs_enable'] ) ) {
284 return true;
285 }
286
287 return (bool) $tools['logs_enable'];
288 }
289
290 /**
291 * Get the retention days setting.
292 *
293 * @since 1.8.11
294 *
295 * @return int
296 */
297 public static function get_retention_days() {
298 $tools = get_option( 'charitable_tools', array() );
299 $default = 30;
300
301 return isset( $tools['logs_retention_days'] ) ? absint( $tools['logs_retention_days'] ) : $default;
302 }
303
304 /**
305 * AJAX handler: Get a single log record for the modal.
306 *
307 * @since 1.8.11
308 */
309 public static function ajax_get_record() {
310 check_ajax_referer( 'charitable_logs_nonce', 'nonce' );
311
312 if ( ! current_user_can( 'manage_charitable_settings' ) ) {
313 wp_send_json_error( array( 'message' => __( 'Permission denied.', 'charitable' ) ) );
314 return;
315 }
316
317 $id = isset( $_POST['record_id'] ) ? absint( $_POST['record_id'] ) : 0;
318 $record = self::get_instance()->get_query()->get_by_id( $id );
319
320 if ( ! $record ) {
321 wp_send_json_error( array( 'message' => __( 'Record not found.', 'charitable' ) ) );
322 return;
323 }
324
325 $all_types = self::get_log_types();
326 $all_levels = self::get_log_levels();
327 $all_sources = self::get_sources();
328
329 $type_labels = array();
330 foreach ( $record->get_types( 'key' ) as $type_key ) {
331 $type_labels[] = isset( $all_types[ $type_key ] ) ? $all_types[ $type_key ] : $type_key;
332 }
333
334 wp_send_json_success( array(
335 'id' => $record->id,
336 'title' => $record->title,
337 'message' => $record->message,
338 'types' => implode( ', ', $type_labels ),
339 'level' => isset( $all_levels[ $record->level ] ) ? $all_levels[ $record->level ] : $record->level,
340 'level_key' => $record->level,
341 'source' => isset( $all_sources[ $record->source ] ) ? $all_sources[ $record->source ] : $record->source,
342 'date' => $record->get_date( 'full' ),
343 'campaign_id' => $record->campaign_id,
344 'donation_id' => $record->donation_id,
345 'donor_id' => $record->donor_id,
346 'user_id' => $record->user_id,
347 'object_id' => $record->object_id,
348 'object_type' => $record->object_type,
349 ) );
350 }
351
352 /**
353 * AJAX handler: Delete all log records.
354 *
355 * @since 1.8.11
356 */
357 public static function ajax_delete_all_logs() {
358 check_ajax_referer( 'charitable_logs_nonce', 'nonce' );
359
360 if ( ! current_user_can( 'manage_charitable_settings' ) ) {
361 wp_send_json_error( array( 'message' => __( 'Permission denied.', 'charitable' ) ) );
362 return;
363 }
364
365 $db = self::get_instance()->get_db();
366 $db->truncate_table();
367
368 wp_send_json_success( array( 'message' => __( 'All logs have been deleted.', 'charitable' ) ) );
369 }
370
371 /**
372 * AJAX handler: Export logs as CSV.
373 *
374 * @since 1.8.11
375 */
376 public static function ajax_export_csv() {
377 check_ajax_referer( 'charitable_logs_nonce', 'nonce' );
378
379 if ( ! current_user_can( 'manage_charitable_settings' ) ) {
380 wp_die( esc_html__( 'Permission denied.', 'charitable' ) );
381 }
382
383 $export = new Charitable_Log_Export();
384 $export->run();
385 }
386
387 /**
388 * AJAX handler: Toggle logging on/off.
389 *
390 * @since 1.8.11
391 */
392 public static function ajax_toggle_logging() {
393 check_ajax_referer( 'charitable_logs_nonce', 'nonce' );
394
395 if ( ! current_user_can( 'manage_charitable_settings' ) ) {
396 wp_send_json_error( array( 'message' => __( 'Permission denied.', 'charitable' ) ) );
397 return;
398 }
399
400 $enabled = isset( $_POST['enabled'] ) ? absint( $_POST['enabled'] ) : 0;
401 $tools = get_option( 'charitable_tools', array() );
402
403 if ( ! is_array( $tools ) ) {
404 $tools = array();
405 }
406
407 $tools['logs_enable'] = $enabled;
408 update_option( 'charitable_tools', $tools );
409
410 wp_send_json_success( array(
411 'message' => $enabled
412 ? __( 'Logging enabled.', 'charitable' )
413 : __( 'Logging disabled.', 'charitable' ),
414 ) );
415 }
416
417 /**
418 * AJAX handler: Save log retention days.
419 *
420 * @since 1.8.11
421 */
422 public static function ajax_save_retention() {
423 check_ajax_referer( 'charitable_logs_nonce', 'nonce' );
424
425 if ( ! current_user_can( 'manage_charitable_settings' ) ) {
426 wp_send_json_error( array( 'message' => __( 'Permission denied.', 'charitable' ) ) );
427 return;
428 }
429
430 $days = isset( $_POST['days'] ) ? absint( $_POST['days'] ) : 30;
431 $tools = get_option( 'charitable_tools', array() );
432
433 if ( ! is_array( $tools ) ) {
434 $tools = array();
435 }
436
437 $tools['logs_retention_days'] = $days;
438 update_option( 'charitable_tools', $tools );
439
440 wp_send_json_success( array(
441 'message' => sprintf(
442 /* translators: %d: number of days */
443 __( 'Retention set to %d days.', 'charitable' ),
444 $days
445 ),
446 ) );
447 }
448
449 /**
450 * Enqueue scripts for the logs admin page.
451 *
452 * @since 1.8.11
453 */
454 public static function enqueue_scripts() {
455 if ( ! charitable_is_tools_view() || ! isset( $_GET['tab'] ) || 'logs' !== $_GET['tab'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
456 return;
457 }
458
459 $version = charitable()->get_version();
460 $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
461
462 wp_enqueue_script(
463 'charitable-logger',
464 charitable()->get_path( 'assets', false ) . 'js/admin/charitable-logger.js',
465 array( 'jquery', 'wp-util' ),
466 $version,
467 true
468 );
469
470 wp_localize_script(
471 'charitable-logger',
472 'charitableLogger',
473 array(
474 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
475 'nonce' => wp_create_nonce( 'charitable_logs_nonce' ),
476 'i18n' => array(
477 'deleteConfirm' => __( 'Are you sure you want to delete ALL log records? This cannot be undone.', 'charitable' ),
478 'deleting' => __( 'Deleting...', 'charitable' ),
479 'deleted' => __( 'All logs deleted.', 'charitable' ),
480 'saving' => __( 'Saving...', 'charitable' ),
481 'saved' => __( 'Saved.', 'charitable' ),
482 'error' => __( 'An error occurred.', 'charitable' ),
483 'close' => __( 'Close', 'charitable' ),
484 ),
485 )
486 );
487
488 wp_enqueue_style(
489 'charitable-logger',
490 charitable()->get_path( 'assets', false ) . 'css/admin/charitable-logger.css',
491 array(),
492 $version
493 );
494 }
495 }
496
497 endif;
498