PluginProbe
Stream – Activity Log & Audit Trail / 3.3.0
Stream – Activity Log & Audit Trail v3.3.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.3.0, at classes/class-list-table.php

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