PluginProbe
Sessions / 2.3.1
Sessions v2.3.1
2.1.0 2.10.0 2.11.0 2.12.0 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.2.0 2.3.0 2.3.1 2.4.0 2.4.1 2.5.0 2.6.0 2.6.1 2.6.2 2.7.0 2.8.0 2.9.0 2.9.1 3.0.0 3.1.0 3.1.1 All 39 releases
sessions / includes / features / class-sessions.php

class-sessions.php in Sessions 2.3.1, at includes/features/class-sessions.php

1,010 lines 31.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Sessions list
4 *
5 * Lists all active sessions.
6 *
7 * @package Features
8 * @author Pierre Lannoy <https://pierre.lannoy.fr/>.
9 * @since 1.0.0
10 */
11
12 namespace POSessions\Plugin\Feature;
13
14 use POSessions\System\Conversion;
15
16 use POSessions\System\Date;
17 use POSessions\System\Timezone;
18 use POSessions\System\Option;
19 use POSessions\System\Session;
20 use POSessions\System\User;
21 use POSessions\System\GeoIP;
22 use POSessions\System\UserAgent;
23 use POSessions\System\Role;
24 use Feather\Icons;
25 use POSessions\System\Hash;
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 sessions list functionality.
34 *
35 * Lists all active sessions.
36 *
37 * @package Features
38 * @author Pierre Lannoy <https://pierre.lannoy.fr/>.
39 * @since 1.0.0
40 */
41 class Sessions extends \WP_List_Table {
42
43 /**
44 * Active sessions
45 *
46 * @since 1.0.0
47 * @var array $sessions The sessions list.
48 */
49 private $sessions = [];
50
51 /**
52 * GeoIP helper.
53 *
54 * @since 1.0.0
55 * @var \POSessions\System\GeoIP $geoip GeoIP helper.
56 */
57 private $geoip = null;
58
59 /**
60 * The number of lines to display.
61 *
62 * @since 1.0.0
63 * @var integer $limit The number of lines to display.
64 */
65 private $limit = 0;
66
67 /**
68 * The page to display.
69 *
70 * @since 1.0.0
71 * @var integer $limit The page to display.
72 */
73 private $paged = 1;
74
75 /**
76 * The main filter.
77 *
78 * @since 1.0.0
79 * @var array $filters The main filter.
80 */
81 private $filters = [];
82
83 /**
84 * The order by of the list.
85 *
86 * @since 1.0.0
87 * @var string $orderby The order by of the list.
88 */
89 private $orderby = 'id';
90
91 /**
92 * The order of the list.
93 *
94 * @since 1.0.0
95 * @var string $order The order of the list.
96 */
97 private $order = 'desc';
98
99 /**
100 * The user id.
101 *
102 * @since 1.0.0
103 * @var integer $user_id The user id.
104 */
105 private $user_id = null;
106
107 /**
108 * The current url.
109 *
110 * @since 1.0.0
111 * @var string $url The current url.
112 */
113 private $url = '';
114
115 /**
116 * The form nonce.
117 *
118 * @since 1.0.0
119 * @var string $nonce The form nonce.
120 */
121 private $nonce = '';
122
123 /**
124 * The action to perform.
125 *
126 * @since 1.0.0
127 * @var string $action The action to perform.
128 */
129 private $action = '';
130
131 /**
132 * The token of the current session.
133 *
134 * @since 1.0.0
135 * @var string $selftoken The token of the current session.
136 */
137 private $selftoken = '';
138
139 /**
140 * Is devices detectable.
141 *
142 * @since 1.0.0
143 * @var boolean $available_devices Is devices detectable.
144 */
145 private $available_devices = false;
146
147 /**
148 * The icons.
149 *
150 * @since 1.0.0
151 * @var array $icons The icons for device, browser and os.
152 */
153 private $icons = [];
154
155 /**
156 * The bulk args.
157 *
158 * @since 1.0.0
159 * @var array $bulk The bulk args.
160 */
161 private $bulk = [];
162
163 /**
164 * The Roles.
165 *
166 * @since 1.0.0
167 * @var array $roles The Roles.
168 */
169 private $roles = [];
170
171 /**
172 * Initialize the class and set its properties.
173 *
174 * @since 1.0.0
175 */
176 public function __construct() {
177 parent::__construct(
178 [
179 'singular' => 'session',
180 'plural' => 'sessions',
181 'ajax' => true,
182 ]
183 );
184 $this->geoip = new GeoIP();
185 $this->selftoken = Hash::simple_hash( wp_get_session_token(), false );
186 global $wp_version;
187 if ( version_compare( $wp_version, '4.2-z', '>=' ) && $this->compat_fields && is_array( $this->compat_fields ) ) {
188 array_push( $this->compat_fields, 'all_items' );
189 }
190 $this->available_devices = class_exists( 'PODeviceDetector\API\Device' );
191 $this->roles = Role::get_all();
192 $this->process_args();
193 $this->process_action();
194 $this->sessions = [];
195 foreach ( Session::get_all_sessions() as $user ) {
196 if ( array_key_exists( 'meta_value', $user ) && is_array( $user['meta_value'] ) ) {
197 foreach ( $user['meta_value'] as $token => $session ) {
198 $item = [];
199 $item['token'] = $token;
200 $item['umeta_id'] = $user['umeta_id'];
201 $item['id'] = $user['user_id'];
202 if ( array_key_exists( 'expiration', $session ) ) {
203 $item['expiration'] = $session['expiration'];
204 }
205 if ( array_key_exists( 'login', $session ) ) {
206 $item['login'] = $session['login'];
207 }
208 if ( array_key_exists( 'session_idle', $session ) ) {
209 $item['session_idle'] = $session['session_idle'];
210 }
211 $item['device'] = '-';
212 $item['os_name'] = '-';
213 $item['os'] = '-';
214 $item['browser'] = '-';
215 $item['client_name'] = '-';
216 $item['os_ver'] = '';
217 $item['client_ver'] = '';
218 if ( array_key_exists( 'ua', $session ) ) {
219 $item['ua'] = $session['ua'];
220 if ( $this->available_devices ) {
221 $device = UserAgent::get( $item['ua'] );
222 $item['brand_id'] = $device->brand_short_name;
223 $item['model'] = $device->model_name;
224 $item['device'] = ( '' !== $device->brand_name ? $device->brand_name : esc_html__( 'Generic', 'sessions' ) ) . ( '' !== $device->model_name ? ' ' . $device->model_name : '' );
225 $item['os_id'] = $device->os_short_name;
226 $item['os_name'] = $device->os_name;
227 $item['os_ver'] = ( '' !== $device->os_version ? ' ' . $device->os_version : '' );
228 $item['os'] = $item['os_name'] . $item['os_ver'];
229 $item['client_id'] = $device->client_short_name;
230 $item['client_name'] = ( '' !== $device->client_name ? $device->client_name : $device->client_full_type );
231 if ( $device->client_is_browser ) {
232 $this->icons[ $item['ua'] ]['browser'] = $device->browser_icon_base64();
233 $item['client_ver'] = ( '' !== $device->client_version ? ' ' . $device->client_version : '' );
234 $item['browser'] = $item['client_name'] . $item['client_ver'];
235 } else {
236 $item['browser'] = $item['client_name'];
237 }
238 $this->icons[ $item['ua'] ]['device'] = $device->brand_icon_base64();
239 $this->icons[ $item['ua'] ]['os'] = $device->os_icon_base64();
240 }
241 }
242 if ( array_key_exists( 'ip', $session ) ) {
243 $item['ip'] = $session['ip'];
244 }
245 if ( 0 < count( $this->filters ) ) {
246 foreach ( $this->filters as $filter => $value ) {
247 if ( array_key_exists( $filter, $item ) && (string) $value !== (string) $item[ $filter ] ) {
248 continue 2;
249 }
250 }
251 }
252 $this->sessions[] = $item;
253 }
254 }
255 }
256 }
257
258 /**
259 * Default column formatter.
260 *
261 * @param array $item The current item.
262 * @param string $column_name The current column name.
263 * @return string The cell formatted, ready to print.
264 * @since 1.0.0
265 */
266 protected function column_default( $item, $column_name ) {
267 return $item[ $column_name ];
268 }
269
270 /**
271 * Check box column formatter.
272 *
273 * @param array $item The current item.
274 * @return string The cell formatted, ready to print.
275 * @since 1.0.0
276 */
277 public function column_cb( $item ) {
278 return sprintf(
279 '<input ' . ( $item['token'] === $this->selftoken ? 'disabled ' : '' ) . 'type="checkbox" name="bulk[]" value="%s" />',
280 $item['id'] . ':' . $item['token']
281 );
282 }
283
284 /**
285 * "post" column formatter.
286 *
287 * @param array $item The current item.
288 * @return string The cell formatted, ready to print.
289 * @since 1.0.0
290 */
291 protected function column_id( $item ) {
292 $user_info = get_userdata( $item['id'] );
293 $name = $user_info->display_name;
294 $icon = '<img style="width:32px;margin-right:10px;margin-top:1px;float:left;" src="' . esc_url( get_avatar_url( $item['id'], [ 'size' => '64' ] ) ) . '" />';
295 $role = Role::get_user_main( $item['id'] );
296 if ( array_key_exists( $role, $this->roles ) ) {
297 $role = $this->roles[ $role ]['l10n_name'];
298 } else {
299 $role = '';
300 }
301 $role = '<br /><span style="color:silver">' . $role . '</span>';
302 $user = '<strong><a href="' . get_edit_user_link( $item['id'] ) . '">' . $name . '</a></strong>';
303 return $icon . $user . $this->get_filter( 'id', $item['id'] ) . $role;
304 }
305
306 /**
307 * "ip" column formatter.
308 *
309 * @param array $item The current item.
310 * @return string The cell formatted, ready to print.
311 * @since 1.0.0
312 */
313 protected function column_ip( $item ) {
314 $icon = $this->geoip->get_flag( $item['ip'], '', 'width:14px;padding-left:4px;padding-right:4px;vertical-align:baseline;' );
315 $result = $icon . $item['ip'] . $this->get_filter( 'ip', $item['ip'] );
316 return $result;
317 }
318
319 /**
320 * "device" column formatter.
321 *
322 * @param array $item The current item.
323 * @return string The cell formatted, ready to print.
324 * @since 1.0.0
325 */
326 protected function column_device( $item ) {
327 $icon = '';
328 $name = $item['device'];
329 if ( array_key_exists( $item['ua'], $this->icons ) && array_key_exists( 'device', $this->icons[ $item['ua'] ] ) ) {
330 $icon = '<img style="width:16px;float:left;padding-right:6px;" src="' . $this->icons[ $item['ua'] ]['device'] . '" />';
331 }
332 if ( array_key_exists( 'brand_id', $item ) ) {
333 $url = [
334 'site' => 'all',
335 'type' => 'device',
336 'id' => $item['brand_id'],
337 'extended' => '' !== $item['model'] ? $item['model'] : '-',
338 ];
339 $name = $this->get_internal_link( UserAgent::get_analytics_url( $url ), $item['device'] );
340 }
341 return $icon . $name . $this->get_filter( 'device', $item['device'] );
342 }
343
344 /**
345 * "OS" column formatter.
346 *
347 * @param array $item The current item.
348 * @return string The cell formatted, ready to print.
349 * @since 1.0.0
350 */
351 protected function column_os( $item ) {
352 $icon = '';
353 $name = $item['os_name'] . $item['os_ver'] . $this->get_filter( 'os_name', $item['os_name'] );
354 if ( array_key_exists( $item['ua'], $this->icons ) && array_key_exists( 'os', $this->icons[ $item['ua'] ] ) ) {
355 $icon = '<img style="width:16px;float:left;padding-right:6px;" src="' . $this->icons[ $item['ua'] ]['os'] . '" />';
356 }
357 if ( array_key_exists( 'os_id', $item ) ) {
358 $url = [
359 'site' => 'all',
360 'type' => 'os',
361 'id' => $item['os_id'],
362 ];
363 $name = $this->get_internal_link( UserAgent::get_analytics_url( $url ), $item['os_name'] ) . $this->get_filter( 'os_name', $item['os_name'] ) . '<br /><span style="color:silver">' . $item['os_ver'] . '</span>';
364 }
365 return $icon . $name;
366 }
367
368 /**
369 * "browser" column formatter.
370 *
371 * @param array $item The current item.
372 * @return string The cell formatted, ready to print.
373 * @since 1.0.0
374 */
375 protected function column_browser( $item ) {
376 $icon = '';
377 $name = $item['client_name'] . $item['client_ver'] . $this->get_filter( 'client_name', $item['client_name'] );
378 if ( array_key_exists( $item['ua'], $this->icons ) && array_key_exists( 'browser', $this->icons[ $item['ua'] ] ) ) {
379 $icon = '<img style="width:16px;float:left;padding-right:6px;" src="' . $this->icons[ $item['ua'] ]['browser'] . '" />';
380 }
381 if ( array_key_exists( 'client_id', $item ) ) {
382 $url = [
383 'site' => 'all',
384 'type' => 'browser',
385 'id' => $item['client_id'],
386 ];
387 $name = $this->get_internal_link( UserAgent::get_analytics_url( $url ), $item['client_name'] ) . $this->get_filter( 'client_name', $item['client_name'] ) . '<br /><span style="color:silver">' . $item['client_ver'] . '</span>';
388 }
389 return $icon . $name;
390 }
391
392 /**
393 * "login" column formatter.
394 *
395 * @param array $item The current item.
396 * @return string The cell formatted, ready to print.
397 * @since 1.0.0
398 */
399 protected function column_login( $item ) {
400 if ( array_key_exists( 'login', $item ) ) {
401 $datetime = new \DateTime( date( 'Y-m-d H:i:s', $item['login'] ) );
402 $datetime->setTimezone( Timezone::network_get() );
403 return $datetime->format( 'Y-m-d H:i:s' );
404 }
405 return '';
406 }
407
408 /**
409 * "idle" column formatter.
410 *
411 * @param array $item The current item.
412 * @return string The cell formatted, ready to print.
413 * @since 1.0.0
414 */
415 protected function column_idle( $item ) {
416 if ( array_key_exists( 'session_idle', $item ) ) {
417 $value = $item['session_idle'] - time();
418 if ( 0 === $value ) {
419 return esc_html__( 'Now', 'sessions' );
420 }
421 if ( 0 > $value ) {
422 $value = 0 - $value;
423 if ( $value < 72 * HOUR_IN_SECONDS ) {
424 return sprintf( esc_html__( '%s ago', 'sessions' ), implode( ', ', Date::get_age_array_from_seconds( $value, true, true ) ) );
425 }
426 return sprintf( esc_html__( '%s ago', 'sessions' ), sprintf( esc_html__( '%d days', 'sessions' ), (int) round( $value / ( 24 * HOUR_IN_SECONDS ), 0 ) ) );
427 }
428 if ( $value < 72 * HOUR_IN_SECONDS ) {
429 return sprintf( esc_html__( 'In %s', 'sessions' ), implode( ', ', Date::get_age_array_from_seconds( $value, true, true ) ) );
430 }
431 return sprintf( esc_html__( 'In %s', 'sessions' ), sprintf( esc_html__( '%d days', 'sessions' ), (int) round( $value / ( 24 * HOUR_IN_SECONDS ), 0 ) ) );
432 }
433 return '';
434 }
435
436 /**
437 * "expiration" column formatter.
438 *
439 * @param array $item The current item.
440 * @return string The cell formatted, ready to print.
441 * @since 1.0.0
442 */
443 protected function column_expiration( $item ) {
444 if ( array_key_exists( 'expiration', $item ) ) {
445 $value = $item['expiration'] - time();
446 if ( 0 === $value ) {
447 return esc_html__( 'Now', 'sessions' );
448 }
449 if ( 0 > $value ) {
450 $value = 0 - $value;
451 if ( $value < 72 * HOUR_IN_SECONDS ) {
452 return sprintf( esc_html__( '%s ago', 'sessions' ), implode( ', ', Date::get_age_array_from_seconds( $value, true, true ) ) );
453 }
454 return sprintf( esc_html__( '%s ago', 'sessions' ), sprintf( esc_html__( '%d days', 'sessions' ), (int) round( $value / ( 24 * HOUR_IN_SECONDS ), 0 ) ) );
455 }
456 if ( $value < 72 * HOUR_IN_SECONDS ) {
457 return sprintf( esc_html__( 'In %s', 'sessions' ), implode( ', ', Date::get_age_array_from_seconds( $value, true, true ) ) );
458 }
459 return sprintf( esc_html__( 'In %s', 'sessions' ), sprintf( esc_html__( '%d days', 'sessions' ), (int) round( $value / ( 24 * HOUR_IN_SECONDS ), 0 ) ) );
460 }
461 return '';
462 }
463
464 /**
465 * Get an internal link markup.
466 *
467 * @param string $url The url.
468 * @param string $anchor The anchor.
469 * @return string The link markup, ready to print.
470 * @since 1.0.0
471 */
472 private function get_internal_link( $url, $anchor ) {
473 if ( '' === $url ) {
474 return $anchor;
475 }
476 return '<a href="' . $url . '" style="text-decoration:none;color:inherit;">' . $anchor . '</a>';
477 }
478
479 /**
480 * Enumerates columns.
481 *
482 * @return array The columns.
483 * @since 1.0.0
484 */
485 public function get_columns() {
486 $columns = [
487 'cb' => '<input type="checkbox" />',
488 'id' => esc_html__( 'Session', 'sessions' ),
489 'ip' => esc_html__( 'Remote IP', 'sessions' ),
490 'device' => esc_html__( 'Device', 'sessions' ),
491 'os' => esc_html__( 'OS', 'sessions' ),
492 'browser' => esc_html__( 'Client', 'sessions' ),
493 'login' => esc_html__( 'Login', 'sessions' ),
494 'idle' => esc_html__( 'Idle exp.', 'sessions' ),
495 'expiration' => esc_html__( 'Standard exp.', 'sessions' ),
496 ];
497 return $columns;
498 }
499
500 /**
501 * Enumerates hidden columns.
502 *
503 * @return array The hidden columns.
504 * @since 1.0.0
505 */
506 protected function get_hidden_columns() {
507 if ( $this->available_devices ) {
508 return [];
509 } else {
510 return [ 'device', 'os', 'browser' ];
511 }
512
513 }
514
515 /**
516 * Enumerates sortable columns.
517 *
518 * @return array The sortable columns.
519 * @since 1.0.0
520 */
521 protected function get_sortable_columns() {
522 $sortable_columns = [
523 'id' => [ 'id', true ],
524 'device' => [ 'device', true ],
525 'os' => [ 'os', true ],
526 'browser' => [ 'browser', true ],
527 'login' => [ 'login', true ],
528 'idle' => [ 'idle', true ],
529 'expiration' => [ 'expiration', true ],
530 ];
531 return $sortable_columns;
532 }
533
534 /**
535 * Enumerates bulk actions.
536 *
537 * @return array The bulk actions.
538 * @since 1.0.0
539 */
540 public function get_bulk_actions() {
541 return [
542 'invalidate' => esc_html__( 'Delete session(s)', 'sessions' ),
543 ];
544 }
545
546 /**
547 * Generate the table navigation above or below the table
548 *
549 * @param string $which Position of extra control.
550 * @since 1.0.0
551 */
552 protected function display_tablenav( $which ) {
553 if ( 'top' === $which ) {
554 wp_nonce_field( 'bulk-sessions-tools', '_wpnonce', false );
555 }
556 echo '<div class="tablenav ' . esc_attr( $which ) . '">';
557 if ( $this->has_items() ) {
558 echo '<div class="alignleft actions bulkactions">';
559 $this->bulk_actions( $which );
560 echo '</div>';
561 }
562 $this->extra_tablenav( $which );
563 $this->pagination( $which );
564 echo '<br class="clear" />';
565 echo '</div>';
566 }
567
568 /**
569 * Extra controls to be displayed between bulk actions and pagination.
570 *
571 * @param string $which Position of extra control.
572 * @since 1.0.0
573 */
574 public function extra_tablenav( $which ) {
575 $list = $this;
576 $args = compact( 'list', 'which' );
577 foreach ( $args as $key => $val ) {
578 $$key = $val;
579 }
580 if ( 'top' === $which || 'bottom' === $which ) {
581 include POSE_ADMIN_DIR . 'partials/sessions-admin-tools-lines.php';
582 }
583 }
584
585 /**
586 * Prepares the list to be displayed.
587 *
588 * @since 1.0.0
589 */
590 public function prepare_items() {
591 $this->set_pagination_args(
592 [
593 'total_items' => count( $this->sessions ),
594 'per_page' => $this->limit,
595 'total_pages' => ceil( count( $this->sessions ) / $this->limit ),
596 ]
597 );
598 $current_page = $this->get_pagenum();
599 $columns = $this->get_columns();
600 $hidden = $this->get_hidden_columns();
601 $sortable = $this->get_sortable_columns();
602 $this->_column_headers = [ $columns, $hidden, $sortable ];
603 $data = $this->sessions;
604 usort(
605 $data,
606 function ( $a, $b ) {
607 if ( 'device' === $this->orderby || 'os' === $this->orderby || 'browser' === $this->orderby ) {
608 $result = 0;
609 if ( array_key_exists( $this->orderby, $a ) && array_key_exists( $this->orderby, $b ) ) {
610 $result = strcmp( strtolower( $a[ $this->orderby ] ), strtolower( $b[ $this->orderby ] ) );
611 }
612 } else {
613 $result = 0;
614 if ( array_key_exists( $this->orderby, $a ) && array_key_exists( $this->orderby, $b ) ) {
615 $result = intval( $a[ $this->orderby ] ) < intval( $b[ $this->orderby ] ) ? 1 : -1;
616 }
617 }
618 return ( 'asc' === $this->order ) ? -$result : $result;
619 }
620 );
621 $this->items = array_slice( $data, ( ( $current_page - 1 ) * $this->limit ), $this->limit );
622 }
623
624 /**
625 * Get the filter image.
626 *
627 * @param string $filter The filter name.
628 * @param string $value The filter value.
629 * @param boolean $soft Optional. The image must be softened.
630 * @return string The filter image, ready to print.
631 * @since 1.0.0
632 */
633 protected function get_filter( $filter, $value, $soft = false ) {
634 $filters = $this->filters;
635 if ( array_key_exists( $filter, $this->filters ) ) {
636 unset( $this->filters[ $filter ] );
637 $url = $this->get_page_url();
638 $alt = esc_html__( 'Remove this filter', 'sessions' );
639 $fill = '#9999FF';
640 $stroke = '#0000AA';
641 } else {
642 $this->filters[ $filter ] = $value;
643 $url = $this->get_page_url();
644 $alt = esc_html__( 'Add as filter', 'sessions' );
645 $fill = 'none';
646 if ( $soft ) {
647 $stroke = '#C0C0FF';
648 } else {
649 $stroke = '#3333AA';
650 }
651 }
652 $this->filters = $filters;
653 return '&nbsp;<a href="' . $url . '"><img title="' . $alt . '" style="width:11px;vertical-align:baseline;" src="' . Icons::get_base64( 'filter', $fill, $stroke ) . '" /></a>';
654 }
655
656 /**
657 * Get the page url with args.
658 *
659 * @return string The url.
660 * @since 1.0.0
661 */
662 public function get_page_url() {
663 $args = [];
664 $args['page'] = 'pose-manager';
665 if ( count( $this->filters ) > 0 ) {
666 foreach ( $this->filters as $key => $filter ) {
667 if ( '' !== $filter ) {
668 $args[ $key ] = $filter;
669 }
670 }
671 }
672 if ( 40 !== $this->limit ) {
673 $args['limit'] = $this->limit;
674 }
675 if ( 'id' !== $this->orderby || 'desc' !== $this->order ) {
676 $args['orderby'] = $this->orderby;
677 $args['order'] = $this->order;
678 }
679 $url = add_query_arg( $args, admin_url( 'admin.php' ) );
680 return $url;
681 }
682
683 /**
684 * Get available lines breakdowns.
685 *
686 * @since 1.0.0
687 */
688 public function get_line_number_select() {
689 $_disp = [ 20, 40, 60, 80 ];
690 $result = [];
691 foreach ( $_disp as $d ) {
692 $l = [];
693 $l['value'] = $d;
694 // phpcs:ignore
695 $l['text'] = sprintf( esc_html__( 'Display %d sessions per page', 'sessions' ), $d );
696 $l['selected'] = ( intval( $d ) === intval( $this->limit ) ? 'selected="selected" ' : '' );
697 $result[] = $l;
698 }
699 return $result;
700 }
701
702 /**
703 * Pagination links.
704 *
705 * @param string $which Position of extra control.
706 * @since 1.0.0
707 */
708 protected function pagination( $which ) {
709 if ( empty( $this->_pagination_args ) ) {
710 return;
711 }
712 $total_items = (int) $this->_pagination_args['total_items'];
713 $total_pages = (int) $this->_pagination_args['total_pages'];
714 $infinite_scroll = false;
715 if ( isset( $this->_pagination_args['infinite_scroll'] ) ) {
716 $infinite_scroll = $this->_pagination_args['infinite_scroll'];
717 }
718 if ( 'top' === $which && $total_pages > 1 ) {
719 $this->screen->render_screen_reader_content( 'heading_pagination' );
720 }
721 // phpcs:ignore
722 $output = '<span class="displaying-num">' . sprintf( _n( '%s item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '</span>';
723 $current = (int) $this->get_pagenum();
724 $removable_query_args = wp_removable_query_args();
725 $current_url = $this->url;
726 $current_url = remove_query_arg( $removable_query_args, $current_url );
727 $page_links = [];
728 $total_pages_before = '<span class="paging-input">';
729 $total_pages_after = '</span></span>';
730 $disable_first = false;
731 $disable_last = false;
732 $disable_prev = false;
733 $disable_next = false;
734 if ( 1 === $current ) {
735 $disable_first = true;
736 $disable_prev = true;
737 }
738 if ( 2 === $current ) {
739 $disable_first = true;
740 }
741 if ( $current === $total_pages ) {
742 $disable_last = true;
743 $disable_next = true;
744 }
745 if ( $current === $total_pages - 1 ) {
746 $disable_last = true;
747 }
748 if ( $disable_first ) {
749 $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&laquo;</span>';
750 } else {
751 $page_links[] = sprintf(
752 "<a class='first-page button' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
753 $this->get_url( remove_query_arg( 'paged', $current_url ), true ),
754 __( 'First page' ),
755 '&laquo;'
756 );
757 }
758 if ( $disable_prev ) {
759 $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&lsaquo;</span>';
760 } else {
761 $page_links[] = sprintf(
762 "<a class='prev-page button' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
763 $this->get_url( add_query_arg( 'paged', max( 1, $current - 1 ), $current_url ), true ),
764 __( 'Previous page' ),
765 '&lsaquo;'
766 );
767 }
768 if ( 'bottom' === $which ) {
769 $html_current_page = $current;
770 $total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page' ) . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">';
771 } else {
772 $html_current_page = sprintf(
773 "%s<input class='current-page' id='current-page-selector' type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' /><span class='tablenav-paging-text'>",
774 '<label for="current-page-selector" class="screen-reader-text">' . __( 'Current Page' ) . '</label>',
775 $current,
776 strlen( $total_pages )
777 );
778 }
779 $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
780 // phpcs:ignore
781 $page_links[] = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . $total_pages_after;
782 if ( $disable_next ) {
783 $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&rsaquo;</span>';
784 } else {
785 $page_links[] = sprintf(
786 "<a class='next-page button' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
787 $this->get_url( add_query_arg( 'paged', min( $total_pages, $current + 1 ), $current_url ), true ),
788 __( 'Next page' ),
789 '&rsaquo;'
790 );
791 }
792 if ( $disable_last ) {
793 $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&raquo;</span>';
794 } else {
795 $page_links[] = sprintf(
796 "<a class='last-page button' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
797 $this->get_url( add_query_arg( 'paged', $total_pages, $current_url ), true ),
798 __( 'Last page' ),
799 '&raquo;'
800 );
801 }
802 $pagination_links_class = 'pagination-links';
803 if ( ! empty( $infinite_scroll ) ) {
804 $pagination_links_class .= ' hide-if-js';
805 }
806 $output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';
807 if ( $total_pages ) {
808 $page_class = $total_pages < 2 ? ' one-page' : '';
809 } else {
810 $page_class = ' no-pages';
811 }
812 $this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";
813 // phpcs:ignore
814 echo $this->_pagination;
815 }
816
817 /**
818 * Print column headers, accounting for hidden and sortable columns.
819 *
820 * @staticvar int $cb_counter.
821 * @param bool $with_id Whether to set the id attribute or not.
822 * @since 1.0.0
823 */
824 public function print_column_headers( $with_id = true ) {
825 list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
826 if ( ! empty( $columns['cb'] ) ) {
827 static $cb_counter = 1;
828 $columns['cb'] = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . __( 'Select All' ) . '</label><input id="cb-select-all-' . $cb_counter . '" type="checkbox" />';
829 $cb_counter++;
830 }
831 foreach ( $columns as $column_key => $column_display_name ) {
832 $class = [ 'manage-column', "column-$column_key" ];
833 if ( in_array( $column_key, $hidden, true ) ) {
834 $class[] = 'hidden';
835 }
836 if ( 'cb' === $column_key ) {
837 $class[] = 'check-column';
838 } elseif ( in_array( $column_key, [ 'posts', 'comments', 'links' ], true ) ) {
839 $class[] = 'num';
840 }
841 if ( $column_key === $primary ) {
842 $class[] = 'column-primary';
843 }
844 if ( isset( $sortable[ $column_key ] ) ) {
845 list( $orderby, $desc_first ) = $sortable[ $column_key ];
846 if ( $this->orderby === $orderby ) {
847 $order = 'asc' === $this->order ? 'desc' : 'asc';
848 $class[] = 'sorted';
849 $class[] = $this->order;
850 } else {
851 $order = $desc_first ? 'desc' : 'asc';
852 $class[] = 'sortable';
853 $class[] = $desc_first ? 'asc' : 'desc';
854 }
855 $column_display_name = '<a href="' . $this->get_url( add_query_arg( compact( 'orderby', 'order' ), $this->url ), true ) . '"><span>' . $column_display_name . '</span><span class="sorting-indicator"></span></a>';
856 }
857 $tag = ( 'cb' === $column_key ) ? 'td' : 'th';
858 $scope = ( 'th' === $tag ) ? 'scope="col"' : '';
859 $id = $with_id ? "id='$column_key'" : '';
860 if ( ! empty( $class ) ) {
861 $class = "class='" . join( ' ', $class ) . "'";
862 }
863 // phpcs:ignore
864 echo "<$tag $scope $id $class>$column_display_name</$tag>";
865 }
866 }
867
868 /**
869 * Generates content for a single row of the table
870 *
871 * @param object $item The current item
872 */
873 public function single_row( $item ) {
874 echo '<tr' . ( $item['token'] === $this->selftoken ? ' class="pose-selftoken"' : '' ) . '>';
875 $this->single_row_columns( $item );
876 echo '</tr>';
877 }
878
879 /**
880 * Get the cleaned url.
881 *
882 * @param boolean $url Optional. The url, false for current url.
883 * @param boolean $limit Optional. Has the limit to be in the url.
884 * @return string The url cleaned, ready to use.
885 * @since 1.0.0
886 */
887 public function get_url( $url = false, $limit = false ) {
888 global $wp;
889 $url = remove_query_arg( 'limit', $url );
890 if ( $limit ) {
891 $url .= ( false === strpos( $url, '?' ) ? '?' : '&' ) . 'limit=' . $this->limit;
892 }
893 return esc_url( $url );
894 }
895
896 /**
897 * Initializes all the list properties.
898 *
899 * @since 1.0.0
900 */
901 public function process_args() {
902 $this->nonce = filter_input( INPUT_POST, '_wpnonce' );
903 $this->url = set_url_scheme( 'http://' . filter_input( INPUT_SERVER, 'HTTP_HOST' ) . filter_input( INPUT_SERVER, 'REQUEST_URI' ) );
904 $this->limit = filter_input( INPUT_GET, 'limit', FILTER_SANITIZE_NUMBER_INT );
905 $this->user_id = filter_input( INPUT_GET, 'user_id', FILTER_SANITIZE_NUMBER_INT );
906 foreach ( [ 'top', 'bottom' ] as $which ) {
907 if ( wp_verify_nonce( $this->nonce, 'bulk-sessions-tools' ) && array_key_exists( 'dolimit-' . $which, $_POST ) ) {
908 $this->limit = filter_input( INPUT_POST, 'limit-' . $which, FILTER_SANITIZE_NUMBER_INT );
909 }
910 }
911 if ( 0 === intval( $this->limit ) ) {
912 $this->limit = filter_input( INPUT_POST, 'limit-top', FILTER_SANITIZE_NUMBER_INT );
913 }
914 if ( 0 === intval( $this->limit ) ) {
915 $this->limit = 40;
916 }
917 $this->paged = filter_input( INPUT_GET, 'paged', FILTER_SANITIZE_NUMBER_INT );
918 if ( ! $this->paged ) {
919 $this->paged = filter_input( INPUT_POST, 'paged', FILTER_SANITIZE_NUMBER_INT );
920 if ( ! $this->paged ) {
921 $this->paged = 1;
922 }
923 }
924 $this->order = filter_input( INPUT_GET, 'order', FILTER_SANITIZE_STRING );
925 if ( ! $this->order ) {
926 $this->order = 'desc';
927 }
928 $this->orderby = filter_input( INPUT_GET, 'orderby', FILTER_SANITIZE_STRING );
929 if ( ! $this->orderby ) {
930 $this->orderby = 'id';
931 }
932 foreach ( [ 'id', 'ip', 'device', 'os_name', 'client_name' ] as $f ) {
933 $v = filter_input( INPUT_GET, $f, FILTER_SANITIZE_STRING );
934 if ( $v ) {
935 $this->filters[ $f ] = $v;
936 }
937 }
938 foreach ( [ 'top', 'bottom' ] as $which ) {
939 if ( wp_verify_nonce( $this->nonce, 'bulk-sessions-tools' ) && array_key_exists( 'dowarmup-' . $which, $_POST ) ) {
940 $this->action = 'warmup';
941 }
942 if ( wp_verify_nonce( $this->nonce, 'bulk-sessions-tools' ) && array_key_exists( 'doinvalidate-' . $which, $_POST ) ) {
943 $this->action = 'reset';
944 }
945 }
946 if ( '' === $this->action ) {
947 $action = '-1';
948 if ( '-1' === $action && wp_verify_nonce( $this->nonce, 'bulk-sessions-tools' ) && array_key_exists( 'action', $_POST ) ) {
949 $action = filter_input( INPUT_POST, 'action', FILTER_SANITIZE_STRING );
950 }
951 if ( '-1' === $action && wp_verify_nonce( $this->nonce, 'bulk-sessions-tools' ) && array_key_exists( 'action2', $_POST ) ) {
952 $action = filter_input( INPUT_POST, 'action2', FILTER_SANITIZE_STRING );
953 }
954 if ( '-1' !== $action && wp_verify_nonce( $this->nonce, 'bulk-sessions-tools' ) && array_key_exists( 'bulk', $_POST ) ) {
955 $this->bulk = filter_input( INPUT_POST, 'bulk', FILTER_SANITIZE_STRING, FILTER_FORCE_ARRAY );
956 if ( 0 < count( $this->bulk ) ) {
957 $this->action = $action;
958 }
959 }
960 }
961 }
962
963 /**
964 * Processes the selected action.
965 *
966 * @since 1.0.0
967 */
968 public function process_action() {
969 switch ( $this->action ) {
970 case 'reset':
971 $count = Session::delete_all_sessions();
972 if ( is_integer( $count ) ) {
973 if ( 0 < $count ) {
974 $message = esc_html__( 'All sessions have been deleted.', 'sessions' );
975 $code = 0;
976 } else {
977 $message = esc_html__( 'No sessions to delete.', 'sessions' );
978 $code = 0;
979 }
980 } else {
981 $message = esc_html__( 'Unable to delete all sessions. Please see events log.', 'sessions' );
982 $code = 500;
983 }
984 break;
985 case 'invalidate':
986 $count = Session::delete_selected_sessions( $this->bulk );
987 if ( is_integer( $count ) ) {
988 if ( 0 < $count ) {
989 $message = esc_html__( 'All selected sessions have been deleted.', 'sessions' );
990 $code = 0;
991 } else {
992 $message = esc_html__( 'No sessions to delete.', 'sessions' );
993 $code = 0;
994 }
995 } else {
996 $message = esc_html__( 'Unable to delete all selected sessions. Please see events log.', 'sessions' );
997 $code = 500;
998 }
999 break;
1000 default:
1001 return;
1002 }
1003 if ( 0 === $code ) {
1004 add_settings_error( 'oembed_manager_no_error', $code, $message, 'updated' );
1005 } else {
1006 add_settings_error( 'oembed_manager_error', $code, $message, 'error' );
1007 }
1008 }
1009 }
1010