PluginProbe
Stream – Activity Log & Audit Trail / 3.0.5
Stream – Activity Log & Audit Trail v3.0.5
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-settings.php

class-settings.php in Stream – Activity Log & Audit Trail 3.0.5, at classes/class-settings.php

1,035 lines 31.8 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 use \WP_Roles;
5 use \WP_User;
6 use \WP_User_Query;
7
8 class Settings {
9 /**
10 * Hold Plugin class
11 * @var Plugin
12 */
13 public $plugin;
14
15 /**
16 * Settings key/identifier
17 *
18 * @var string
19 */
20 public $option_key = 'wp_stream';
21
22 /**
23 * Network settings key/identifier
24 *
25 * @var string
26 */
27 public $network_options_key = 'wp_stream_network';
28
29 /**
30 * Plugin settings
31 *
32 * @var array
33 */
34 public $options = array();
35
36 /**
37 * Settings fields
38 *
39 * @var array
40 */
41 public $fields = array();
42
43 /**
44 * Class constructor.
45 *
46 * @param Plugin $plugin The main Plugin class.
47 */
48 public function __construct( $plugin ) {
49 $this->plugin = $plugin;
50
51 $this->option_key = $this->get_option_key();
52 $this->options = $this->get_options();
53
54 // Register settings, and fields
55 add_action( 'admin_init', array( $this, 'register_settings' ) );
56
57 // Remove records when records TTL is shortened
58 add_action( 'update_option_' . $this->option_key, array( $this, 'updated_option_ttl_remove_records' ), 10, 2 );
59
60 // Apply label translations for settings
61 add_filter( 'wp_stream_serialized_labels', array( $this, 'get_settings_translations' ) );
62
63 // Ajax callback function to search users
64 add_action( 'wp_ajax_stream_get_users', array( $this, 'get_users' ) );
65
66 // Ajax callback function to search IPs
67 add_action( 'wp_ajax_stream_get_ips', array( $this, 'get_ips' ) );
68 }
69
70 /**
71 * Ajax callback function to search users, used on exclude setting page
72 *
73 * @uses \WP_User_Query
74 */
75 public function get_users() {
76 if ( ! defined( 'DOING_AJAX' ) || ! current_user_can( $this->plugin->admin->settings_cap ) ) {
77 return;
78 }
79
80 check_ajax_referer( 'stream_get_users', 'nonce' );
81
82 $response = (object) array(
83 'status' => false,
84 'message' => esc_html__( 'There was an error in the request', 'stream' ),
85 );
86
87 $search = '';
88 if ( isset( $_POST['find'] ) ) {
89 // @TODO: Make this pass phpcs
90 $search = esc_html( wp_unslash( trim( $_POST['find'] ) ) ); // input var okay
91 }
92 $request = (object) array(
93 'find' => $search,
94 );
95
96 add_filter( 'user_search_columns', array( $this, 'add_display_name_search_columns' ), 10, 3 );
97
98 $users = new WP_User_Query(
99 array(
100 'search' => "*{$request->find}*",
101 'search_columns' => array(
102 'user_login',
103 'user_nicename',
104 'user_email',
105 'user_url',
106 ),
107 'orderby' => 'display_name',
108 'number' => $this->plugin->admin->preload_users_max,
109 )
110 );
111
112 remove_filter( 'user_search_columns', array( $this, 'add_display_name_search_columns' ), 10 );
113
114 if ( 0 === $users->get_total() ) {
115 wp_send_json_error( $response );
116 }
117
118 $response->status = true;
119 $response->message = '';
120 $response->users = array();
121
122 foreach ( $users->results as $key => $user ) {
123 $author = new Author( $user->ID );
124
125 $args = array(
126 'id' => $author->ID,
127 'text' => $author->display_name,
128 );
129
130 $args['tooltip'] = esc_attr(
131 sprintf(
132 __( "ID: %d\nUser: %s\nEmail: %s\nRole: %s", 'stream' ),
133 $author->id,
134 $author->user_login,
135 $author->user_email,
136 ucwords( $author->get_role() )
137 )
138 );
139
140 $args['icon'] = $author->get_avatar_src( 32 );
141
142 $response->users[] = $args;
143 }
144
145 if ( empty( $search ) || preg_match( '/wp|cli|system|unknown/i', $search ) ) {
146 $author = new Author( 0 );
147 $response->users[] = array(
148 'id' => $author->id,
149 'text' => $author->get_display_name(),
150 'icon' => $author->get_avatar_src( 32 ),
151 'tooltip' => esc_html__( 'Actions performed by the system when a user is not logged in (e.g. auto site upgrader, or invoking WP-CLI without --user)', 'stream' ),
152 );
153 }
154
155 wp_send_json_success( $response );
156 }
157
158 /**
159 * Ajax callback function to search IP addresses, used on exclude setting page
160 */
161 public function get_ips() {
162 if ( ! defined( 'DOING_AJAX' ) || ! current_user_can( $this->plugin->admin->settings_cap ) ) {
163 return;
164 }
165
166 check_ajax_referer( 'stream_get_ips', 'nonce' );
167
168 $ips = $this->plugin->db->existing_records( 'ip' );
169
170 if ( $ips ) {
171 wp_send_json_success( $ips );
172 } else {
173 wp_send_json_error();
174 }
175 }
176
177 /**
178 * Filter the columns to search in a WP_User_Query search.
179 *
180 * @param array $search_columns Array of column names to be searched.
181 * @param string $search Text being searched.
182 * @param \WP_User_Query $query current WP_User_Query instance.
183 *
184 * @return array
185 */
186 public function add_display_name_search_columns( $search_columns, $search, $query ) {
187 unset( $search );
188 unset( $query );
189
190 $search_columns[] = 'display_name';
191
192 return $search_columns;
193 }
194
195 /**
196 * Returns the option key
197 *
198 * @return string
199 */
200 public function get_option_key() {
201 $option_key = $this->option_key;
202
203 $current_page = wp_stream_filter_input( INPUT_GET, 'page' );
204
205 if ( ! $current_page ) {
206 $current_page = wp_stream_filter_input( INPUT_GET, 'action' );
207 }
208
209 if ( 'wp_stream_network_settings' === $current_page ) {
210 $option_key = $this->network_options_key;
211 }
212
213 return apply_filters( 'wp_stream_settings_option_key', $option_key );
214 }
215
216 /**
217 * Return settings fields
218 *
219 * @return array
220 */
221 public function get_fields() {
222 $fields = array(
223 'general' => array(
224 'title' => esc_html__( 'General', 'stream' ),
225 'fields' => array(
226 array(
227 'name' => 'role_access',
228 'title' => esc_html__( 'Role Access', 'stream' ),
229 'type' => 'multi_checkbox',
230 'desc' => esc_html__( 'Users from the selected roles above will have permission to view Stream Records. However, only site Administrators can access Stream Settings.', 'stream' ),
231 'choices' => $this->get_roles(),
232 'default' => array( 'administrator' ),
233 ),
234 array(
235 'name' => 'records_ttl',
236 'title' => esc_html__( 'Keep Records for', 'stream' ),
237 'type' => 'number',
238 'class' => 'small-text',
239 'desc' => esc_html__( 'Maximum number of days to keep activity records.', 'stream' ),
240 'default' => 30,
241 'min' => 1,
242 'max' => 999,
243 'step' => 1,
244 'after_field' => esc_html__( 'days', 'stream' ),
245 ),
246 array(
247 'name' => 'keep_records_indefinitely',
248 'title' => esc_html__( 'Keep Records Indefinitely', 'stream' ),
249 'type' => 'checkbox',
250 'desc' => sprintf( '<strong>%s</strong> %s', esc_html__( 'Not recommended.', 'stream' ), esc_html__( 'Purging old records helps to keep your WordPress installation running optimally.', 'stream' ) ),
251 'after_field' => esc_html__( 'Enabled', 'stream' ),
252 'default' => 0,
253 ),
254 ),
255 ),
256 'exclude' => array(
257 'title' => esc_html__( 'Exclude', 'stream' ),
258 'fields' => array(
259 array(
260 'name' => 'rules',
261 'title' => esc_html__( 'Exclude Rules', 'stream' ),
262 'type' => 'rule_list',
263 'desc' => esc_html__( 'Create rules to exclude certain kinds of activity from being recorded by Stream.', 'stream' ),
264 'default' => array(),
265 'nonce' => 'stream_get_ips',
266 ),
267 ),
268 ),
269 'advanced' => array(
270 'title' => esc_html__( 'Advanced', 'stream' ),
271 'fields' => array(
272 array(
273 'name' => 'comment_flood_tracking',
274 'title' => esc_html__( 'Comment Flood Tracking', 'stream' ),
275 'type' => 'checkbox',
276 'desc' => esc_html__( 'WordPress will automatically prevent duplicate comments from flooding the database. By default, Stream does not track these attempts unless you opt-in here. Enabling this is not necessary or recommended for most sites.', 'stream' ),
277 'after_field' => esc_html__( 'Enabled', 'stream' ),
278 'default' => 0,
279 ),
280 array(
281 'name' => 'delete_all_records',
282 'title' => esc_html__( 'Reset Stream Database', 'stream' ),
283 'type' => 'link',
284 'href' => add_query_arg(
285 array(
286 'action' => 'wp_stream_reset',
287 'wp_stream_nonce' => wp_create_nonce( 'stream_nonce' ),
288 ),
289 admin_url( 'admin-ajax.php' )
290 ),
291 'class' => 'warning',
292 'desc' => esc_html__( 'Warning: This will delete all activity records from the database.', 'stream' ),
293 'default' => 0,
294 'sticky' => 'bottom',
295 ),
296 ),
297 ),
298 );
299
300 // If Akismet is active, allow Admins to opt-in to Akismet tracking
301 if ( class_exists( 'Akismet' ) ) {
302 $akismet_tracking = array(
303 'name' => 'akismet_tracking',
304 'title' => esc_html__( 'Akismet Tracking', 'stream' ),
305 'type' => 'checkbox',
306 'desc' => esc_html__( 'Akismet already keeps statistics for comment attempts that it blocks as SPAM. By default, Stream does not track these attempts unless you opt-in here. Enabling this is not necessary or recommended for most sites.', 'stream' ),
307 'after_field' => esc_html__( 'Enabled', 'stream' ),
308 'default' => 0,
309 );
310
311 array_push( $fields['advanced']['fields'], $akismet_tracking );
312 }
313
314 // If WP Cron is enabled, allow Admins to opt-in to WP Cron tracking
315 if ( wp_stream_is_cron_enabled() ) {
316 $wp_cron_tracking = array(
317 'name' => 'wp_cron_tracking',
318 'title' => esc_html__( 'WP Cron Tracking', 'stream' ),
319 'type' => 'checkbox',
320 'desc' => esc_html__( 'By default, Stream does not track activity performed by WordPress cron events unless you opt-in here. Enabling this is not necessary or recommended for most sites.', 'stream' ),
321 'after_field' => esc_html__( 'Enabled', 'stream' ),
322 'default' => 0,
323 );
324
325 array_push( $fields['advanced']['fields'], $wp_cron_tracking );
326 }
327
328 /**
329 * Filter allows for modification of options fields
330 *
331 * @return array Array of option fields
332 */
333 $this->fields = apply_filters( 'wp_stream_settings_option_fields', $fields );
334
335 // Sort option fields in each tab by title ASC
336 foreach ( $this->fields as $tab => $options ) {
337 $titles = array();
338
339 foreach ( $options['fields'] as $field ) {
340 $prefix = null;
341
342 if ( ! empty( $field['sticky'] ) ) {
343 $prefix = ( 'bottom' === $field['sticky'] ) ? 'ZZZ' : 'AAA';
344 }
345
346 $titles[] = $prefix . $field['title'];
347 }
348
349 array_multisort( $titles, SORT_ASC, $this->fields[ $tab ]['fields'] );
350 }
351
352 return $this->fields;
353 }
354
355 /**
356 * Returns a list of options based on the current screen.
357 *
358 * @return array
359 */
360 public function get_options() {
361 $option_key = $this->option_key;
362 $defaults = $this->get_defaults( $option_key );
363
364 /**
365 * Filter allows for modification of options
366 *
367 * @param array
368 *
369 * @return array Updated array of options
370 */
371 return apply_filters(
372 'wp_stream_settings_options',
373 wp_parse_args(
374 is_network_admin() ? (array) get_site_option( $option_key, array() ) : (array) get_option( $option_key, array() ),
375 $defaults
376 ),
377 $option_key
378 );
379 }
380
381 /**
382 * Iterate through registered fields and extract default values
383 *
384 * @return array
385 */
386 public function get_defaults() {
387 $fields = $this->get_fields();
388 $defaults = array();
389
390 foreach ( $fields as $section_name => $section ) {
391 foreach ( $section['fields'] as $field ) {
392 $defaults[ $section_name . '_' . $field['name'] ] = isset( $field['default'] ) ? $field['default'] : null;
393 }
394 }
395
396 return (array) $defaults;
397 }
398
399 /**
400 * Registers settings fields and sections
401 *
402 * @return void
403 */
404 public function register_settings() {
405 $sections = $this->get_fields();
406
407 register_setting( $this->option_key, $this->option_key, array( $this, 'sanitize_settings' ) );
408
409 foreach ( $sections as $section_name => $section ) {
410 add_settings_section(
411 $section_name,
412 null,
413 '__return_false',
414 $this->option_key
415 );
416
417 foreach ( $section['fields'] as $field_idx => $field ) {
418 if ( ! isset( $field['type'] ) ) { // No field type associated, skip, no GUI
419 continue;
420 }
421
422 add_settings_field(
423 $field['name'],
424 $field['title'],
425 ( isset( $field['callback'] ) ? $field['callback'] : array( $this, 'output_field' ) ),
426 $this->option_key,
427 $section_name,
428 $field + array(
429 'section' => $section_name,
430 'label_for' => sprintf( '%s_%s_%s', $this->option_key, $section_name, $field['name'] ), // xss ok
431 )
432 );
433 }
434 }
435 }
436
437 /**
438 * Sanitization callback for settings field values before save
439 *
440 * @param array $input
441 *
442 * @return array
443 */
444 public function sanitize_settings( $input ) {
445 $output = array();
446 $sections = $this->get_fields();
447
448 foreach ( $sections as $section => $data ) {
449 if ( empty( $data['fields'] ) || ! is_array( $data['fields'] ) ) {
450 continue;
451 }
452
453 foreach ( $data['fields'] as $field ) {
454 $type = ! empty( $field['type'] ) ? $field['type'] : null;
455 $name = ! empty( $field['name'] ) ? sprintf( '%s_%s', $section, $field['name'] ) : null;
456
457 if ( empty( $type ) || ! isset( $input[ $name ] ) || '' === $input[ $name ] ) {
458 continue;
459 }
460
461 // Sanitize depending on the type of field
462 switch ( $type ) {
463 case 'number':
464 $output[ $name ] = is_numeric( $input[ $name ] ) ? intval( trim( $input[ $name ] ) ) : '';
465 break;
466 case 'checkbox':
467 $output[ $name ] = is_numeric( $input[ $name ] ) ? absint( trim( $input[ $name ] ) ) : '';
468 break;
469 default:
470 if ( is_array( $input[ $name ] ) ) {
471 $output[ $name ] = $input[ $name ];
472
473 // Support all values in multidimentional arrays too
474 array_walk_recursive( $output[ $name ], function( &$v, $k ) {
475 $v = trim( $v );
476 } );
477 } else {
478 $output[ $name ] = trim( $input[ $name ] );
479 }
480 }
481 }
482 }
483
484 return $output;
485 }
486
487 /**
488 * Compile HTML needed for displaying the field
489 *
490 * @param array $field Field settings
491 *
492 * @return string HTML to be displayed
493 */
494 public function render_field( $field ) {
495 $output = null;
496 $type = isset( $field['type'] ) ? $field['type'] : null;
497 $section = isset( $field['section'] ) ? $field['section'] : null;
498 $name = isset( $field['name'] ) ? $field['name'] : null;
499 $class = isset( $field['class'] ) ? $field['class'] : null;
500 $placeholder = isset( $field['placeholder'] ) ? $field['placeholder'] : null;
501 $description = isset( $field['desc'] ) ? $field['desc'] : null;
502 $href = isset( $field['href'] ) ? $field['href'] : null;
503 $rows = isset( $field['rows'] ) ? $field['rows'] : 10;
504 $cols = isset( $field['cols'] ) ? $field['cols'] : 50;
505 $after_field = isset( $field['after_field'] ) ? $field['after_field'] : null;
506 $default = isset( $field['default'] ) ? $field['default'] : null;
507 $min = isset( $field['min'] ) ? $field['min'] : 0;
508 $max = isset( $field['max'] ) ? $field['max'] : 999;
509 $step = isset( $field['step'] ) ? $field['step'] : 1;
510 $title = isset( $field['title'] ) ? $field['title'] : null;
511 $nonce = isset( $field['nonce'] ) ? $field['nonce'] : null;
512
513 if ( isset( $field['value'] ) ) {
514 $current_value = $field['value'];
515 } else {
516 if ( isset( $this->options[ $section . '_' . $name ] ) ) {
517 $current_value = $this->options[ $section . '_' . $name ];
518 } else {
519 $current_value = null;
520 }
521 }
522
523 $option_key = $this->option_key;
524
525 if ( is_callable( $current_value ) ) {
526 $current_value = call_user_func( $current_value );
527 }
528
529 if ( ! $type || ! $section || ! $name ) {
530 return '';
531 }
532
533 if ( 'multi_checkbox' === $type
534 && ( empty( $field['choices'] ) || ! is_array( $field['choices'] ) )
535 ) {
536 return '';
537 }
538
539 switch ( $type ) {
540 case 'text':
541 case 'number':
542 $output = sprintf(
543 '<input type="%1$s" name="%2$s[%3$s_%4$s]" id="%2$s_%3$s_%4$s" class="%5$s" placeholder="%6$s" min="%7$d" max="%8$d" step="%9$d" value="%10$s" /> %11$s',
544 esc_attr( $type ),
545 esc_attr( $option_key ),
546 esc_attr( $section ),
547 esc_attr( $name ),
548 esc_attr( $class ),
549 esc_attr( $placeholder ),
550 esc_attr( $min ),
551 esc_attr( $max ),
552 esc_attr( $step ),
553 esc_attr( $current_value ),
554 wp_kses_post( $after_field )
555 );
556 break;
557 case 'textarea':
558 $output = sprintf(
559 '<textarea name="%1$s[%2$s_%3$s]" id="%1$s_%2$s_%3$s" class="%4$s" placeholder="%5$s" rows="%6$d" cols="%7$d">%8$s</textarea> %9$s',
560 esc_attr( $option_key ),
561 esc_attr( $section ),
562 esc_attr( $name ),
563 esc_attr( $class ),
564 esc_attr( $placeholder ),
565 absint( $rows ),
566 absint( $cols ),
567 esc_textarea( $current_value ),
568 wp_kses_post( $after_field )
569 );
570 break;
571 case 'checkbox':
572 if ( isset( $current_value ) ) {
573 $value = $current_value;
574 } elseif ( isset( $default ) ) {
575 $value = $default;
576 } else {
577 $value = 0;
578 }
579
580 $output = sprintf(
581 '<label><input type="checkbox" name="%1$s[%2$s_%3$s]" id="%1$s[%2$s_%3$s]" value="1" %4$s /> %5$s</label>',
582 esc_attr( $option_key ),
583 esc_attr( $section ),
584 esc_attr( $name ),
585 checked( $value, 1, false ),
586 wp_kses_post( $after_field )
587 );
588 break;
589 case 'multi_checkbox':
590 $output = sprintf(
591 '<div id="%1$s[%2$s_%3$s]"><fieldset>',
592 esc_attr( $option_key ),
593 esc_attr( $section ),
594 esc_attr( $name )
595 );
596 // Fallback if nothing is selected
597 $output .= sprintf(
598 '<input type="hidden" name="%1$s[%2$s_%3$s][]" value="__placeholder__" />',
599 esc_attr( $option_key ),
600 esc_attr( $section ),
601 esc_attr( $name )
602 );
603 $current_value = (array) $current_value;
604 $choices = $field['choices'];
605 if ( is_callable( $choices ) ) {
606 $choices = call_user_func( $choices );
607 }
608 foreach ( $choices as $value => $label ) {
609 $output .= sprintf(
610 '<label>%1$s <span>%2$s</span></label><br />',
611 sprintf(
612 '<input type="checkbox" name="%1$s[%2$s_%3$s][]" value="%4$s" %5$s />',
613 esc_attr( $option_key ),
614 esc_attr( $section ),
615 esc_attr( $name ),
616 esc_attr( $value ),
617 checked( in_array( $value, $current_value, true ), true, false )
618 ),
619 esc_html( $label )
620 );
621 }
622 $output .= '</fieldset></div>';
623 break;
624 case 'select':
625 $current_value = $this->options[ $section . '_' . $name ];
626 $default_value = isset( $default['value'] ) ? $default['value'] : '-1';
627 $default_name = isset( $default['name'] ) ? $default['name'] : 'Choose Setting';
628
629 $output = sprintf(
630 '<select name="%1$s[%2$s_%3$s]" class="%1$s_%2$s_%3$s">',
631 esc_attr( $option_key ),
632 esc_attr( $section ),
633 esc_attr( $name )
634 );
635 $output .= sprintf(
636 '<option value="%1$s" %2$s>%3$s</option>',
637 esc_attr( $default_value ),
638 checked( $default_value === $current_value, true, false ),
639 esc_html( $default_name )
640 );
641 foreach ( $field['choices'] as $value => $label ) {
642 $output .= sprintf(
643 '<option value="%1$s" %2$s>%3$s</option>',
644 esc_attr( $value ),
645 checked( $value === $current_value, true, false ),
646 esc_html( $label )
647 );
648 }
649 $output .= '</select>';
650 break;
651 case 'file':
652 $output = sprintf(
653 '<input type="file" name="%1$s[%2$s_%3$s]" class="%4$s">',
654 esc_attr( $option_key ),
655 esc_attr( $section ),
656 esc_attr( $name ),
657 esc_attr( $class )
658 );
659 break;
660 case 'link':
661 $output = sprintf(
662 '<a id="%1$s_%2$s_%3$s" class="%4$s" href="%5$s">%6$s</a>',
663 esc_attr( $option_key ),
664 esc_attr( $section ),
665 esc_attr( $name ),
666 esc_attr( $class ),
667 esc_attr( $href ),
668 esc_attr( $title )
669 );
670 break;
671 case 'select2' :
672 if ( ! isset( $current_value ) ) {
673 $current_value = '';
674 }
675
676 $data_values = array();
677
678 if ( isset( $field['choices'] ) ) {
679 $choices = $field['choices'];
680 if ( is_callable( $choices ) ) {
681 $param = ( isset( $field['param'] ) ) ? $field['param'] : null;
682 $choices = call_user_func( $choices, $param );
683 }
684 foreach ( $choices as $key => $value ) {
685 if ( is_array( $value ) ) {
686 $child_values = array();
687 if ( isset( $value['children'] ) ) {
688 $child_values = array();
689 foreach ( $value['children'] as $child_key => $child_value ) {
690 $child_values[] = array( 'id' => $child_key, 'text' => $child_value );
691 }
692 }
693 if ( isset( $value['label'] ) ) {
694 $data_values[] = array( 'id' => $key, 'text' => $value['label'], 'children' => $child_values );
695 }
696 } else {
697 $data_values[] = array( 'id' => $key, 'text' => $value );
698 }
699 }
700 $class .= ' with-source';
701 }
702
703 $input_html = sprintf(
704 '<input type="hidden" name="%1$s[%2$s_%3$s]" data-values=\'%4$s\' value="%5$s" class="select2-select %6$s" data-placeholder="%7$s" />',
705 esc_attr( $option_key ),
706 esc_attr( $section ),
707 esc_attr( $name ),
708 esc_attr( wp_stream_json_encode( $data_values ) ),
709 esc_attr( $current_value ),
710 esc_attr( $class ),
711 sprintf( esc_html__( 'Any %s', 'stream' ), $title )
712 );
713
714 $output = sprintf(
715 '<div class="%1$s_%2$s_%3$s">%4$s</div>',
716 esc_attr( $option_key ),
717 esc_attr( $section ),
718 esc_attr( $name ),
719 $input_html
720 );
721
722 break;
723 case 'rule_list' :
724 $output = '<p class="description">' . esc_html( $description ) . '</p>';
725
726 $actions_top = sprintf( '<input type="button" class="button" id="%1$s_new_rule" value="&#43; %2$s" />', esc_attr( $section . '_' . $name ), esc_html__( 'Add New Rule', 'stream' ) );
727 $actions_bottom = sprintf( '<input type="button" class="button" id="%1$s_remove_rules" value="%2$s" />', esc_attr( $section . '_' . $name ), esc_html__( 'Delete Selected Rules', 'stream' ) );
728
729 $output .= sprintf( '<div class="tablenav top">%1$s</div>', $actions_top );
730 $output .= '<table class="wp-list-table widefat fixed stream-exclude-list">';
731
732 unset( $description );
733
734 $heading_row = sprintf(
735 '<tr>
736 <td scope="col" class="manage-column column-cb check-column">%1$s</td>
737 <th scope="col" class="manage-column">%2$s</th>
738 <th scope="col" class="manage-column">%3$s</th>
739 <th scope="col" class="manage-column">%4$s</th>
740 <th scope="col" class="manage-column">%5$s</th>
741 <th scope="col" class="actions-column manage-column"><span class="hidden">%6$s</span></th>
742 </tr>',
743 '<input class="cb-select" type="checkbox" />',
744 esc_html__( 'Author or Role', 'stream' ),
745 esc_html__( 'Context', 'stream' ),
746 esc_html__( 'Action', 'stream' ),
747 esc_html__( 'IP Address', 'stream' ),
748 esc_html__( 'Filters', 'stream' )
749 );
750
751 $exclude_rows = array();
752
753 // Prepend an empty row
754 $current_value['exclude_row'] = array( 'helper' => '' ) + ( isset( $current_value['exclude_row'] ) ? $current_value['exclude_row'] : array() );
755
756 foreach ( $current_value['exclude_row'] as $key => $value ) {
757 // Prepare values
758 $author_or_role = isset( $current_value['author_or_role'][ $key ] ) ? $current_value['author_or_role'][ $key ] : '';
759 $connector = isset( $current_value['connector'][ $key ] ) ? $current_value['connector'][ $key ] : '';
760 $context = isset( $current_value['context'][ $key ] ) ? $current_value['context'][ $key ] : '';
761 $action = isset( $current_value['action'][ $key ] ) ? $current_value['action'][ $key ] : '';
762 $ip_address = isset( $current_value['ip_address'][ $key ] ) ? $current_value['ip_address'][ $key ] : '';
763
764 // Author or Role dropdown menu
765 $author_or_role_values = array();
766 $author_or_role_selected = array();
767
768 foreach ( $this->get_roles() as $role_id => $role ) {
769 $args = array( 'id' => $role_id, 'text' => $role );
770 $users = count_users();
771 $count = isset( $users['avail_roles'][ $role_id ] ) ? $users['avail_roles'][ $role_id ] : 0;
772
773 if ( ! empty( $count ) ) {
774 $args['user_count'] = sprintf( _n( '1 user', '%s users', absint( $count ), 'stream' ), absint( $count ) );
775 }
776
777 if ( $role_id === $author_or_role ) {
778 $author_or_role_selected['id'] = $role_id;
779 $author_or_role_selected['text'] = $role;
780 }
781
782 $author_or_role_values[] = $args;
783 }
784
785 if ( empty( $author_or_role_selected ) && is_numeric( $author_or_role ) ) {
786 $user = new WP_User( $author_or_role );
787 $display_name = ( 0 === $user->ID ) ? esc_html__( 'N/A', 'stream' ) : $user->display_name;
788 $author_or_role_selected = array( 'id' => $user->ID, 'text' => $display_name );
789 }
790
791 $author_or_role_input = sprintf(
792 '<input type="hidden" name="%1$s[%2$s_%3$s][%4$s][]" data-values=\'%5$s\' data-selected-id=\'%6$s\' data-selected-text=\'%7$s\' value="%6$s" class="select2-select %4$s" data-placeholder="%8$s" data-nonce="%9$s" />',
793 esc_attr( $option_key ),
794 esc_attr( $section ),
795 esc_attr( $name ),
796 'author_or_role',
797 esc_attr( wp_stream_json_encode( $author_or_role_values ) ),
798 isset( $author_or_role_selected['id'] ) ? esc_attr( $author_or_role_selected['id'] ) : '',
799 isset( $author_or_role_selected['text'] ) ? esc_attr( $author_or_role_selected['text'] ) : '',
800 esc_html__( 'Any Author or Role', 'stream' ),
801 esc_attr( wp_create_nonce( 'stream_get_users' ) )
802 );
803
804 // Context dropdown menu
805 $context_values = array();
806
807 foreach ( $this->get_terms_labels( 'context' ) as $context_id => $context_data ) {
808 if ( is_array( $context_data ) ) {
809 $child_values = array();
810 if ( isset( $context_data['children'] ) ) {
811 $child_values = array();
812 foreach ( $context_data['children'] as $child_id => $child_value ) {
813 $child_values[] = array( 'id' => $child_id, 'text' => $child_value, 'parent' => $context_id );
814 }
815 }
816 if ( isset( $context_data['label'] ) ) {
817 $context_values[] = array( 'id' => $context_id, 'text' => $context_data['label'], 'children' => $child_values );
818 }
819 } else {
820 $context_values[] = array( 'id' => $context_id, 'text' => $context_data );
821 }
822 }
823
824 $connector_input = sprintf(
825 '<input type="hidden" name="%1$s[%2$s_%3$s][%4$s][]" class="%4$s" value="%5$s">',
826 esc_attr( $option_key ),
827 esc_attr( $section ),
828 esc_attr( $name ),
829 esc_attr( 'connector' ),
830 esc_attr( $connector )
831 );
832
833 $context_input = sprintf(
834 '<input type="hidden" name="%1$s[%2$s_%3$s][%4$s][]" data-values=\'%5$s\' value="%6$s" class="select2-select with-source %4$s" data-placeholder="%7$s" data-group="%8$s" />',
835 esc_attr( $option_key ),
836 esc_attr( $section ),
837 esc_attr( $name ),
838 'context',
839 esc_attr( wp_stream_json_encode( $context_values ) ),
840 esc_attr( $context ),
841 esc_html__( 'Any Context', 'stream' ),
842 'connector'
843 );
844
845 // Action dropdown menu
846 $action_values = array();
847
848 foreach ( $this->get_terms_labels( 'action' ) as $action_id => $action_data ) {
849 $action_values[] = array( 'id' => $action_id, 'text' => $action_data );
850 }
851
852 $action_input = sprintf(
853 '<input type="hidden" name="%1$s[%2$s_%3$s][%4$s][]" data-values=\'%5$s\' value="%6$s" class="select2-select with-source %4$s" data-placeholder="%7$s" />',
854 esc_attr( $option_key ),
855 esc_attr( $section ),
856 esc_attr( $name ),
857 'action',
858 esc_attr( wp_stream_json_encode( $action_values ) ),
859 esc_attr( $action ),
860 esc_html__( 'Any Action', 'stream' )
861 );
862
863 // IP Address input
864 $ip_address_input = sprintf(
865 '<input type="hidden" name="%1$s[%2$s_%3$s][%4$s][]" value="%5$s" class="select2-select %4$s" data-placeholder="%6$s" data-nonce="%7$s" />',
866 esc_attr( $option_key ),
867 esc_attr( $section ),
868 esc_attr( $name ),
869 'ip_address',
870 esc_attr( $ip_address ),
871 esc_html__( 'Any IP Address', 'stream' ),
872 esc_attr( wp_create_nonce( 'stream_get_ips' ) )
873 );
874
875 // Hidden helper input
876 $helper_input = sprintf(
877 '<input type="hidden" name="%1$s[%2$s_%3$s][%4$s][]" value="" />',
878 esc_attr( $option_key ),
879 esc_attr( $section ),
880 esc_attr( $name ),
881 'exclude_row'
882 );
883
884 $exclude_rows[] = sprintf(
885 '<tr class="%1$s %2$s">
886 <th scope="row" class="check-column">%3$s %4$s</th>
887 <td>%5$s</td>
888 <td>%6$s %7$s</td>
889 <td>%8$s</td>
890 <td>%9$s</td>
891 <th scope="row" class="actions-column">%10$s</th>
892 </tr>',
893 ( 0 !== $key % 2 ) ? 'alternate' : '',
894 ( 'helper' === $key ) ? 'hidden helper' : '',
895 '<input class="cb-select" type="checkbox" />',
896 $helper_input,
897 $author_or_role_input,
898 $connector_input,
899 $context_input,
900 $action_input,
901 $ip_address_input,
902 '<a href="#" class="exclude_rules_remove_rule_row">Delete</a>'
903 );
904 }
905
906 $no_rules_found_row = sprintf(
907 '<tr class="no-items hidden"><td class="colspanchange" colspan="5">%1$s</td></tr>',
908 esc_html__( 'No rules found.', 'stream' )
909 );
910
911 $output .= '<thead>' . $heading_row . '</thead>';
912 $output .= '<tfoot>' . $heading_row . '</tfoot>';
913 $output .= '<tbody>' . $no_rules_found_row . implode( '', $exclude_rows ) . '</tbody>';
914
915 $output .= '</table>';
916
917 $output .= sprintf( '<div class="tablenav bottom">%1$s</div>', $actions_bottom );
918
919 break;
920 }
921 $output .= ! empty( $description ) ? wp_kses_post( sprintf( '<p class="description">%s</p>', $description ) ) : null;
922
923 return $output;
924 }
925
926 /**
927 * Render Callback for post_types field
928 *
929 * @param array $field
930 *
931 * @return string
932 */
933 public function output_field( $field ) {
934 $method = 'output_' . $field['name'];
935
936 if ( method_exists( $this, $method ) ) {
937 return call_user_func( array( $this, $method ), $field );
938 }
939
940 $output = $this->render_field( $field );
941
942 echo $output; // xss ok
943 }
944
945 /**
946 * Get an array of user roles
947 *
948 * @return array
949 */
950 public function get_roles() {
951 $wp_roles = new WP_Roles();
952 $roles = array();
953
954 foreach ( $wp_roles->get_names() as $role => $label ) {
955 $roles[ $role ] = translate_user_role( $label );
956 }
957
958 return $roles;
959 }
960
961 /**
962 * Function will return all terms labels of given column
963 *
964 * @param string $column string Name of the column
965 *
966 * @return array
967 */
968 public function get_terms_labels( $column ) {
969 $return_labels = array();
970
971 if ( isset( $this->plugin->connectors->term_labels[ 'stream_' . $column ] ) ) {
972 if ( 'context' === $column && isset( $this->plugin->connectors->term_labels['stream_connector'] ) ) {
973 $connectors = $this->plugin->connectors->term_labels['stream_connector'];
974 $contexts = $this->plugin->connectors->term_labels['stream_context'];
975
976 foreach ( $connectors as $connector => $connector_label ) {
977 $return_labels[ $connector ]['label'] = $connector_label;
978 foreach ( $contexts as $context => $context_label ) {
979 if ( isset( $this->plugin->connectors->contexts[ $connector ] ) && array_key_exists( $context, $this->plugin->connectors->contexts[ $connector ] ) ) {
980 $return_labels[ $connector ]['children'][ $context ] = $context_label;
981 }
982 }
983 }
984 } else {
985 $return_labels = $this->plugin->connectors->term_labels[ 'stream_' . $column ];
986 }
987
988 ksort( $return_labels );
989 }
990
991 return $return_labels;
992 }
993
994 /**
995 * Remove records when records TTL is shortened
996 *
997 * @action update_option_wp_stream
998 *
999 * @param array $old_value
1000 * @param array $new_value
1001 */
1002 public function updated_option_ttl_remove_records( $old_value, $new_value ) {
1003 $ttl_before = isset( $old_value['general_records_ttl'] ) ? (int) $old_value['general_records_ttl'] : -1;
1004 $ttl_after = isset( $new_value['general_records_ttl'] ) ? (int) $new_value['general_records_ttl'] : -1;
1005
1006 if ( $ttl_after < $ttl_before ) {
1007 /**
1008 * Action assists in purging when TTL is shortened
1009 */
1010 do_action( 'wp_stream_auto_purge' );
1011 }
1012 }
1013
1014 /**
1015 * Get translations of serialized Stream settings
1016 *
1017 * @filter wp_stream_serialized_labels
1018 *
1019 * @return array Multidimensional array of fields
1020 */
1021 public function get_settings_translations( $labels ) {
1022 if ( ! isset( $labels[ $this->option_key ] ) ) {
1023 $labels[ $this->option_key ] = array();
1024 }
1025
1026 foreach ( $this->get_fields() as $section_slug => $section ) {
1027 foreach ( $section['fields'] as $field ) {
1028 $labels[ $this->option_key ][ sprintf( '%s_%s', $section_slug, $field['name'] ) ] = $field['title'];
1029 }
1030 }
1031
1032 return $labels;
1033 }
1034 }
1035