| 1 |
<?php |
| 2 |
/** |
| 3 |
* Activity Log logs view. |
| 4 |
* |
| 5 |
* @package King_Addons |
| 6 |
*/ |
| 7 |
|
| 8 |
if (!defined('ABSPATH')) { |
| 9 |
exit; |
| 10 |
} |
| 11 |
|
| 12 |
$filters = $this->get_filters_from_request(); |
| 13 |
$page = isset($_GET['paged']) ? max(1, absint($_GET['paged'])) : 1; |
| 14 |
$per_page = (int) ($settings['rows_per_page'] ?? 20); |
| 15 |
|
| 16 |
$current_page = isset($_GET['page']) ? sanitize_key($_GET['page']) : 'king-addons-activity-log'; |
| 17 |
$can_manage = current_user_can('manage_options'); |
| 18 |
|
| 19 |
$logs = $this->get_logs($filters, $page, $per_page); |
| 20 |
$total = $this->get_logs_count($filters); |
| 21 |
$total_pages = $per_page > 0 ? (int) ceil($total / $per_page) : 1; |
| 22 |
|
| 23 |
$event_labels = $this->get_event_labels(); |
| 24 |
$users = $this->get_recent_users_for_filter(); |
| 25 |
|
| 26 |
$filter_args = [ |
| 27 |
'page' => $current_page, |
| 28 |
'view' => 'logs', |
| 29 |
]; |
| 30 |
|
| 31 |
if (!empty($filters['search'])) { |
| 32 |
$filter_args['s'] = sanitize_text_field(wp_unslash($_GET['s'] ?? '')); |
| 33 |
} |
| 34 |
if (!empty($filters['event_key'])) { |
| 35 |
$filter_args['event_key'] = sanitize_text_field(wp_unslash($_GET['event_key'] ?? '')); |
| 36 |
} |
| 37 |
if (!empty($filters['severity'])) { |
| 38 |
$filter_args['severity'] = sanitize_text_field(wp_unslash($_GET['severity'] ?? '')); |
| 39 |
} |
| 40 |
if (!empty($filters['user_id'])) { |
| 41 |
$filter_args['user_id'] = absint($_GET['user_id'] ?? 0); |
| 42 |
} |
| 43 |
if (!empty($_GET['date_from'])) { |
| 44 |
$filter_args['date_from'] = sanitize_text_field(wp_unslash($_GET['date_from'])); |
| 45 |
} |
| 46 |
if (!empty($_GET['date_to'])) { |
| 47 |
$filter_args['date_to'] = sanitize_text_field(wp_unslash($_GET['date_to'])); |
| 48 |
} |
| 49 |
if (!empty($_GET['ip']) && $is_pro) { |
| 50 |
$filter_args['ip'] = sanitize_text_field(wp_unslash($_GET['ip'])); |
| 51 |
} |
| 52 |
|
| 53 |
$reset_url = add_query_arg([ |
| 54 |
'page' => $current_page, |
| 55 |
'view' => 'logs', |
| 56 |
], admin_url('admin.php')); |
| 57 |
|
| 58 |
$export_args = array_merge($filter_args, [ |
| 59 |
'action' => 'kng_activity_log_export', |
| 60 |
]); |
| 61 |
$export_url = wp_nonce_url(add_query_arg($export_args, admin_url('admin-post.php')), 'kng_activity_log_export'); |
| 62 |
|
| 63 |
$date_from_value = isset($_GET['date_from']) ? sanitize_text_field(wp_unslash($_GET['date_from'])) : ''; |
| 64 |
$date_to_value = isset($_GET['date_to']) ? sanitize_text_field(wp_unslash($_GET['date_to'])) : ''; |
| 65 |
?> |
| 66 |
|
| 67 |
<div class="ka-card"> |
| 68 |
<div class="ka-card-header"> |
| 69 |
<span class="dashicons dashicons-filter purple"></span> |
| 70 |
<h2><?php esc_html_e('Filters', 'king-addons'); ?></h2> |
| 71 |
</div> |
| 72 |
<div class="ka-card-body"> |
| 73 |
<form method="get" class="kng-activity-filters"> |
| 74 |
<input type="hidden" name="page" value="king-addons-activity-log"> |
| 75 |
<input type="hidden" name="view" value="logs"> |
| 76 |
|
| 77 |
<input type="search" name="s" value="<?php echo esc_attr($filters['search']); ?>" placeholder="<?php esc_attr_e('Search events, users, objects...', 'king-addons'); ?>"> |
| 78 |
|
| 79 |
<select name="event_key"> |
| 80 |
<option value=""><?php esc_html_e('All events', 'king-addons'); ?></option> |
| 81 |
<?php foreach ($event_labels as $key => $label) : ?> |
| 82 |
<option value="<?php echo esc_attr($key); ?>" <?php selected($filters['event_key'], $key); ?>> |
| 83 |
<?php echo esc_html($label); ?> |
| 84 |
</option> |
| 85 |
<?php endforeach; ?> |
| 86 |
</select> |
| 87 |
|
| 88 |
<select name="severity"> |
| 89 |
<option value=""><?php esc_html_e('All severity', 'king-addons'); ?></option> |
| 90 |
<option value="info" <?php selected($filters['severity'], 'info'); ?>><?php esc_html_e('Info', 'king-addons'); ?></option> |
| 91 |
<option value="notice" <?php selected($filters['severity'], 'notice'); ?>><?php esc_html_e('Notice', 'king-addons'); ?></option> |
| 92 |
<option value="warning" <?php selected($filters['severity'], 'warning'); ?>><?php esc_html_e('Warning', 'king-addons'); ?></option> |
| 93 |
<option value="critical" <?php selected($filters['severity'], 'critical'); ?>><?php esc_html_e('Critical', 'king-addons'); ?></option> |
| 94 |
</select> |
| 95 |
|
| 96 |
<select name="user_id"> |
| 97 |
<option value=""><?php esc_html_e('All users', 'king-addons'); ?></option> |
| 98 |
<?php foreach ($users as $user_id => $user_login) : ?> |
| 99 |
<option value="<?php echo esc_attr($user_id); ?>" <?php selected($filters['user_id'], $user_id); ?>> |
| 100 |
<?php echo esc_html($user_login); ?> |
| 101 |
</option> |
| 102 |
<?php endforeach; ?> |
| 103 |
</select> |
| 104 |
|
| 105 |
<input type="date" name="date_from" value="<?php echo esc_attr($date_from_value); ?>"> |
| 106 |
<input type="date" name="date_to" value="<?php echo esc_attr($date_to_value); ?>"> |
| 107 |
|
| 108 |
<?php if ($is_pro) : ?> |
| 109 |
<input type="text" name="ip" value="<?php echo esc_attr($filters['ip']); ?>" placeholder="<?php esc_attr_e('IP address', 'king-addons'); ?>"> |
| 110 |
<?php else : ?> |
| 111 |
<div class="kng-pro-input"> |
| 112 |
<input type="text" placeholder="<?php esc_attr_e('IP address (Pro)', 'king-addons'); ?>" disabled> |
| 113 |
<span class="ka-pro-badge">PRO</span> |
| 114 |
</div> |
| 115 |
<?php endif; ?> |
| 116 |
|
| 117 |
<div class="kng-activity-filter-actions"> |
| 118 |
<button type="submit" class="ka-btn ka-btn-primary"><?php esc_html_e('Apply Filters', 'king-addons'); ?></button> |
| 119 |
<a class="ka-btn ka-btn-secondary" href="<?php echo esc_url($reset_url); ?>"><?php esc_html_e('Reset', 'king-addons'); ?></a> |
| 120 |
<?php if ($can_manage) : ?> |
| 121 |
<a class="ka-btn ka-btn-secondary" href="<?php echo esc_url($export_url); ?>"><?php esc_html_e('Export CSV', 'king-addons'); ?></a> |
| 122 |
<?php endif; ?> |
| 123 |
</div> |
| 124 |
</form> |
| 125 |
</div> |
| 126 |
</div> |
| 127 |
|
| 128 |
<div class="ka-card"> |
| 129 |
<div class="ka-card-header"> |
| 130 |
<span class="dashicons dashicons-clipboard purple"></span> |
| 131 |
<h2><?php esc_html_e('Activity Logs', 'king-addons'); ?></h2> |
| 132 |
</div> |
| 133 |
<div class="ka-card-body" style="padding:0;"> |
| 134 |
<?php if (empty($logs)) : ?> |
| 135 |
<div class="ka-empty"> |
| 136 |
<span class="dashicons dashicons-shield-alt"></span> |
| 137 |
<p><?php esc_html_e('No log entries yet.', 'king-addons'); ?></p> |
| 138 |
</div> |
| 139 |
<?php else : ?> |
| 140 |
<table class="ka-table kng-activity-table"> |
| 141 |
<thead> |
| 142 |
<tr> |
| 143 |
<th><?php esc_html_e('Time', 'king-addons'); ?></th> |
| 144 |
<th><?php esc_html_e('User', 'king-addons'); ?></th> |
| 145 |
<th><?php esc_html_e('Role', 'king-addons'); ?></th> |
| 146 |
<th><?php esc_html_e('Event', 'king-addons'); ?></th> |
| 147 |
<th><?php esc_html_e('Object', 'king-addons'); ?></th> |
| 148 |
<th><?php esc_html_e('Severity', 'king-addons'); ?></th> |
| 149 |
<th><?php esc_html_e('IP', 'king-addons'); ?></th> |
| 150 |
<th><?php esc_html_e('Source', 'king-addons'); ?></th> |
| 151 |
<th><?php esc_html_e('Actions', 'king-addons'); ?></th> |
| 152 |
</tr> |
| 153 |
</thead> |
| 154 |
<tbody> |
| 155 |
<?php foreach ($logs as $log) : |
| 156 |
$event_label = $event_labels[$log->event_key] ?? $log->event_key; |
| 157 |
$object_label = $log->object_title ?: $log->object_id; |
| 158 |
$object_label = $object_label ? $log->object_type . ': ' . $object_label : '-'; |
| 159 |
$object_url = ''; |
| 160 |
|
| 161 |
if (!empty($log->object_type) && !empty($log->object_id)) { |
| 162 |
if ($log->object_type === 'user') { |
| 163 |
$object_url = get_edit_user_link((int) $log->object_id); |
| 164 |
} elseif (post_type_exists($log->object_type)) { |
| 165 |
$object_url = get_edit_post_link((int) $log->object_id); |
| 166 |
} |
| 167 |
} |
| 168 |
|
| 169 |
$payload = [ |
| 170 |
'event' => $event_label, |
| 171 |
'event_key' => $log->event_key, |
| 172 |
'time' => $this->format_time($log->created_at), |
| 173 |
'severity' => $log->severity, |
| 174 |
'user' => $log->user_login ?: __('Guest', 'king-addons'), |
| 175 |
'role' => $log->user_role ?: '', |
| 176 |
'ip' => $log->ip ?: '', |
| 177 |
'user_agent' => $log->user_agent ?: '', |
| 178 |
'object_label' => $object_label, |
| 179 |
'object_url' => $object_url, |
| 180 |
'context' => $log->context, |
| 181 |
'source' => $log->source, |
| 182 |
'message' => $log->message, |
| 183 |
'data' => json_decode($log->data ?? '', true), |
| 184 |
]; |
| 185 |
?> |
| 186 |
<tr> |
| 187 |
<td><?php echo esc_html($this->format_time($log->created_at)); ?></td> |
| 188 |
<td><?php echo esc_html($log->user_login ?: __('Guest', 'king-addons')); ?></td> |
| 189 |
<td><?php echo esc_html($log->user_role ?: '-'); ?></td> |
| 190 |
<td> |
| 191 |
<strong><?php echo esc_html($event_label); ?></strong> |
| 192 |
<small class="kng-activity-muted"><?php echo esc_html($log->event_key); ?></small> |
| 193 |
</td> |
| 194 |
<td> |
| 195 |
<?php if ($object_url) : ?> |
| 196 |
<a href="<?php echo esc_url($object_url); ?>" target="_blank" rel="noopener"> |
| 197 |
<?php echo esc_html($object_label); ?> |
| 198 |
</a> |
| 199 |
<?php else : ?> |
| 200 |
<?php echo esc_html($object_label); ?> |
| 201 |
<?php endif; ?> |
| 202 |
</td> |
| 203 |
<td> |
| 204 |
<span class="kng-severity-badge kng-severity-<?php echo esc_attr($log->severity); ?>"> |
| 205 |
<?php echo esc_html(ucfirst($log->severity)); ?> |
| 206 |
</span> |
| 207 |
</td> |
| 208 |
<td><?php echo esc_html($log->ip ?: '-'); ?></td> |
| 209 |
<td><?php echo esc_html($log->source ?: '-'); ?></td> |
| 210 |
<td> |
| 211 |
<button type="button" class="ka-btn ka-btn-secondary ka-btn-sm kng-log-view" data-log="<?php echo esc_attr(wp_json_encode($payload)); ?>"> |
| 212 |
<?php esc_html_e('View', 'king-addons'); ?> |
| 213 |
</button> |
| 214 |
</td> |
| 215 |
</tr> |
| 216 |
<?php endforeach; ?> |
| 217 |
</tbody> |
| 218 |
</table> |
| 219 |
<?php endif; ?> |
| 220 |
</div> |
| 221 |
</div> |
| 222 |
|
| 223 |
<?php if ($total_pages > 1) : ?> |
| 224 |
<div class="kng-activity-pagination"> |
| 225 |
<?php for ($i = 1; $i <= $total_pages; $i++) : |
| 226 |
$page_url = add_query_arg(array_merge($filter_args, ['paged' => $i]), admin_url('admin.php')); |
| 227 |
?> |
| 228 |
<a class="<?php echo $i === $page ? 'active' : ''; ?>" href="<?php echo esc_url($page_url); ?>"> |
| 229 |
<?php echo esc_html($i); ?> |
| 230 |
</a> |
| 231 |
<?php endfor; ?> |
| 232 |
</div> |
| 233 |
<?php endif; ?> |
| 234 |
|
| 235 |
<div class="kng-activity-drawer" aria-hidden="true"> |
| 236 |
<div class="kng-activity-drawer-overlay"></div> |
| 237 |
<div class="kng-activity-drawer-panel"> |
| 238 |
<div class="kng-activity-drawer-header"> |
| 239 |
<div> |
| 240 |
<span class="kng-activity-drawer-label"><?php esc_html_e('Event Details', 'king-addons'); ?></span> |
| 241 |
<h3 data-field="event"><?php esc_html_e('Event', 'king-addons'); ?></h3> |
| 242 |
</div> |
| 243 |
<button type="button" class="kng-activity-drawer-close"> |
| 244 |
<span class="dashicons dashicons-no-alt"></span> |
| 245 |
</button> |
| 246 |
</div> |
| 247 |
<div class="kng-activity-drawer-body"> |
| 248 |
<div class="kng-activity-drawer-grid"> |
| 249 |
<div> |
| 250 |
<span class="kng-activity-muted"><?php esc_html_e('Time', 'king-addons'); ?></span> |
| 251 |
<div data-field="time"></div> |
| 252 |
</div> |
| 253 |
<div> |
| 254 |
<span class="kng-activity-muted"><?php esc_html_e('Severity', 'king-addons'); ?></span> |
| 255 |
<div class="kng-severity-badge" data-field="severity"></div> |
| 256 |
</div> |
| 257 |
<div> |
| 258 |
<span class="kng-activity-muted"><?php esc_html_e('User', 'king-addons'); ?></span> |
| 259 |
<div data-field="user"></div> |
| 260 |
</div> |
| 261 |
<div> |
| 262 |
<span class="kng-activity-muted"><?php esc_html_e('Role', 'king-addons'); ?></span> |
| 263 |
<div data-field="role"></div> |
| 264 |
</div> |
| 265 |
<div> |
| 266 |
<span class="kng-activity-muted"><?php esc_html_e('IP Address', 'king-addons'); ?></span> |
| 267 |
<div data-field="ip"></div> |
| 268 |
</div> |
| 269 |
<div> |
| 270 |
<span class="kng-activity-muted"><?php esc_html_e('Source', 'king-addons'); ?></span> |
| 271 |
<div data-field="source"></div> |
| 272 |
</div> |
| 273 |
<div> |
| 274 |
<span class="kng-activity-muted"><?php esc_html_e('Context', 'king-addons'); ?></span> |
| 275 |
<div data-field="context"></div> |
| 276 |
</div> |
| 277 |
<div> |
| 278 |
<span class="kng-activity-muted"><?php esc_html_e('Object', 'king-addons'); ?></span> |
| 279 |
<div data-field="object"></div> |
| 280 |
</div> |
| 281 |
</div> |
| 282 |
|
| 283 |
<div class="kng-activity-section"> |
| 284 |
<span class="kng-activity-muted"><?php esc_html_e('Summary', 'king-addons'); ?></span> |
| 285 |
<p data-field="message"></p> |
| 286 |
</div> |
| 287 |
|
| 288 |
<div class="kng-activity-section"> |
| 289 |
<span class="kng-activity-muted"><?php esc_html_e('User Agent', 'king-addons'); ?></span> |
| 290 |
<p data-field="user_agent"></p> |
| 291 |
</div> |
| 292 |
|
| 293 |
<div class="kng-activity-section"> |
| 294 |
<span class="kng-activity-muted"><?php esc_html_e('Event Data', 'king-addons'); ?></span> |
| 295 |
<pre class="kng-activity-json" data-field="data"></pre> |
| 296 |
</div> |
| 297 |
</div> |
| 298 |
</div> |
| 299 |
</div> |
| 300 |
|