PluginProbe
Debug Log Viewer / 2.2.3
Debug Log Viewer v2.2.3
2.5 2.5.1 2.5.2 2.4 trunk 1.0.2 1.0.3 1.1 1.1.1 1.2 1.2.1 1.3 1.4 1.4.1 1.4.2 1.4.3 2.0.1 2.0.3 2.0.4 2.0.5 2.1 2.2.3
debug-log-viewer / admin / controllers / CleanupController.php

CleanupController.php in Debug Log Viewer 2.2.3, at admin/controllers/CleanupController.php

525 lines 15.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Cleanup Controller
4 *
5 * Manages the WordPress Cron scheduling and execution of automatic log cleanup
6 *
7 * @package DebugLogViewer
8 * @since 1.0.0
9 */
10
11 namespace DebugLogViewer\Admin\Controllers;
12
13 if (! defined('ABSPATH')) {
14 exit; // Exit if accessed directly
15 }
16
17 use DebugLogViewer\Admin\Services\LogCleanupService;
18 use DebugLogViewer\Admin\Services\EmailService;
19 use DebugLogViewer\Admin\Traits\ScheduleTrait;
20
21 require_once realpath(__DIR__) . '/BaseController.php';
22 require_once realpath(__DIR__) . '/../services/LogCleanup.php';
23 require_once realpath(__DIR__) . '/../services/Email.php';
24 require_once realpath(__DIR__) . '/../traits/ScheduleTrait.php';
25
26 class CleanupController extends BaseController
27 {
28
29 use ScheduleTrait;
30
31 const CRON_HOOK = 'dbg_lv_auto_cleanup_log';
32 const SCHEDULE_NAME = 'DBG_LV_AUTO_CLEANUP';
33
34 private $cleanupService;
35
36 public function __construct()
37 {
38 $this->cleanupService = new LogCleanupService();
39 }
40
41 public function init()
42 {
43 $this->register_hooks();
44 }
45
46 private function register_hooks()
47 {
48 add_action(self::CRON_HOOK, [$this, 'executeScheduledCleanup']);
49 add_action('admin_init', [$this, 'handleManualCleanup']);
50 add_action('admin_notices', [$this, 'displayCleanupNotices']);
51 add_action('wp_ajax_dbg_lv_run_manual_cleanup', [$this, 'ajaxRunManualCleanup']);
52 add_action('wp_ajax_dbg_lv_get_cleanup_stats', [$this, 'ajaxGetCleanupStats']);
53 }
54
55 public function schedule_cleanup($recurrence = 'daily')
56 {
57 // Free users can only use daily schedule
58 if (!dbg_lv()->is_premium() && $recurrence !== 'daily') {
59 $recurrence = 'daily';
60 }
61
62 if (wp_next_scheduled(self::CRON_HOOK)) {
63 $this->unschedule_cleanup();
64 }
65
66 // Calculate next run time based on schedule interval
67 $time = new \DateTime();
68 $time->add($this->getScheduleInterval($recurrence));
69 $next_run = $time->getTimestamp();
70
71 $scheduled = wp_schedule_event($next_run, $recurrence, self::CRON_HOOK);
72
73 if (false === $scheduled) {
74 error_log('Debug Log Viewer: Failed to schedule auto-cleanup cron job');
75 return false;
76 }
77
78 update_option(LogCleanupService::OPTION_CLEANUP_SCHEDULE, $recurrence);
79 update_option(LogCleanupService::OPTION_CLEANUP_NEXT_RUN, wp_next_scheduled(self::CRON_HOOK));
80
81 return true;
82 }
83
84 public function unschedule_cleanup()
85 {
86 $timestamp = wp_next_scheduled(self::CRON_HOOK);
87
88 if ($timestamp) {
89 wp_unschedule_event($timestamp, self::CRON_HOOK);
90 delete_option(LogCleanupService::OPTION_CLEANUP_SCHEDULE);
91 delete_option(LogCleanupService::OPTION_CLEANUP_NEXT_RUN);
92 return true;
93 }
94
95 return false;
96 }
97
98 public function executeScheduledCleanup()
99 {
100 if (! get_option(LogCleanupService::OPTION_CLEANUP_ENABLED, false)) {
101 return;
102 }
103
104 $result = $this->cleanupService->executeCleanup();
105
106 $this->log_cleanup_result($result);
107
108 update_option(LogCleanupService::OPTION_CLEANUP_NEXT_RUN, wp_next_scheduled(self::CRON_HOOK));
109
110 if ($result['success'] && isset($result['old_size']) && $result['old_size'] > 0) {
111 $this->maybe_send_cleanup_notification($result);
112 }
113
114 return $result;
115 }
116
117 public function handleManualCleanup()
118 {
119 if (! isset($_GET['dbg_lv_manual_cleanup']) || ! isset($_GET['_wpnonce'])) {
120 return;
121 }
122
123 if (! wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'dbg_lv_manual_cleanup')) {
124 wp_die(esc_html__('Security check failed', 'debug-log-viewer'));
125 }
126
127 if (! current_user_can('manage_options')) {
128 wp_die(esc_html__('You do not have permission to perform this action', 'debug-log-viewer'));
129 }
130
131 $result = $this->cleanupService->executeCleanup();
132
133 set_transient('dlv_cleanup_result', $result, 60);
134
135 $redirect_url = remove_query_arg(array('dbg_lv_manual_cleanup', '_wpnonce'));
136 wp_safe_redirect($redirect_url);
137 exit;
138 }
139
140 /**
141 * AJAX handler for manual cleanup
142 */
143 public function ajaxRunManualCleanup()
144 {
145 $this->verifyAjaxRequest();
146
147 $result = $this->cleanupService->executeCleanup();
148
149 if ($result['success']) {
150 wp_send_json_success(array(
151 'message' => $result['message'],
152 'result' => $result,
153 ));
154 } else {
155 wp_send_json_error(array(
156 'message' => $result['message'],
157 'result' => $result,
158 ));
159 }
160 }
161
162 /**
163 * AJAX handler to get fresh cleanup stats and history
164 */
165 public function ajaxGetCleanupStats()
166 {
167 $this->verifyAjaxRequest();
168
169 $stats = $this->cleanupService->get_cleanup_stats();
170 $history = $this->getCleanupHistory(10);
171 // Format history for frontend
172 $formatted_history = array();
173 foreach ($history as $entry) {
174 $method_labels = array(
175 'truncateKeepLast' => __('Truncate', 'debug-log-viewer'),
176 'clear' => __('Clear', 'debug-log-viewer'),
177 'archiveAndRotate' => __('Archive', 'debug-log-viewer'),
178 'unknown' => __('Unknown', 'debug-log-viewer'),
179 );
180
181 $reduction_html = '—';
182 if (isset($entry['old_size']) && isset($entry['new_size']) && $entry['old_size'] > 0) {
183 $reduction = $entry['old_size'] - $entry['new_size'];
184 $reduction_percent = round(($reduction / $entry['old_size']) * 100, 1);
185 $reduction_html = '<span class="text-success">' . size_format($reduction, 2) . ' (' . $reduction_percent . '%)</span>';
186 }
187
188 $formatted_history[] = array(
189 'date' => isset($entry['timestamp']) ? date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($entry['timestamp'])) : '-',
190 'success' => isset($entry['success']) ? $entry['success'] : false,
191 'method' => isset($entry['method']) ? esc_html($method_labels[$entry['method']] ?? $entry['method']) : '-',
192 'size_before' => isset($entry['old_size']) && $entry['old_size'] > 0 ? size_format($entry['old_size'], 2) : '—',
193 'size_after' => isset($entry['new_size']) && $entry['new_size'] > 0 ? size_format($entry['new_size'], 2) : '—',
194 'reduction' => $reduction_html,
195 'message' => isset($entry['message']) ? esc_html($entry['message']) : '',
196 );
197 }
198
199 wp_send_json_success(array(
200 'stats' => $stats,
201 'history' => $formatted_history,
202 ));
203 }
204
205 public function displayCleanupNotices()
206 {
207 $result = get_transient('dlv_cleanup_result');
208
209 if (! $result) {
210 return;
211 }
212
213 delete_transient('dlv_cleanup_result');
214
215 $class = $result['success'] ? 'notice-success' : 'notice-error';
216 $message = esc_html($result['message']);
217
218 if (isset($result['old_size']) && isset($result['new_size'])) {
219 $old_size_mb = round($result['old_size'] / 1024 / 1024, 2);
220 $new_size_mb = round($result['new_size'] / 1024 / 1024, 2);
221 $message .= sprintf(
222 ' (Reduced from %s MB to %s MB)',
223 number_format($old_size_mb, 2),
224 number_format($new_size_mb, 2)
225 );
226 }
227
228 printf(
229 '<div class="notice %s is-dismissible"><p><strong>Debug Log Viewer:</strong> %s</p></div>',
230 esc_attr($class),
231 wp_kses_post($message)
232 );
233 }
234
235 private function log_cleanup_result($result)
236 {
237 // Only log history for Pro users (cleanup history is a Pro feature)
238 if (! dbg_lv()->is_premium()) {
239 return;
240 }
241
242 $history = get_option(LogCleanupService::OPTION_CLEANUP_HISTORY, []);
243
244 $history[] = array(
245 'timestamp' => current_time('mysql'),
246 'success' => $result['success'],
247 'message' => $result['message'],
248 'old_size' => $result['old_size'] ?? 0,
249 'new_size' => $result['new_size'] ?? 0,
250 'method' => $result['method'] ?? 'unknown',
251 );
252
253 if (count($history) > 50) {
254 $history = array_slice($history, -50);
255 }
256
257 update_option(LogCleanupService::OPTION_CLEANUP_HISTORY, $history);
258
259 if (defined('WP_DEBUG') && WP_DEBUG) {
260 $log_message = sprintf(
261 'Debug Log Viewer Auto-Cleanup: %s (Method: %s)',
262 $result['message'],
263 $result['method'] ?? 'N/A'
264 );
265 error_log($log_message);
266 }
267 }
268
269 private function maybe_send_cleanup_notification($result)
270 {
271 if (! get_option(LogCleanupService::OPTION_CLEANUP_NOTIFICATIONS, false)) {
272 return;
273 }
274
275 $admin_email = get_option('admin_email');
276 $site_name = get_option('blogname');
277 $old_size_mb = round($result['old_size'] / 1024 / 1024, 2);
278 $new_size_mb = round($result['new_size'] / 1024 / 1024, 2);
279
280 $email_service = new EmailService();
281 $email_service->setEmails(array($admin_email));
282 $email_service->setSubject(
283 sprintf('[%s] Debug Log Auto-Cleanup Performed', $site_name)
284 );
285 $email_service->setBody(
286 sprintf(
287 "<p>Debug Log Auto-Cleanup was performed on <strong>%s</strong></p>" .
288 "<h3>Details:</h3>" .
289 "<ul>" .
290 "<li><strong>Previous size:</strong> %s MB</li>" .
291 "<li><strong>New size:</strong> %s MB</li>" .
292 "<li><strong>Method:</strong> %s</li>" .
293 "<li><strong>Time:</strong> %s</li>" .
294 "</ul>" .
295 "<p><em>This is an automated notification from Debug Log Viewer plugin.</em></p>",
296 esc_html($site_name),
297 number_format($old_size_mb, 2),
298 number_format($new_size_mb, 2),
299 esc_html($result['method'] ?? 'N/A'),
300 current_time('mysql')
301 )
302 );
303 $email_service->send();
304 }
305
306 /**
307 * Get cleanup schedule information
308 *
309 * @return array Schedule information
310 */
311 public function get_schedule_info()
312 {
313 $next_run = wp_next_scheduled(self::CRON_HOOK);
314
315 return array(
316 'is_scheduled' => (bool) $next_run,
317 'next_run' => $next_run,
318 'next_run_human' => $next_run ? human_time_diff(time(), $next_run) : '',
319 'schedule' => get_option(LogCleanupService::OPTION_CLEANUP_SCHEDULE, 'daily'),
320 );
321 }
322
323 /**
324 * Get formatted next scheduled time (absolute date/time)
325 *
326 * @return string|null Formatted time string or null if not scheduled
327 */
328 public function getFormattedNextScheduledTime()
329 {
330 $timestamp = wp_next_scheduled(self::CRON_HOOK);
331
332 if (! $timestamp) {
333 return null;
334 }
335
336 $datetime = new \DateTime('@' . $timestamp);
337 $datetime->setTimezone(wp_timezone());
338 $date_format = get_option('date_format', 'F j, Y');
339 $time_format = get_option('time_format', 'g:i a');
340
341 return sprintf(
342 /* translators: %1$s: date, %2$s: time */
343 __('Next run: %1$s at %2$s', 'debug-log-viewer'),
344 $datetime->format($date_format),
345 $datetime->format($time_format)
346 );
347 }
348
349 /**
350 * Get time until next cleanup (relative time)
351 *
352 * @return string|null Relative time string or null if not scheduled
353 */
354 public function getTimeUntilNextCleanup()
355 {
356 $timestamp = wp_next_scheduled(self::CRON_HOOK);
357
358 if (!$timestamp) {
359 return null;
360 }
361
362 $current_time = time();
363 $time_diff = $timestamp - $current_time;
364
365 if ($time_diff <= 0) {
366 return __('Running soon...', 'debug-log-viewer');
367 }
368
369 $days = floor($time_diff / 86400);
370 $hours = floor(($time_diff % 86400) / 3600);
371 $minutes = floor(($time_diff % 3600) / 60);
372
373 if ($days > 0) {
374 if ($hours > 0) {
375 return sprintf(
376 /* translators: %d: number of days */
377 _n('%d day', '%d days', $days, 'debug-log-viewer') . ' ' .
378 /* translators: %d: number of hours */
379 _n('%d hour', '%d hours', $hours, 'debug-log-viewer'),
380 $days,
381 $hours
382 );
383 } else {
384 return sprintf(
385 /* translators: %d: number of days */
386 _n('%d day', '%d days', $days, 'debug-log-viewer'),
387 $days
388 );
389 }
390 } elseif ($hours > 0) {
391 return sprintf(
392 /* translators: %d: number of hours */
393 _n('%d hour', '%d hours', $hours, 'debug-log-viewer'),
394 $hours
395 );
396 } else {
397 return sprintf(
398 /* translators: %d: number of minutes */
399 _n('%d minute', '%d minutes', $minutes, 'debug-log-viewer'),
400 $minutes
401 );
402 }
403 }
404
405 /**
406 * Get next scheduled time for AJAX response
407 *
408 * @return array AJAX response data
409 */
410 public function getNextScheduleTimeForAjax()
411 {
412 $timestamp = wp_next_scheduled(self::CRON_HOOK);
413
414 if (! $timestamp) {
415 return [
416 'success' => true,
417 'scheduled' => false,
418 'message' => __('Not scheduled', 'debug-log-viewer'),
419 ];
420 }
421
422 return [
423 'success' => true,
424 'scheduled' => true,
425 'timestamp' => $timestamp,
426 'formatted_time' => $this->getFormattedNextScheduledTime(),
427 'time_until' => $this->getTimeUntilNextCleanup(),
428 ];
429 }
430
431 /**
432 * Get cleanup history
433 *
434 * @param int $limit Number of entries to retrieve
435 * @return array Cleanup history
436 */
437 public function getCleanupHistory($limit = 10)
438 {
439 // Cleanup history is a Pro-only feature
440 if (! dbg_lv()->is_premium()) {
441 return [];
442 }
443
444 $history = get_option(LogCleanupService::OPTION_CLEANUP_HISTORY, []);
445
446 if ($limit > 0) {
447 $history = array_slice($history, -$limit);
448 }
449
450 return array_reverse($history); // Most recent first
451 }
452
453 /**
454 * Initialize cleanup on plugin activation
455 *
456 * Should be called during plugin activation
457 */
458 public static function activate()
459 {
460 // Set default options
461 add_option(LogCleanupService::OPTION_MAX_LOG_SIZE, 50); // 50 MB default
462 add_option(LogCleanupService::OPTION_CLEANUP_ENABLED, false); // Disabled by default
463 add_option(LogCleanupService::OPTION_CLEANUP_METHOD, LogCleanupService::METHOD_TRUNCATE_KEEP_LAST);
464 add_option(LogCleanupService::OPTION_RETENTION_PERCENT, 10); // Keep last 10%
465 add_option(LogCleanupService::OPTION_CLEANUP_SCHEDULE, 'daily');
466 add_option(LogCleanupService::OPTION_CLEANUP_NOTIFICATIONS, false);
467 }
468
469 /**
470 * Cleanup on plugin deactivation
471 *
472 * Should be called during plugin deactivation
473 */
474 public static function deactivate()
475 {
476 // Unschedule cron job
477 $timestamp = wp_next_scheduled(self::CRON_HOOK);
478 if ($timestamp) {
479 wp_unschedule_event($timestamp, self::CRON_HOOK);
480 }
481 }
482
483 /**
484 * Save cleanup settings
485 */
486 public function saveSettings()
487 {
488 $this->verifyAjaxRequest('dbg_lv_save_cleanup_settings');
489
490
491 // Collect settings - using camelCase keys to match validate_settings() expectations
492 $settings = [
493 'cleanupEnabled' => isset($_POST['cleanup_enabled']) && $_POST['cleanup_enabled'] == '1',
494 'maxLogSize' => isset($_POST['max_log_size']) ? absint($_POST['max_log_size']) : 50,
495 'cleanupMethod' => isset($_POST['cleanup_method']) ? sanitize_text_field(wp_unslash($_POST['cleanup_method'])) : 'keep_last',
496 'retentionPercentage' => isset($_POST['retention_percentage']) ? absint($_POST['retention_percentage']) : 10,
497 'emailNotifications' => isset($_POST['email_notifications']) && $_POST['email_notifications'] == '1',
498 ];
499 // Validate settings using the service
500 $validated = LogCleanupService::validate_settings($settings);
501
502 // Save to database - using keys returned from validate_settings()
503 update_option(LogCleanupService::OPTION_CLEANUP_ENABLED, $validated['cleanupEnabled']);
504 update_option(LogCleanupService::OPTION_MAX_LOG_SIZE, $validated['maxLogSize']);
505 update_option(LogCleanupService::OPTION_CLEANUP_METHOD, $validated['cleanupMethod']);
506 update_option(LogCleanupService::OPTION_CLEANUP_NOTIFICATIONS, $validated['emailNotifications'] ?? false);
507 update_option(LogCleanupService::OPTION_RETENTION_PERCENT, $validated['retentionPercentage']);
508
509 // Handle cron scheduling
510 if ($validated['cleanupEnabled']) {
511 $schedule = isset($_POST['cleanup_schedule']) ? sanitize_text_field(wp_unslash($_POST['cleanup_schedule'])) : 'daily';
512 $this->schedule_cleanup($schedule);
513 $message = __('Auto-Cleanup enabled successfully', 'debug-log-viewer');
514 } else {
515 $this->unschedule_cleanup();
516 $message = __('Auto-Cleanup disabled successfully', 'debug-log-viewer');
517 }
518
519 wp_send_json_success([
520 'message' => $message,
521 'settings' => $validated,
522 ]);
523 }
524 }
525