PluginProbe
Stream – Activity Log & Audit Trail / 3.2.0
Stream – Activity Log & Audit Trail v3.2.0
4.4.0 4.3.0 4.2.2 4.2.1 trunk 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.1 3.1.1 3.10.0 3.2.0 3.2.1 3.2.2 3.2.3 All 50 releases
stream / classes / class-list-table.php

class-list-table.php in Stream – Activity Log & Audit Trail 3.2.0, at classes/class-list-table.php

1,032 lines 30.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace WP_Stream;
3
4 class List_Table extends \WP_List_Table {
5 /**
6 * Hold Plugin class
7 *
8 * @var Plugin
9 */
10 public $plugin;
11
12 /**
13 * Class constructor.
14 *
15 * @param Plugin $plugin The main Plugin class.
16 * @param array $args
17 */
18 function __construct( $plugin, $args = array() ) {
19 $this->plugin = $plugin;
20
21 $screen_id = isset( $args['screen'] ) ? $args['screen'] : null;
22
23 /**
24 * Filter the list table screen ID
25 *
26 * @return string
27 */
28 $screen_id = apply_filters( 'wp_stream_list_table_screen_id', $screen_id );
29
30 parent::__construct(
31 array(
32 'post_type' => 'stream',
33 'plural' => 'records',
34 'screen' => $screen_id,
35 )
36 );
37
38 add_screen_option(
39 'per_page',
40 array(
41 'default' => 20,
42 'label' => __( 'Records per page', 'stream' ),
43 'option' => 'edit_stream_per_page',
44 )
45 );
46
47 // Check for default hidden columns
48 $this->get_hidden_columns();
49
50 add_filter( 'screen_settings', array( $this, 'screen_controls' ), 10, 2 );
51 add_filter( 'set-screen-option', array( $this, 'set_screen_option' ), 10, 3 );
52
53 set_screen_options();
54 }
55
56 function extra_tablenav( $which ) {
57 if ( 'top' === $which ) {
58 echo $this->filters_form(); // xss ok
59 }
60 }
61
62 function no_items() {
63 ?>
64 <div class="stream-list-table-no-items">
65 <p><?php esc_html_e( 'Sorry, no activity records were found.', 'stream' ) ?></p>
66 </div>
67 <?php
68 }
69
70 function get_columns() {
71 /**
72 * Allows devs to add new columns to table
73 *
74 * @return array
75 */
76 return apply_filters(
77 'wp_stream_list_table_columns',
78 array(
79 'date' => __( 'Date', 'stream' ),
80 'summary' => __( 'Summary', 'stream' ),
81 'user_id' => __( 'User', 'stream' ),
82 'context' => __( 'Context', 'stream' ),
83 'action' => __( 'Action', 'stream' ),
84 'ip' => __( 'IP Address', 'stream' ),
85 )
86 );
87 }
88
89 function get_sortable_columns() {
90 return array(
91 'date' => array( 'date', false ),
92 );
93 }
94
95 function get_hidden_columns() {
96 if ( ! $user = wp_get_current_user() ) {
97 return array();
98 }
99
100 // Directly checking the user meta; to check whether user has changed screen option or not
101 $hidden = $this->plugin->admin->get_user_meta( $user->ID, 'manage' . $this->screen->id . 'columnshidden', true );
102
103 // If user meta is not found; add the default hidden column 'id'
104 if ( ! $hidden ) {
105 $hidden = array( 'id' );
106 $this->plugin->admin->update_user_meta( $user->ID, 'manage' . $this->screen->id . 'columnshidden', $hidden );
107 }
108
109 return $hidden;
110 }
111
112 function prepare_items() {
113 $columns = $this->get_columns();
114 $sortable = $this->get_sortable_columns();
115 $hidden = $this->get_hidden_columns();
116 $primary = $columns['summary'];
117
118 $this->_column_headers = array( $columns, $hidden, $sortable, $primary );
119
120 $this->items = $this->get_records();
121
122 $total_items = $this->get_total_found_rows();
123
124 $this->set_pagination_args(
125 array(
126 'total_items' => $total_items,
127 'per_page' => $this->get_items_per_page( 'edit_stream_per_page', 20 ),
128 )
129 );
130 }
131
132 function get_records() {
133 $args = array();
134
135 // Parse sorting params
136 if ( $order = wp_stream_filter_input( INPUT_GET, 'order' ) ) {
137 $args['order'] = $order;
138 }
139
140 if ( $orderby = wp_stream_filter_input( INPUT_GET, 'orderby' ) ) {
141 $args['orderby'] = $orderby;
142 }
143
144 $params = array(
145 'search',
146 'date',
147 'date_from',
148 'date_to',
149 'date_after',
150 'date_before',
151 );
152
153 foreach ( $params as $param ) {
154 $value = wp_stream_filter_input( INPUT_GET, $param );
155
156 if ( $value ) {
157 $args[ $param ] = $value;
158 }
159 }
160
161 // Additional filter properties
162 $properties = array(
163 'record',
164 'site_id',
165 'blog_id',
166 'object_id',
167 'user_id',
168 'user_role',
169 'ip',
170 'connector',
171 'context',
172 'action',
173 );
174
175 // Add property fields to defaults, including their __in/__not_in variations
176 foreach ( $properties as $property ) {
177 $value = wp_stream_filter_input( INPUT_GET, $property );
178
179 // Allow 0 values
180 if ( isset( $value ) && '' !== $value && false !== $value ) {
181 $args[ $property ] = $value;
182 }
183
184 $value_in = wp_stream_filter_input( INPUT_GET, $property . '__in' );
185
186 if ( $value_in ) {
187 $args[ $property . '__in' ] = explode( ',', $value_in );
188 }
189
190 $value_not_in = wp_stream_filter_input( INPUT_GET, $property . '__not_in' );
191
192 if ( $value_not_in ) {
193 $args[ $property . '__not_in' ] = explode( ',', $value_not_in );
194 }
195 }
196
197 $args['paged'] = $this->get_pagenum();
198
199 if ( isset( $args['context'] ) && 0 === strpos( $args['context'], 'group-' ) ) {
200 $args['connector'] = str_replace( 'group-', '', $args['context'] );
201 $args['context'] = '';
202 }
203
204 if ( ! isset( $args['records_per_page'] ) ) {
205 $args['records_per_page'] = $this->get_items_per_page( 'edit_stream_per_page', 20 );
206 }
207 $args['records_per_page'] = apply_filters( 'stream_records_per_page', $args['records_per_page'] );
208
209 $items = $this->plugin->db->get_records( $args );
210 return $items;
211 }
212
213 /**
214 * Get last query found rows
215 *
216 * @return integer
217 */
218 public function get_total_found_rows() {
219 return $this->plugin->db->get_found_records_count();
220 }
221
222 function column_default( $item, $column_name ) {
223 $out = '';
224 $record = new Record( $item );
225
226 switch ( $column_name ) {
227 case 'date' :
228 $created = date( 'Y-m-d H:i:s', strtotime( $record->created ) );
229 $date_string = sprintf(
230 '<time datetime="%s" class="relative-time record-created">%s</time>',
231 wp_stream_get_iso_8601_extended_date( strtotime( $record->created ) ),
232 get_date_from_gmt( $created, 'Y/m/d' )
233 );
234 $out = $this->column_link( $date_string, 'date', get_date_from_gmt( $created, 'Y/m/d' ) );
235 $out .= '<br />';
236 $out .= get_date_from_gmt( $created, 'h:i:s A' );
237 break;
238
239 case 'summary' :
240 $out = $record->summary;
241 $object_title = $record->get_object_title();
242 $view_all_text = $object_title ? sprintf( esc_html__( 'View all activity for "%s"', 'stream' ), esc_attr( $object_title ) ) : esc_html__( 'View all activity for this object', 'stream' );
243
244 if ( $record->object_id ) {
245 $out .= $this->column_link(
246 '<span class="dashicons dashicons-search stream-filter-object-id"></span>',
247 array(
248 'object_id' => $record->object_id,
249 'context' => $record->context,
250 ),
251 null,
252 esc_attr( $view_all_text )
253 );
254 }
255 $out .= $this->get_action_links( $record );
256 break;
257
258 case 'user_id' :
259 $user = new Author( (int) $record->user_id, (array) maybe_unserialize( $record->user_meta ) );
260
261 $filtered_records_url = add_query_arg(
262 array(
263 'page' => $this->plugin->admin->records_page_slug,
264 'user_id' => absint( $user->id ),
265 ),
266 self_admin_url( $this->plugin->admin->admin_parent_page )
267 );
268
269 $out = sprintf(
270 '<a href="%s">%s <span>%s</span></a>%s%s%s',
271 $filtered_records_url,
272 $user->get_avatar_img( 80 ),
273 $user->get_display_name(),
274 $user->is_deleted() ? sprintf( '<br /><small class="deleted">%s</small>', esc_html__( 'Deleted User', 'stream' ) ) : '',
275 sprintf( '<br /><small>%s</small>', $user->get_role() ),
276 sprintf( '<br /><small>%s</small>', $user->get_agent_label( $user->get_agent() ) )
277 );
278 break;
279
280 case 'context':
281 $connector_title = $this->get_term_title( $record->{'connector'}, 'connector' );
282 $context_title = $this->get_term_title( $record->{'context'}, 'context' );
283
284 $out = $this->column_link( $connector_title, 'connector', $item->{'connector'} );
285 $out .= '<br />&#8627;&nbsp;';
286 $out .= $this->column_link(
287 $context_title,
288 array(
289 'connector' => $record->{'connector'},
290 'context' => $record->{'context'},
291 )
292 );
293 break;
294
295 case 'action':
296 $out = $this->column_link( $this->get_term_title( $record->{$column_name}, $column_name ), $column_name, $record->{$column_name} );
297 break;
298
299 case 'blog_id':
300 $blog = ( $record->blog_id && is_multisite() ) ? get_blog_details( $record->blog_id ) : $this->plugin->admin->network->get_network_blog();
301 $out = $this->column_link( $blog->blogname, 'blog_id', $blog->blog_id );
302 break;
303
304 case 'ip' :
305 $out = $this->column_link( $record->{$column_name}, 'ip', $record->{$column_name} );
306 break;
307
308 default :
309 /**
310 * Registers new Columns to be inserted into the table. The cell contents of this column is set
311 * below with 'wp_stream_insert_column_default_'
312 *
313 * @return array
314 */
315 $inserted_columns = apply_filters( 'wp_stream_register_column_defaults', $new_columns = array() );
316
317 if ( ! empty( $inserted_columns ) && is_array( $inserted_columns ) ) {
318 foreach ( $inserted_columns as $column_title ) {
319 /**
320 * If column title inserted via wp_stream_register_column_defaults ($column_title) exists
321 * among columns registered with get_columns ($column_name) and there is an action associated
322 * with this column, do the action
323 *
324 * Also, note that the action name must include the $column_title registered
325 * with wp_stream_register_column_defaults
326 */
327 if ( $column_title === $column_name && has_filter( "wp_stream_insert_column_default_{$column_title}" ) ) {
328 /**
329 * Allows for the addition of content under a specified column.
330 *
331 * @param object $record Contents of the row
332 *
333 * @return string
334 */
335 $out = apply_filters( "wp_stream_insert_column_default_{$column_title}", $column_name, $record );
336 } else {
337 $out = $column_name;
338 }
339 }
340 } else {
341 $out = $column_name;
342 }
343 }
344
345 $allowed_tags = wp_kses_allowed_html( 'post' );
346 $allowed_tags['time'] = array( 'datetime' => true, 'class' => true );
347 $allowed_tags['img']['srcset'] = true;
348
349 echo wp_kses( $out, $allowed_tags );
350 }
351
352 public function get_action_links( $record ) {
353 $out = '';
354
355 /**
356 * Filter allows modification of action links for a specific connector
357 *
358 * @param array
359 * @param Record
360 *
361 * @return array Action links for this connector
362 */
363 $action_links = apply_filters( 'wp_stream_action_links_' . $record->connector, array(), $record );
364
365 /**
366 * Filter allows addition of custom links for a specific connector
367 *
368 * @param array
369 * @param Record
370 *
371 * @return array Custom links for this connector
372 */
373 $custom_links = apply_filters( 'wp_stream_custom_action_links_' . $record->connector, array(), $record );
374
375 if ( $action_links || $custom_links ) {
376 $out .= '<div class="row-actions">';
377 }
378
379 $links = array();
380 if ( $action_links && is_array( $action_links ) ) {
381 foreach ( $action_links as $al_title => $al_href ) {
382 $links[] = sprintf(
383 '<span><a href="%s" class="action-link">%s</a></span>',
384 $al_href,
385 $al_title
386 );
387 }
388 }
389
390 if ( $custom_links && is_array( $custom_links ) ) {
391 foreach ( $custom_links as $key => $link ) {
392 $links[] = $link;
393 }
394 }
395
396 $out .= implode( ' | ', $links );
397
398 if ( $action_links || $custom_links ) {
399 $out .= '</div>';
400 }
401
402 return $out;
403 }
404
405 function column_link( $display, $key, $value = null, $title = null ) {
406 $url = add_query_arg(
407 array(
408 'page' => $this->plugin->admin->records_page_slug,
409 ),
410 self_admin_url( $this->plugin->admin->admin_parent_page )
411 );
412
413 $args = ! is_array( $key ) ? array( $key => $value ) : $key;
414
415 foreach ( $args as $k => $v ) {
416 $url = add_query_arg( $k, $v, $url );
417 }
418
419 return sprintf(
420 '<a href="%s" title="%s">%s</a>',
421 esc_url( $url ),
422 esc_attr( $title ),
423 $display
424 );
425 }
426
427 public function get_term_title( $term, $type ) {
428 if ( ! isset( $this->plugin->connectors->term_labels[ "stream_$type" ][ $term ] ) ) {
429 return $term;
430 }
431
432 return $this->plugin->connectors->term_labels[ "stream_$type" ][ $term ];
433 }
434
435 /**
436 * Assembles records for display in search filters
437 *
438 * Gathers list of all users/connectors, then compares it to
439 * results of existing records. All items that do not exist in records
440 * get assigned a disabled value of "true".
441 *
442 * @param string $column List table column name
443 *
444 * @return array Options to be displayed in search filters
445 */
446 function assemble_records( $column ) {
447 // @todo eliminate special condition for authors, especially using a WP_User object as the value; should use string or stringifiable object
448 if ( 'user_id' === $column ) {
449 $all_records = array();
450
451 // If the number of users exceeds the max users constant value then return an empty array and use AJAX instead
452 $user_count = count_users();
453 $total_users = $user_count['total_users'];
454
455 if ( $total_users > $this->plugin->admin->preload_users_max ) {
456 $selected_user = wp_stream_filter_input( INPUT_GET, 'user_id' );
457 if ( $selected_user ) {
458 $user = new Author( $selected_user );
459 return array( $selected_user => $user->get_display_name() );
460 } else {
461 return array();
462 }
463 }
464
465 $users = array_map(
466 function( $user_id ) {
467 return new Author( $user_id );
468 },
469 get_users( array( 'fields' => 'ID' ) )
470 );
471
472 if ( is_multisite() && is_super_admin() ) {
473 $super_admins = array_map(
474 function( $login ) {
475 $user = get_user_by( 'login', $login );
476 return new Author( $user->ID );
477 },
478 get_super_admins()
479 );
480 $users = array_unique( array_merge( $users, $super_admins ) );
481 }
482
483 $users[] = new Author( 0, array( 'is_wp_cli' => true ) );
484
485 foreach ( $users as $user ) {
486 $all_records[ $user->id ] = $user->get_display_name();
487 }
488 } else {
489 $prefixed_column = sprintf( 'stream_%s', $column );
490 $all_records = $this->plugin->connectors->term_labels[ $prefixed_column ];
491 }
492
493 $existing_records = $this->plugin->db->existing_records( $column );
494 $active_records = array();
495 $disabled_records = array();
496
497 foreach ( $all_records as $record => $label ) {
498 if ( array_key_exists( $record, $existing_records ) ) {
499 $active_records[ $record ] = array( 'label' => $label, 'disabled' => '' );
500 } else {
501 $disabled_records[ $record ] = array( 'label' => $label, 'disabled' => 'disabled="disabled"' );
502 }
503 }
504
505 // Remove WP-CLI pseudo user if no records with user=0 exist
506 if ( isset( $disabled_records[0] ) ) {
507 unset( $disabled_records[0] );
508 }
509
510 $sort = function( $a, $b ) use ( $column ) {
511 $label_a = (string) $a['label'];
512 $label_b = (string) $b['label'];
513
514 if ( $label_a === $label_b ) {
515 return 0;
516 }
517
518 return ( strtolower( $label_a ) < strtolower( $label_b ) ) ? -1 : 1;
519 };
520
521 uasort( $active_records, $sort );
522 uasort( $disabled_records, $sort );
523
524 // Not using array_merge() in order to preserve the array index for the users dropdown which uses the user_id as the key
525 $all_records = $active_records + $disabled_records;
526
527 return $all_records;
528 }
529
530 public function get_filters() {
531 $filters = array();
532
533 $date_interval = new Date_Interval();
534
535 $filters['date'] = array(
536 'title' => __( 'dates', 'stream' ),
537 'items' => $date_interval->intervals,
538 );
539
540 $users = $this->get_users_dropdown_items(
541 $this->assemble_records( 'user_id' )
542 );
543
544 $filters['user_id'] = array(
545 'title' => __( 'users', 'stream' ),
546 'items' => $users,
547 'ajax' => count( $users ) <= 0,
548 );
549
550 $filters['context'] = array(
551 'title' => __( 'contexts', 'stream' ),
552 'items' => $this->assemble_records( 'context' ),
553 );
554
555 $filters['action'] = array(
556 'title' => __( 'actions', 'stream' ),
557 'items' => $this->assemble_records( 'action' ),
558 );
559
560 /**
561 * Filter allows additional filters in the list table dropdowns
562 * Note the format of the filters above, with they key and array
563 * containing a title and array of items.
564 *
565 * @return array
566 */
567 return apply_filters( 'wp_stream_list_table_filters', $filters );
568 }
569
570 function filters_form() {
571 $filters = $this->get_filters();
572
573 $filters_string = sprintf( '<input type="hidden" name="page" value="%s" />', 'wp_stream' );
574 $filters_string .= sprintf( '<span class="filter_info hidden">%s</span>', esc_html__( 'Show filter controls via the screen options tab above.', 'stream' ) );
575
576 foreach ( $filters as $name => $data ) {
577
578 $data = wp_parse_args( $data, array(
579 'title' => '',
580 'items' => array(),
581 'ajax' => false,
582 ) );
583
584 if ( 'date' === $name ) {
585 $filters_string .= $this->filter_date( $data['items'] );
586 } else {
587 if ( 'context' === $name ) {
588 // Add Connectors as parents, and apply the Contexts as children
589 $connectors = $this->assemble_records( 'connector' );
590 $context_items = array();
591
592 foreach ( $connectors as $connector => $item ) {
593 $context_items[ $connector ]['label'] = $item['label'];
594
595 foreach ( $data['items'] as $context_value => $context_item ) {
596 if ( isset( $this->plugin->connectors->contexts[ $connector ] ) && array_key_exists( $context_value, $this->plugin->connectors->contexts[ $connector ] ) ) {
597 $context_items[ $connector ]['children'][ $context_value ] = $context_item;
598 }
599 }
600
601 if ( isset( $context_items[ $connector ]['children'] ) ) {
602 $labels = wp_list_pluck( $context_items[ $connector ]['children'], 'label' );
603
604 // Sort child items by label
605 array_multisort( $labels, SORT_ASC, $context_items[ $connector ]['children'] );
606 }
607 }
608
609 foreach ( $context_items as $context_value => $context_item ) {
610 if ( ! isset( $context_item['children'] ) || empty( $context_item['children'] ) ) {
611 unset( $context_items[ $context_value ] );
612 }
613 }
614
615 $data['items'] = $context_items;
616
617 $labels = wp_list_pluck( $data['items'], 'label' );
618
619 // Sort top-level items by label
620 array_multisort( $labels, SORT_ASC, $data['items'] );
621
622 // Output a hidden input to handle the connector value
623 $filters_string .= sprintf(
624 '<input type="hidden" name="connector" class="record-filter-connector" value="%s" />',
625 esc_attr( wp_stream_filter_input( INPUT_GET, 'connector' ) )
626 );
627 }
628
629 $filters_string .= $this->filter_select( $name, $data['title'], $data['items'], $data['ajax'] );
630 }
631 }
632
633 $filters_string .= sprintf( '<input type="submit" id="record-query-submit" class="button" value="%s" />', __( 'Filter', 'stream' ) );
634
635 // Parse all query vars into an array
636 $query_vars = array();
637
638 if ( isset( $_SERVER['QUERY_STRING'] ) ) {
639 parse_str( urldecode( $_SERVER['QUERY_STRING'] ), $query_vars );
640 }
641
642 // Ignore certain query vars and query vars that are empty
643 foreach ( $query_vars as $query_var => $value ) {
644 if ( '' === $value || 'page' === $query_var || 'paged' === $query_var ) {
645 unset( $query_vars[ $query_var ] );
646 }
647 }
648
649 $url = add_query_arg(
650 array(
651 'page' => $this->plugin->admin->records_page_slug,
652 ),
653 self_admin_url( $this->plugin->admin->admin_parent_page )
654 );
655
656 // Display reset action if records are being filtered
657 if ( ! empty( $query_vars ) ) {
658 $filters_string .= sprintf( '<a href="%s" id="record-query-reset"><span class="dashicons dashicons-dismiss"></span> <span class="record-query-reset-text">%s</span></a>', esc_url( $url ), __( 'Reset filters', 'stream' ) );
659 }
660
661 return sprintf( '<div class="alignleft actions">%s</div>', $filters_string ); // xss ok
662 }
663
664 function filter_select( $name, $title, $items, $ajax = false ) {
665 if ( $ajax ) {
666 $out = sprintf(
667 '<input type="hidden" name="%s" class="chosen-select" value="%s" data-placeholder="%s" />',
668 esc_attr( $name ),
669 esc_attr( wp_stream_filter_input( INPUT_GET, $name ) ),
670 esc_attr( $title )
671 );
672 } else {
673 $options = array( '<option value=""></option>' );
674 $selected = wp_stream_filter_input( INPUT_GET, $name );
675
676 foreach ( $items as $key => $item ) {
677 $value = isset( $item['children'] ) ? 'group-' . $key : $key;
678 $option_args = array(
679 'value' => $value,
680 'selected' => selected( $value, $selected, false ),
681 'disabled' => isset( $item['disabled'] ) ? $item['disabled'] : null,
682 'icon' => isset( $item['icon'] ) ? $item['icon'] : null,
683 'group' => isset( $item['children'] ) ? $key : null,
684 'tooltip' => isset( $item['tooltip'] ) ? $item['tooltip'] : null,
685 'class' => isset( $item['children'] ) ? 'level-1' : null,
686 'label' => isset( $item['label'] ) ? $item['label'] : null,
687 );
688 $options[] = $this->filter_option( $option_args );
689
690 if ( isset( $item['children'] ) ) {
691 foreach ( $item['children'] as $child_value => $child_item ) {
692 $option_args = array(
693 'value' => $child_value,
694 'selected' => selected( $child_value, $selected, false ),
695 'disabled' => isset( $child_item['disabled'] ) ? $child_item['disabled'] : null,
696 'icon' => isset( $child_item['icon'] ) ? $child_item['icon'] : null,
697 'group' => $key,
698 'tooltip' => isset( $child_item['tooltip'] ) ? $child_item['tooltip'] : null,
699 'class' => 'level-2',
700 'label' => isset( $child_item['label'] ) ? '- ' . $child_item['label'] : null,
701 );
702 $options[] = $this->filter_option( $option_args );
703 }
704 }
705 }
706 $out = sprintf(
707 '<select name="%s" class="chosen-select" data-placeholder="%s">%s</select>',
708 esc_attr( $name ),
709 sprintf( esc_attr__( 'Show all %s', 'stream' ), $title ),
710 implode( '', $options )
711 );
712 }
713
714 return $out;
715 }
716
717 function filter_option( $args ) {
718 $defaults = array(
719 'value' => null,
720 'selected' => null,
721 'disabled' => null,
722 'icon' => null,
723 'group' => null,
724 'tooltip' => null,
725 'class' => null,
726 'label' => null,
727 );
728 wp_parse_args( $args, $defaults );
729
730 return sprintf(
731 '<option value="%s" %s %s %s %s %s class="%s">%s</option>',
732 esc_attr( $args['value'] ),
733 $args['selected'],
734 $args['disabled'],
735 $args['icon'] ? sprintf( 'data-icon="%s"', esc_attr( $args['icon'] ) ) : null,
736 $args['group'] ? sprintf( 'data-group="%s"', esc_attr( $args['group'] ) ) : null,
737 $args['tooltip'] ? sprintf( 'title="%s"', esc_attr( $args['tooltip'] ) ) : null,
738 $args['class'] ? esc_attr( $args['class'] ) : null,
739 esc_html( $args['label'] )
740 );
741 }
742
743 function filter_search() {
744 $search = null;
745 if ( isset( $_GET['search'] ) ) {
746 // @TODO: Make this pass phpcs
747 $search = esc_attr( wp_unslash( $_GET['search'] ) ); // input var okay
748 }
749 $out = sprintf(
750 '<p class="search-box">
751 <label class="screen-reader-text" for="record-search-input">%1$s:</label>
752 <input type="search" id="record-search-input" name="search" value="%2$s" />
753 <input type="submit" name="" id="search-submit" class="button" value="%1$s" />
754 </p>',
755 esc_attr__( 'Search Records', 'stream' ),
756 $search
757 );
758
759 return $out;
760 }
761
762 function filter_date( $items ) {
763 wp_enqueue_style( 'jquery-ui' );
764 wp_enqueue_style( 'wp-stream-datepicker' );
765 wp_enqueue_script( 'jquery-ui-datepicker' );
766
767 $date_predefined = wp_stream_filter_input( INPUT_GET, 'date_predefined' );
768 $date_from = wp_stream_filter_input( INPUT_GET, 'date_from' );
769 $date_to = wp_stream_filter_input( INPUT_GET, 'date_to' );
770
771 ob_start();
772 ?>
773 <div class="date-interval">
774
775 <select class="field-predefined hide-if-no-js" name="date_predefined" data-placeholder="<?php esc_attr_e( 'All Time', 'stream' ) ?>">
776 <option></option>
777 <option value="custom" <?php selected( 'custom' === $date_predefined ); ?>><?php esc_attr_e( 'Custom', 'stream' ) ?></option>
778 <?php
779 foreach ( $items as $key => $interval ) {
780 $end = isset( $interval['end'] ) ? $interval['end']->format( 'Y/m/d' ) : null;
781
782 printf(
783 '<option value="%s" data-from="%s" data-to="%s" %s>%s</option>',
784 esc_attr( $key ),
785 esc_attr( $interval['start']->format( 'Y/m/d' ) ),
786 esc_attr( $end ),
787 selected( $key === $date_predefined ),
788 esc_html( $interval['label'] )
789 );
790 }
791 ?>
792 </select>
793
794 <div class="date-inputs">
795 <div class="box">
796 <i class="date-remove dashicons"></i>
797 <input type="text" name="date_from" class="date-picker field-from" placeholder="<?php esc_attr_e( 'Start Date', 'stream' ) ?>" value="<?php echo esc_attr( $date_from ) ?>" />
798 </div>
799 <span class="connector dashicons"></span>
800
801 <div class="box">
802 <i class="date-remove dashicons"></i>
803 <input type="text" name="date_to" class="date-picker field-to" placeholder="<?php esc_attr_e( 'End Date', 'stream' ) ?>" value="<?php echo esc_attr( $date_to ) ?>" />
804 </div>
805 </div>
806
807 </div>
808 <?php
809
810 return ob_get_clean();
811 }
812
813 /**
814 * Output a Select dropdown of actions relating to the Stream records
815 *
816 * @return string
817 */
818 function record_actions_form() {
819 /**
820 * Filter the records screen actions dropdown menu
821 *
822 * @return array Should be in the format of action_slug => 'Action Name'
823 */
824 $actions = apply_filters( 'wp_stream_record_actions_menu', array() );
825
826 if ( empty( $actions ) ) {
827 return '';
828 }
829
830 ob_start();
831 printf( '<div class="alignleft actions recordactions"><select name="%s">', esc_attr( 'record-actions' ) );
832 printf( '<option value="">%s</option>', esc_attr__( 'Record Actions', 'stream' ) );
833 foreach ( $actions as $value => $name ) {
834 printf(
835 '<option value="%s">%s</option>',
836 esc_attr( $value ),
837 esc_attr( $name )
838 );
839 }
840 echo '</select></div>';
841 wp_nonce_field( 'stream_record_actions_nonce', 'stream_record_actions_nonce' );
842
843 printf( '<input type="hidden" name="page" value="%s">', esc_attr( wp_stream_filter_input( INPUT_GET, 'page' ) ) );
844 printf( '<input type="hidden" name="date_predefined" value="%s">', esc_attr( wp_stream_filter_input( INPUT_GET, 'date_predefined' ) ) );
845 printf( '<input type="hidden" name="date_from" value="%s">', esc_attr( wp_stream_filter_input( INPUT_GET, 'date_from' ) ) );
846 printf( '<input type="hidden" name="date_to" value="%s">', esc_attr( wp_stream_filter_input( INPUT_GET, 'date_to' ) ) );
847 printf( '<input type="hidden" name="user_id" value="%s">', esc_attr( wp_stream_filter_input( INPUT_GET, 'user_id' ) ) );
848 printf( '<input type="hidden" name="connector" value="%s">', esc_attr( wp_stream_filter_input( INPUT_GET, 'connector' ) ) );
849 printf( '<input type="hidden" name="context" value="%s">', esc_attr( wp_stream_filter_input( INPUT_GET, 'context' ) ) );
850 printf( '<input type="hidden" name="action" value="%s">', esc_attr( wp_stream_filter_input( INPUT_GET, 'action' ) ) );
851
852 printf( '<input type="submit" name="" id="record-actions-submit" class="button" value="%s">', esc_attr__( 'Apply', 'stream' ) );
853 echo '<div class="clear"></div>';
854
855 return ob_get_clean();
856 }
857
858 function display() {
859 $url = self_admin_url( $this->plugin->admin->admin_parent_page );
860
861 echo '<form method="get" action="' . esc_url( $url ) . '" id="record-filter-form">';
862 echo $this->filter_search(); // xss ok
863 parent::display();
864 echo '</form>';
865
866 echo '<form method="get" action="' . esc_url( $url ) . '" id="record-actions-form">';
867 echo $this->record_actions_form(); // xss ok
868 echo '</form>';
869 }
870
871 function single_row( $item ) {
872 $classes = apply_filters( 'wp_stream_record_classes', array(), $item );
873 $class_string = '';
874 if ( ! empty( $classes ) ) {
875 $class_string = ' class="' . esc_attr( join( ' ', $classes ) ) . '"';
876 }
877
878 echo sprintf( '<tr%s>', $class_string ); // xss ok
879 $this->single_row_columns( $item );
880 echo '</tr>';
881 }
882
883 function display_tablenav( $which ) {
884 if ( 'top' === $which ) : ?>
885 <div class="tablenav <?php echo esc_attr( $which ); ?>">
886 <?php
887 $this->pagination( $which );
888 $this->extra_tablenav( $which );
889 ?>
890
891 <br class="clear" />
892 </div>
893 <?php else : ?>
894 <div class="tablenav <?php echo esc_attr( $which ); ?>">
895 <?php
896 /**
897 * Fires after the list table is displayed.
898 */
899 do_action( 'wp_stream_after_list_table' );
900 $this->pagination( $which );
901 $this->extra_tablenav( $which );
902 ?>
903
904 <br class="clear" />
905 </div>
906 <?php
907 endif;
908 }
909
910 function set_screen_option( $dummy, $option, $value ) {
911 if ( 'edit_stream_per_page' === $option ) {
912 return $value;
913 } else {
914 return $dummy;
915 }
916 }
917
918 function set_live_update_option( $dummy, $option, $value ) {
919 unset( $value );
920
921 // @codingStandardsIgnoreStart
922 if (
923 $this->plugin->admin->live_update->user_meta_key === $option
924 &&
925 isset( $_POST[ $this->plugin->admin->live_update->user_meta_key ] )
926 ) {
927 $value = esc_attr( $_POST[ $this->plugin->admin->live_update->user_meta_key ] ); //input var okay
928
929 return $value;
930 }
931 // @codingStandardsIgnoreEnd
932
933 return $dummy;
934 }
935
936 public function screen_controls( $status, $args ) {
937 unset( $status );
938 unset( $args );
939
940 $user_id = get_current_user_id();
941 $option = $this->plugin->admin->get_user_meta( $user_id, $this->plugin->admin->live_update->user_meta_key, true );
942 $heartbeat = wp_script_is( 'heartbeat', 'done' ) ? 'true' : 'false';
943
944 if ( 'on' === $option && 'false' === $heartbeat ) {
945 $option = 'off';
946
947 $this->plugin->admin->update_user_meta( $user_id, $this->plugin->admin->live_update->user_meta_key, 'off' );
948 }
949
950 $nonce = wp_create_nonce( $this->plugin->admin->live_update->user_meta_key . '_nonce' );
951
952 ob_start();
953 ?>
954 <fieldset>
955 <h5><?php esc_html_e( 'Live updates', 'stream' ) ?></h5>
956
957 <div>
958 <input type="hidden" name="stream_live_update_nonce" id="stream_live_update_nonce" value="<?php echo esc_attr( $nonce ) ?>" />
959 </div>
960 <div>
961 <input type="hidden" name="enable_live_update_user" id="enable_live_update_user" value="<?php echo absint( $user_id ) ?>" />
962 </div>
963 <div class="metabox-prefs stream-live-update-checkbox">
964 <label for="enable_live_update">
965 <input type="checkbox" value="on" name="enable_live_update" id="enable_live_update" data-heartbeat="<?php echo esc_attr( $heartbeat ) ?>" <?php checked( $option, 'on' ) ?> />
966 <?php esc_html_e( 'Enabled', 'stream' ) ?><span class="spinner"></span>
967 </label>
968 </div>
969 </fieldset>
970 <?php
971 return ob_get_clean();
972 }
973
974 /**
975 * This function is use to map List table column name with excluded setting keys
976 *
977 * @param string $column List table column name
978 *
979 * @return string setting name for that column
980 */
981 function get_column_excluded_setting_key( $column ) {
982 switch ( $column ) {
983 case 'connector':
984 $output = 'connectors';
985 break;
986 case 'context':
987 $output = 'contexts';
988 break;
989 case 'action':
990 $output = 'action';
991 break;
992 case 'ip':
993 $output = 'ip_addresses';
994 break;
995 case 'user_id':
996 $output = 'users';
997 break;
998 default:
999 $output = false;
1000 }
1001
1002 return $output;
1003 }
1004
1005 /**
1006 * Get users as dropdown items
1007 *
1008 * @param array $users
1009 *
1010 * @return array
1011 */
1012 public function get_users_dropdown_items( $users ) {
1013 $record_meta = array();
1014
1015 foreach ( $users as $user_id => $args ) {
1016 $user = new Author( $user_id );
1017 $disabled = isset( $args['disabled'] ) ? $args['disabled'] : null;
1018
1019 $record_meta[ $user_id ] = array(
1020 'text' => $user->get_display_name(),
1021 'id' => $user_id,
1022 'label' => $user->get_display_name(),
1023 'icon' => $user->get_avatar_src( 32 ),
1024 'title' => '',
1025 'disabled' => $disabled,
1026 );
1027 }
1028
1029 return $record_meta;
1030 }
1031 }
1032