PluginProbe
DecaLog / 4.4.0
DecaLog v4.4.0
3.0.2 3.1.0 3.10.0 3.2.0 3.3.0 3.4.0 3.4.1 3.5.0 3.5.1 3.6.0 3.6.1 3.6.2 3.6.3 3.7.0 3.7.1 3.8.0 3.9.0 3.9.1 4.0.0 4.1.0 4.2.0 4.3.0 4.3.1 4.4.0 4.5.0 All 75 releases
decalog / includes / features / class-events.php

class-events.php in DecaLog 4.4.0, at includes/features/class-events.php

762 lines 22.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Events list
4 *
5 * Lists all events.
6 *
7 * @package Features
8 * @author Pierre Lannoy <https://pierre.lannoy.fr/>.
9 * @since 1.0.0
10 */
11
12 namespace Decalog\Plugin\Feature;
13
14 use Decalog\Logger;
15 use Decalog\Storage\AbstractStorage;
16 use Decalog\System\Date;
17 use Decalog\System\Option;
18 use Decalog\System\Role;
19 use Decalog\System\Timezone;
20 use Feather\Icons;
21 use Decalog\System\GeoIP;
22 use Decalog\System\Hash;
23 use Decalog\Storage\DBStorage;
24 use Decalog\Storage\APCuStorage;
25 use Decalog\Plugin\Feature\SDK;
26
27
28 if ( ! class_exists( 'WP_List_Table' ) ) {
29 require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
30 }
31
32 /**
33 * Define the events list functionality.
34 *
35 * Lists all events.
36 *
37 * @package Features
38 * @author Pierre Lannoy <https://pierre.lannoy.fr/>.
39 * @since 1.0.0
40 */
41 class Events extends \WP_List_Table {
42
43 /**
44 * The available events logs.
45 *
46 * @since 1.0.0
47 * @var array $logs The loggers list.
48 */
49 private static $logs = [];
50
51 /**
52 * The storage engine.
53 *
54 * @since 3.0.0
55 * @var \Decalog\Storage\AbstractStorage $storage The storage engine.
56 */
57 private $storage = null;
58
59 /**
60 * The columns always shown.
61 *
62 * @since 1.0.0
63 * @var array $standard_columns The columns always shown.
64 */
65 private static $standard_columns = [];
66
67 /**
68 * The columns which may be shown.
69 *
70 * @since 1.0.0
71 * @var array $extra_columns The columns which may be shown.
72 */
73 private static $extra_columns = [];
74
75 /**
76 * The columns which must be shown to the current user.
77 *
78 * @since 1.0.0
79 * @var array $extra_columns The columns which must be shown to the current user.
80 */
81 private static $user_columns = [];
82
83 /**
84 * The order of the columns.
85 *
86 * @since 1.0.0
87 * @var array $columns_order The order of the columns.
88 */
89 private static $columns_order = [];
90
91 /**
92 * The events types icons.
93 *
94 * @since 1.0.0
95 * @var array $icons The icons list.
96 */
97 private $icons = [];
98
99 /**
100 * The number of lines to display.
101 *
102 * @since 1.0.0
103 * @var integer $limit The number of lines to display.
104 */
105 private $limit = 25;
106
107 /**
108 * The logger ID.
109 *
110 * @since 1.0.0
111 * @var string $logger The logger ID.
112 */
113 private $logger = null;
114
115 /**
116 * GeoIP helper.
117 *
118 * @since 1.0.0
119 * @var \Decalog\System\GeoIP $geoip GeoIP helper.
120 */
121 private $geoip = null;
122
123 /**
124 * The main filter.
125 *
126 * @since 1.0.0
127 * @var array $filters The main filter.
128 */
129 private $filters = [];
130
131 /**
132 * Forces the site_id filter if set.
133 *
134 * @since 1.0.0
135 * @var integer $force_siteid Forces the site_id filter if set.
136 */
137 private $force_siteid = null;
138
139 /**
140 * The token of the current session.
141 *
142 * @since 2.4.0
143 * @var string $selftoken The token of the current session.
144 */
145 private $selftoken = '';
146
147 /**
148 * Initialize the class and set its properties.
149 *
150 * @since 1.0.0
151 */
152 public function __construct() {
153 parent::__construct(
154 [
155 'singular' => 'event',
156 'plural' => 'events',
157 'ajax' => true,
158 ]
159 );
160 $this->geoip = new GeoIP();
161 $this->selftoken = Hash::simple_hash( wp_get_session_token(), false );
162 }
163
164 /**
165 * Default column formatter.
166 *
167 * @param object $item The current item to render.
168 * @param string $column_name The name of the current rendered column.
169 * @return string The cell formatted, ready to print.
170 * @since 1.0.0
171 */
172 protected function column_default( $item, $column_name ) {
173 return $item[ $column_name ];
174 }
175
176 /**
177 * "event" column formatter.
178 *
179 * @param object $item The current item to render.
180 * @return string The cell formatted, ready to print.
181 * @since 1.0.0
182 */
183 protected function column_event( $item ) {
184 $args = [];
185 $args['page'] = 'decalog-viewer';
186 $args['logid'] = $this->logger;
187 $args['eventid'] = $item['id'];
188 $url = add_query_arg( $args, admin_url( 'admin.php' ) );
189 $icon = '<img style="width:18px;float:left;padding-right:6px;" src="' . EventTypes::$icons[ $item['level'] ] . '" />';
190 $name = '<a href="' . $url . '">' . ChannelTypes::$channel_names[ strtoupper( $item['channel'] ) ] . '</a>' . $this->get_filter( 'channel', $item['channel'] ) . $this->get_actions( 'event', $item ) . '&nbsp;<span style="color:silver">#' . $item['id'] . '</span>';
191 /* translators: as in the sentence "Error code 501" or "Alert code 0" */
192 $code = '<br /><span style="color:silver">' . sprintf( decalog_esc_html__( '%1$s code %2$s', 'decalog' ), ucfirst( $item['level'] ), $item['code'] ) . '</span>';
193 $result = $icon . $name . $code;
194 return $result;
195 }
196
197 /**
198 * "component" column formatter.
199 *
200 * @param object $item The current item to render.
201 * @return string The cell formatted, ready to print.
202 * @since 1.0.0
203 */
204 protected function column_component( $item ) {
205 $icon = '<img style="width:28px;float:left;padding-top:6px;padding-right:6px;" src="' . SDK::get_icon( $item['component'] ) . '" />';
206 $name = $item['component'] . $this->get_filter( 'component', $item['component'] ) . $this->get_actions( 'source', $item ) . ' <span style="color:silver">' . $item['version'] . '</span>';
207 $result = $icon . $name . '<br /><span style="color:silver">' . ClassTypes::$classe_names[ $item['class'] ] . $this->get_filter( 'class', $item['class'], true ) . '</span>';
208 return $result;
209 }
210
211 /**
212 * "time" column formatter.
213 *
214 * @param object $item The current item to render.
215 * @return string The cell formatted, ready to print.
216 * @since 1.0.0
217 */
218 protected function column_time( $item ) {
219 $result = Date::get_date_from_mysql_utc( $item['timestamp'], Timezone::network_get()->getName(), 'Y-m-d H:i:s' ) . $this->get_actions( 'time', $item );
220 $result .= '<br /><span style="color:silver">' . Date::get_positive_time_diff_from_mysql_utc( $item['timestamp'] ) . '</span>';
221 return $result;
222 }
223
224 /**
225 * "site" column formatter.
226 *
227 * @param object $item The current item to render.
228 * @return string The cell formatted, ready to print.
229 * @since 1.0.0
230 */
231 protected function column_site( $item ) {
232 $name = $item['site_name'] . $this->get_filter( 'site_id', $item['site_id'] );
233 // phpcs:ignore
234 $result = $name . '<br /><span style="color:silver">' . sprintf(decalog_esc_html__('Site ID %s', 'decalog'), $item['site_id']) . '</span>' . $this->get_actions( 'site', $item );
235 return $result;
236 }
237
238 /**
239 * "user" column formatter.
240 *
241 * @param object $item The current item to render.
242 * @return string The cell formatted, ready to print.
243 * @since 1.0.0
244 */
245 protected function column_user( $item ) {
246 $user = $item['user_name'];
247 if ( 'anonymous' === $user ) {
248 $user = '<em>' . decalog_esc_html__( 'Anonymous user', 'decalog' ) . '</em>';
249 }
250 $id = '';
251 $se = '';
252 if ( 0 === strpos( $item['user_name'], '{' ) ) {
253 $user = '<em>' . decalog_esc_html__( 'Pseudonymized user', 'decalog' ) . '</em>';
254 } elseif ( 0 !== (int) $item['user_id'] ) {
255 // phpcs:ignore
256 $id = sprintf( decalog_esc_html__( ' (UID %s)', 'decalog' ), $item[ 'user_id' ] );
257 $se = '<br /><span style="color:silver">' . sprintf( decalog_esc_html__( 'Session #%1$s…%2$s', 'decalog' ), substr( $item['user_session'], 0, 2 ), substr( $item['user_session'], -2 ) ) . '</span>';
258 }
259 $result = $user . $id . $this->get_filter( 'user_id', $item['user_id'] ) . $this->get_actions( 'user', $item ) . $se . ( '' !== $se ? $this->get_pose_shortcut( (int) $item['user_id'] ) : '' ) . ( '' !== $se ? $this->get_filter( 'user_session', $item['user_session'] ) : '' );
260 return '<span' . ( ( $item['user_session'] ?? '' ) === $this->selftoken ? ' class="decalog-selftoken"' : '' ) . '>' . $result . '</span>';
261 }
262
263 /**
264 * "ip" column formatter.
265 *
266 * @param object $item The current item to render.
267 * @return string The cell formatted, ready to print.
268 * @since 1.0.0
269 */
270 protected function column_ip( $item ) {
271 $ip = $item['remote_ip'];
272 $icon = '';
273 if ( 0 === strpos( $ip, '{' ) ) {
274 $ip = '<em>' . decalog_esc_html__( 'Obfuscated', 'decalog' ) . '</em>';
275 } else {
276 $icon = $this->geoip->get_flag( $ip, '', 'width:14px;padding-left:4px;padding-right:4px;vertical-align:baseline;', '', '', false, true );
277 }
278 $result = $icon . $ip . $this->get_filter( 'remote_ip', $item['remote_ip'] ) . $this->get_actions( 'ip', $item );
279 return $result;
280 }
281
282 /**
283 * "message" column formatter.
284 *
285 * @param object $item The current item to render.
286 * @return string The cell formatted, ready to print.
287 * @since 1.0.0
288 */
289 protected function column_message( $item ) {
290 $cut = 200;
291 $message = $item['message'];
292 if ( $cut < strlen( $message ) ) {
293 $message = substr( $message, 0, $cut ) . '&nbsp;&nbsp;<em>[…]</em>';
294 }
295 return $message;
296 }
297
298 /**
299 * Initialize the list view.
300 *
301 * @return array The columns to render.
302 * @since 1.0.0
303 */
304 public function get_columns() {
305 $columns = [];
306 foreach ( self::$columns_order as $column ) {
307 if ( array_key_exists( $column, self::$standard_columns ) ) {
308 $columns[ $column ] = self::$standard_columns[ $column ];
309 // phpcs:ignore
310 } elseif ( array_key_exists( $column, self::$extra_columns ) && in_array( $column, self::$user_columns, true ) ) {
311 $columns[ $column ] = self::$extra_columns[ $column ];
312 }
313 }
314 return $columns;
315 }
316
317 /**
318 * Initialize storage.
319 *
320 * @since 1.0.0
321 */
322 protected function init_storage() {
323 $this->storage = null;
324 if ( $this->logger ) {
325 $loggers = Option::network_get( 'loggers' );
326 if ( array_key_exists( $this->logger, $loggers ) ) {
327 $bucket_name = 'decalog_' . str_replace( '-', '', $this->logger );
328 switch ( $loggers[ $this->logger ]['configuration']['constant-storage'] ?? 'none' ) {
329 case 'apcu':
330 $this->storage = new APCuStorage( $bucket_name );
331 break;
332 default:
333 $this->storage = new DBStorage( $bucket_name );
334 }
335 }
336 }
337 }
338
339 /**
340 * Initialize values and filter.
341 *
342 * @since 1.0.0
343 */
344 protected function init_values() {
345 $this->limit = filter_input( INPUT_GET, 'limit', FILTER_SANITIZE_NUMBER_INT );
346 if ( ! $this->limit ) {
347 $this->limit = 25;
348 }
349 $this->force_siteid = null;
350 $this->logger = filter_input( INPUT_GET, 'logger_id', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
351 if ( $this->logger ) {
352 $this->set_level_access();
353 } else {
354 $this->set_first_available();
355 }
356 $this->init_storage();
357 $this->filters = [];
358 $level = filter_input( INPUT_GET, 'level', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
359 if ( $level && array_key_exists( strtolower( $level ), EventTypes::$levels ) && 'debug' !== strtolower( $level ) ) {
360 $this->filters['level'] = strtolower( $level );
361 }
362 foreach ( [ 'class', 'channel', 'site_id', 'user_id', 'remote_ip', 'user_session' ] as $f ) {
363 $v = filter_input( INPUT_GET, $f, FILTER_SANITIZE_FULL_SPECIAL_CHARS );
364 if ( $v ) {
365 $this->filters[ $f ] = strtolower( $v );
366 }
367 }
368 $v = filter_input( INPUT_GET, 'component', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
369 if ( $v ) {
370 $this->filters['component'] = $v;
371 }
372 if ( $this->force_siteid ) {
373 $this->filters['site_id'] = $this->force_siteid;
374 }
375 }
376
377 /**
378 * Get the filter image.
379 *
380 * @param string $filter The filter name.
381 * @param string $value The filter value.
382 * @param boolean $soft Optional. The image must be softened.
383 * @return string The filter image, ready to print.
384 * @since 1.0.0
385 */
386 protected function get_filter( $filter, $value, $soft = false ) {
387 $filters = $this->filters;
388 if ( array_key_exists( $filter, $this->filters ) ) {
389 unset( $this->filters[ $filter ] );
390 $url = $this->get_page_url();
391 $alt = decalog_esc_html__( 'Remove this filter', 'decalog' );
392 $fill = '#9999FF';
393 $stroke = '#0000AA';
394 } else {
395 $this->filters[ $filter ] = $value;
396 $url = $this->get_page_url();
397 $alt = decalog_esc_html__( 'Add as filter', 'decalog' );
398 $fill = 'none';
399 if ( $soft ) {
400 $stroke = '#C0C0FF';
401 } else {
402 $stroke = '#3333AA';
403 }
404 }
405 $this->filters = $filters;
406 return '&nbsp;<a href="' . $url . '"><img title="' . $alt . '" style="width:11px;vertical-align:baseline;" src="' . Icons::get_base64( 'filter', $fill, $stroke ) . '" /></a>';
407 }
408
409 /**
410 * Get a shortcut to Sessions (if running).
411 *
412 * @param string $uid The user id.
413 * @return string The shortcut image, ready to print.
414 * @since 2.4.0
415 */
416 protected function get_pose_shortcut( $uid ) {
417 if ( 0 === $uid || ! class_exists( 'POSessions\Plugin\Core' ) ) {
418 return '';
419 }
420 $url = esc_url( admin_url( 'admin.php?page=pose-manager&id=' ) . $uid );
421 $alt = decalog_esc_html__( 'See all sessions', 'decalog' );
422 $fill = '#C0C0FF';
423 $stroke = '#3333AA';
424 return '&nbsp;<a target="_blank" href="' . $url . '"><img title="' . $alt . '" style="width:11px;vertical-align:baseline;" src="' . Icons::get_base64( 'users', $fill, $stroke ) . '" /></a>';
425 }
426
427 /**
428 * Get the action image.
429 *
430 * @param string $url The url to call.
431 * @param string $hint The hint to display.
432 * @param string $icon The icon to display.
433 * @param boolean $soft Optional. The image must be softened.
434 * @return string The action image, ready to print.
435 * @since 3.3.0
436 */
437 protected function get_action( $url, $hint, $icon, $soft = false ) {
438 return '&nbsp;<a href="' . $url . '" target="_blank"><img title="' . esc_html( $hint ) . '" style="width:11px;vertical-align:baseline;" src="' . Icons::get_base64( $icon, 'none', $soft ? '#C0C0FF' : '#3333AA' ) . '" /></a>';
439 }
440
441 /**
442 * Get the action image.
443 *
444 * @param string $column The column where to display actions.
445 * @param object $item The row item, an event.
446 * @return string The actions images, ready to print.
447 * @since 3.3.0
448 */
449 protected function get_actions( $column, $item ) {
450 $item = (array) $item;
451 $item['logger_id'] = $this->logger;
452 $item['country_code'] = '';
453 if ( false === strpos( $item['remote_ip'], '{' ) ) {
454 $item['country_code'] = $this->geoip->get_iso3166_alpha2( $item['remote_ip'] );
455 }
456
457 /**
458 * Filters the available actions for the current item and column.
459 *
460 * @See https://github.com/Pierre-Lannoy/wp-decalog/blob/master/HOOKS.md
461 * @since 3.3.0
462 * @param array $item The full event with metadata.
463 */
464 $actions = apply_filters( 'decalog_events_list_actions_for_' . $column, [], $item );
465
466 $result = '';
467 foreach ( $actions as $action ) {
468 if ( isset( $action['url'] ) ) {
469 $result .= $this->get_action( $action['url'], isset( $action['hint'] ) ? $action['hint'] :decalog__( 'Unknown action', 'decalog' ), isset( $action['icon'] ) ? $action['icon'] : '' );
470 }
471 }
472 return $result;
473 }
474
475 /**
476 * Initialize the list view.
477 *
478 * @since 1.0.0
479 */
480 public function prepare_items() {
481 $this->init_values();
482 $columns = $this->get_columns();
483 $hidden = [];
484 $sortable = $this->get_sortable_columns();
485 $this->_column_headers = [ $columns, $hidden, $sortable ];
486 $current_page = $this->get_pagenum();
487 $total_items = $this->get_count();
488 $this->items = $this->get_list( ( $current_page - 1 ) * $this->limit, $this->limit );
489 $this->set_pagination_args(
490 [
491 'total_items' => $total_items,
492 'per_page' => $this->limit,
493 'total_pages' => ceil( $total_items / $this->limit ),
494 ]
495 );
496 }
497
498 /**
499 * Generate the table navigation above or below the table
500 *
501 * @param string $which Position of extra control.
502 * @since 1.0.0
503 */
504 protected function display_tablenav( $which ) {
505 echo '<div class="tablenav ' . esc_attr( $which ) . '">';
506 $this->extra_tablenav( $which );
507 $this->pagination( $which );
508 echo '<br class="clear" />';
509 echo '</div>';
510 }
511
512 /**
513 * Extra controls to be displayed between bulk actions and pagination.
514 *
515 * @param string $which Position of extra control.
516 * @since 1.0.0
517 */
518 public function extra_tablenav( $which ) {
519 $list = $this;
520 $args = compact( 'list' );
521 foreach ( $args as $key => $val ) {
522 $$key = $val;
523 }
524 if ( 'top' === $which ) {
525 include DECALOG_ADMIN_DIR . 'partials/decalog-admin-view-events-top.php';
526 }
527 if ( 'bottom' === $which ) {
528 include DECALOG_ADMIN_DIR . 'partials/decalog-admin-view-events-bottom.php';
529 }
530 }
531
532 /**
533 * Get the page url with args.
534 *
535 * @return string The url.
536 * @since 1.0.0
537 */
538 public function get_page_url() {
539 $args = [];
540 $args['page'] = 'decalog-viewer';
541 $args['logger_id'] = $this->logger;
542 if ( count( $this->filters ) > 0 ) {
543 foreach ( $this->filters as $key => $filter ) {
544 if ( '' !== $filter ) {
545 $args[ $key ] = $filter;
546 }
547 }
548 }
549 if ( 25 !== $this->limit ) {
550 $args['limit'] = $this->limit;
551 }
552 $url = add_query_arg( $args, admin_url( 'admin.php' ) );
553 return $url;
554 }
555
556 /**
557 * Get available views line.
558 *
559 * @since 1.0.0
560 */
561 public function get_views() {
562 $filters = $this->filters;
563 $level = array_key_exists( 'level', $this->filters ) ? $this->filters['level'] : '';
564 unset( $this->filters['level'] );
565 $s1 = '<a href="' . $this->get_page_url() . '"' . ( '' === $level ? ' class="current"' : '' ) . '>' . decalog_esc_html__( 'All', 'decalog' ) . ' <span class="count">(' . $this->get_count() . ')</span></a>';
566 $this->filters['level'] = 'notice';
567 $s2 = '<a href="' . $this->get_page_url() . '"' . ( 'notice' === $level ? ' class="current"' : '' ) . '>' . decalog_esc_html__( 'Notices & beyond', 'decalog' ) . ' <span class="count">(' . $this->get_count() . ')</span></a>';
568 $this->filters['level'] = 'error';
569 $s3 = '<a href="' . $this->get_page_url() . '"' . ( 'error' === $level ? ' class="current"' : '' ) . '>' . decalog_esc_html__( 'Errors & beyond', 'decalog' ) . ' <span class="count">(' . $this->get_count() . ')</span></a>';
570 $status_links = [
571 'all' => $s1,
572 'notices' => $s2,
573 'errors' => $s3,
574 ];
575 $this->filters = $filters;
576 return $status_links;
577 }
578
579 /**
580 * Get the available events logs.
581 *
582 * @return array The list of available events logs.
583 * @since 1.0.0
584 */
585 public function get_loggers() {
586 return self::$logs;
587 }
588
589 /**
590 * Get the available events logs.
591 *
592 * @return array The list of available events logs.
593 * @since 1.0.0
594 */
595 public static function get() {
596 return self::$logs;
597 }
598
599 /**
600 * Get the current events log id.
601 *
602 * @return string The current events log id.
603 * @since 1.0.0
604 */
605 public function get_current_Log_id() {
606 return $this->logger;
607 }
608
609 /**
610 * Get available lines breakdowns.
611 *
612 * @since 1.0.0
613 */
614 public function get_line_number_select() {
615 $_disp = [ 25, 50, 100, 250, 500 ];
616 $result = [];
617 foreach ( $_disp as $d ) {
618 $l = [];
619 $l['value'] = $d;
620 // phpcs:ignore
621 $l['text'] = sprintf( decalog_esc_html__( 'Show %d lines per page', 'decalog' ), $d );
622 $l['selected'] = ( $d === (int) $this->limit ? 'selected="selected" ' : '' );
623 $result[] = $l;
624 }
625 return $result;
626 }
627
628 /**
629 * Set the level access to an events log.
630 *
631 * @since 1.0.0
632 */
633 private function set_level_access() {
634 $this->force_siteid = null;
635 $id = $this->logger;
636 $this->logger = null;
637 foreach ( self::$logs as $log ) {
638 if ( $id === $log['id'] ) {
639 $this->logger = $id;
640 if ( array_key_exists( 'limit', $log ) ) {
641 $this->force_siteid = $log['limit'];
642 }
643 }
644 }
645 }
646
647 /**
648 * Set the level access to an events log.
649 *
650 * @since 1.0.0
651 */
652 private function set_first_available() {
653 $this->force_siteid = null;
654 $this->logger = null;
655 foreach ( self::$logs as $log ) {
656 if ( array_key_exists( 'limit', $log ) ) {
657 $this->force_siteid = $log['limit'];
658 }
659 $this->logger = $log['id'];
660 break;
661 }
662 }
663
664 /**
665 * Get list of logged errors.
666 *
667 * @param integer $offset The offset to record.
668 * @param integer $rowcount Optional. The number of rows to return.
669 * @return array An array containing the filtered logged errors.
670 * @since 3.0.0
671 */
672 protected function get_list( $offset = null, $rowcount = null ) {
673 return ( $this->storage ? $this->storage->get_list( $this->filters, $offset, $rowcount ) : [] );
674 }
675
676 /**
677 * Count logged errors.
678 *
679 * @return integer The count of the filtered logged errors.
680 * @since 3.0.0
681 */
682 protected function get_count() {
683 return ( $this->storage ? $this->storage->get_count( $this->filters ) : 0 );
684 }
685
686 /**
687 * Initialize the meta class and set its columns properties.
688 *
689 * @since 1.0.0
690 */
691 private static function load_columns() {
692 self::$standard_columns = [];
693 self::$standard_columns['event'] = decalog_esc_html__( 'Event', 'decalog' );
694 self::$standard_columns['time'] = decalog_esc_html__( 'Time', 'decalog' );
695 self::$standard_columns['message'] = decalog_esc_html__( 'Message', 'decalog' );
696 self::$extra_columns = [];
697 self::$extra_columns['component'] = decalog_esc_html__( 'Source', 'decalog' );
698 self::$extra_columns['site'] = decalog_esc_html__( 'Site', 'decalog' );
699 self::$extra_columns['user'] = decalog_esc_html__( 'User', 'decalog' );
700 self::$extra_columns['ip'] = decalog_esc_html__( 'Remote IP', 'decalog' );
701 self::$columns_order = [ 'event', 'component', 'time', 'site', 'user', 'ip', 'message' ];
702 self::$user_columns = [];
703 foreach ( self::$extra_columns as $key => $extra_column ) {
704 if ( 'site' !== $key || ( 'site' === $key && is_multisite() ) ) {
705 self::$user_columns[] = $key;
706 }
707 }
708 }
709
710 /**
711 * Initialize the meta class and set its properties.
712 *
713 * @since 1.0.0
714 */
715 public static function init() {
716 self::$logs = [];
717 foreach ( Option::network_get( 'loggers' ) as $key => $logger ) {
718 if ( 'WordpressHandler' === $logger['handler'] ) {
719 if ( array_key_exists( 'configuration', $logger ) ) {
720 if ( array_key_exists( 'local', $logger['configuration'] ) ) {
721 $local = $logger['configuration']['local'];
722 } else {
723 $local = false;
724 }
725 if ( Role::SUPER_ADMIN === Role::admin_type() || Role::SINGLE_ADMIN === Role::admin_type() || ( Role::LOCAL_ADMIN === Role::admin_type() && $local ) || Role::override_privileges() ) {
726 $log = [
727 'name' => $logger['name'],
728 'running' => $logger['running'],
729 'id' => $key,
730 ];
731 if ( Role::LOCAL_ADMIN === Role::admin_type() ) {
732 $log['limit'] = get_current_blog_id();
733 }
734 self::$logs[] = $log;
735 }
736 }
737 }
738 }
739 uasort(
740 self::$logs,
741 function ( $a, $b ) {
742 if ( $a['running'] === $b['running'] ) {
743 return strcasecmp( str_replace( ' ', '', $a['name'] ), str_replace( ' ', '', $b['name'] ) );
744 } return $a['running'] ? -1 : 1;
745 }
746 );
747 self::load_columns();
748 }
749
750 /**
751 * Get the number of available logs.
752 *
753 * @return integer The number of logs.
754 * @since 1.0.0
755 */
756 public static function loggers_count() {
757 return count( self::$logs );
758 }
759 }
760
761 Events::init();
762