PluginProbe
Stream – Activity Log & Audit Trail / 3.0.1
Stream – Activity Log & Audit Trail v3.0.1
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.1, at classes/class-settings.php

1,027 lines 31.6 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 $output = sprintf(
573 '<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>',
574 esc_attr( $option_key ),
575 esc_attr( $section ),
576 esc_attr( $name ),
577 checked( $current_value, 1, false ),
578 wp_kses_post( $after_field )
579 );
580 break;
581 case 'multi_checkbox':
582 $output = sprintf(
583 '<div id="%1$s[%2$s_%3$s]"><fieldset>',
584 esc_attr( $option_key ),
585 esc_attr( $section ),
586 esc_attr( $name )
587 );
588 // Fallback if nothing is selected
589 $output .= sprintf(
590 '<input type="hidden" name="%1$s[%2$s_%3$s][]" value="__placeholder__" />',
591 esc_attr( $option_key ),
592 esc_attr( $section ),
593 esc_attr( $name )
594 );
595 $current_value = (array) $current_value;
596 $choices = $field['choices'];
597 if ( is_callable( $choices ) ) {
598 $choices = call_user_func( $choices );
599 }
600 foreach ( $choices as $value => $label ) {
601 $output .= sprintf(
602 '<label>%1$s <span>%2$s</span></label><br />',
603 sprintf(
604 '<input type="checkbox" name="%1$s[%2$s_%3$s][]" value="%4$s" %5$s />',
605 esc_attr( $option_key ),
606 esc_attr( $section ),
607 esc_attr( $name ),
608 esc_attr( $value ),
609 checked( in_array( $value, $current_value ), true, false )
610 ),
611 esc_html( $label )
612 );
613 }
614 $output .= '</fieldset></div>';
615 break;
616 case 'select':
617 $current_value = $this->options[ $section . '_' . $name ];
618 $default_value = isset( $default['value'] ) ? $default['value'] : '-1';
619 $default_name = isset( $default['name'] ) ? $default['name'] : 'Choose Setting';
620
621 $output = sprintf(
622 '<select name="%1$s[%2$s_%3$s]" class="%1$s_%2$s_%3$s">',
623 esc_attr( $option_key ),
624 esc_attr( $section ),
625 esc_attr( $name )
626 );
627 $output .= sprintf(
628 '<option value="%1$s" %2$s>%3$s</option>',
629 esc_attr( $default_value ),
630 checked( $default_value === $current_value, true, false ),
631 esc_html( $default_name )
632 );
633 foreach ( $field['choices'] as $value => $label ) {
634 $output .= sprintf(
635 '<option value="%1$s" %2$s>%3$s</option>',
636 esc_attr( $value ),
637 checked( $value === $current_value, true, false ),
638 esc_html( $label )
639 );
640 }
641 $output .= '</select>';
642 break;
643 case 'file':
644 $output = sprintf(
645 '<input type="file" name="%1$s[%2$s_%3$s]" class="%4$s">',
646 esc_attr( $option_key ),
647 esc_attr( $section ),
648 esc_attr( $name ),
649 esc_attr( $class )
650 );
651 break;
652 case 'link':
653 $output = sprintf(
654 '<a id="%1$s_%2$s_%3$s" class="%4$s" href="%5$s">%6$s</a>',
655 esc_attr( $option_key ),
656 esc_attr( $section ),
657 esc_attr( $name ),
658 esc_attr( $class ),
659 esc_attr( $href ),
660 esc_attr( $title )
661 );
662 break;
663 case 'select2' :
664 if ( ! isset( $current_value ) ) {
665 $current_value = '';
666 }
667
668 $data_values = array();
669
670 if ( isset( $field['choices'] ) ) {
671 $choices = $field['choices'];
672 if ( is_callable( $choices ) ) {
673 $param = ( isset( $field['param'] ) ) ? $field['param'] : null;
674 $choices = call_user_func( $choices, $param );
675 }
676 foreach ( $choices as $key => $value ) {
677 if ( is_array( $value ) ) {
678 $child_values = array();
679 if ( isset( $value['children'] ) ) {
680 $child_values = array();
681 foreach ( $value['children'] as $child_key => $child_value ) {
682 $child_values[] = array( 'id' => $child_key, 'text' => $child_value );
683 }
684 }
685 if ( isset( $value['label'] ) ) {
686 $data_values[] = array( 'id' => $key, 'text' => $value['label'], 'children' => $child_values );
687 }
688 } else {
689 $data_values[] = array( 'id' => $key, 'text' => $value );
690 }
691 }
692 $class .= ' with-source';
693 }
694
695 $input_html = sprintf(
696 '<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" />',
697 esc_attr( $option_key ),
698 esc_attr( $section ),
699 esc_attr( $name ),
700 esc_attr( wp_stream_json_encode( $data_values ) ),
701 esc_attr( $current_value ),
702 esc_attr( $class ),
703 sprintf( esc_html__( 'Any %s', 'stream' ), $title )
704 );
705
706 $output = sprintf(
707 '<div class="%1$s_%2$s_%3$s">%4$s</div>',
708 esc_attr( $option_key ),
709 esc_attr( $section ),
710 esc_attr( $name ),
711 $input_html
712 );
713
714 break;
715 case 'rule_list' :
716 $output = '<p class="description">' . esc_html( $description ) . '</p>';
717
718 $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' ) );
719 $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' ) );
720
721 $output .= sprintf( '<div class="tablenav top">%1$s</div>', $actions_top );
722 $output .= '<table class="wp-list-table widefat fixed stream-exclude-list">';
723
724 unset( $description );
725
726 $heading_row = sprintf(
727 '<tr>
728 <td scope="col" class="manage-column column-cb check-column">%1$s</td>
729 <th scope="col" class="manage-column">%2$s</th>
730 <th scope="col" class="manage-column">%3$s</th>
731 <th scope="col" class="manage-column">%4$s</th>
732 <th scope="col" class="manage-column">%5$s</th>
733 <th scope="col" class="actions-column manage-column"><span class="hidden">%6$s</span></th>
734 </tr>',
735 '<input class="cb-select" type="checkbox" />',
736 esc_html__( 'Author or Role', 'stream' ),
737 esc_html__( 'Context', 'stream' ),
738 esc_html__( 'Action', 'stream' ),
739 esc_html__( 'IP Address', 'stream' ),
740 esc_html__( 'Filters', 'stream' )
741 );
742
743 $exclude_rows = array();
744
745 // Prepend an empty row
746 $current_value['exclude_row'] = array( 'helper' => '' ) + ( isset( $current_value['exclude_row'] ) ? $current_value['exclude_row'] : array() );
747
748 foreach ( $current_value['exclude_row'] as $key => $value ) {
749 // Prepare values
750 $author_or_role = isset( $current_value['author_or_role'][ $key ] ) ? $current_value['author_or_role'][ $key ] : '';
751 $connector = isset( $current_value['connector'][ $key ] ) ? $current_value['connector'][ $key ] : '';
752 $context = isset( $current_value['context'][ $key ] ) ? $current_value['context'][ $key ] : '';
753 $action = isset( $current_value['action'][ $key ] ) ? $current_value['action'][ $key ] : '';
754 $ip_address = isset( $current_value['ip_address'][ $key ] ) ? $current_value['ip_address'][ $key ] : '';
755
756 // Author or Role dropdown menu
757 $author_or_role_values = array();
758 $author_or_role_selected = array();
759
760 foreach ( $this->get_roles() as $role_id => $role ) {
761 $args = array( 'id' => $role_id, 'text' => $role );
762 $users = count_users();
763 $count = isset( $users['avail_roles'][ $role_id ] ) ? $users['avail_roles'][ $role_id ] : 0;
764
765 if ( ! empty( $count ) ) {
766 $args['user_count'] = sprintf( _n( '1 user', '%s users', absint( $count ), 'stream' ), absint( $count ) );
767 }
768
769 if ( $role_id === $author_or_role ) {
770 $author_or_role_selected['id'] = $role_id;
771 $author_or_role_selected['text'] = $role;
772 }
773
774 $author_or_role_values[] = $args;
775 }
776
777 if ( empty( $author_or_role_selected ) && is_numeric( $author_or_role ) ) {
778 $user = new WP_User( $author_or_role );
779 $display_name = ( 0 === $user->ID ) ? esc_html__( 'N/A', 'stream' ) : $user->display_name;
780 $author_or_role_selected = array( 'id' => $user->ID, 'text' => $display_name );
781 }
782
783 $author_or_role_input = sprintf(
784 '<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" />',
785 esc_attr( $option_key ),
786 esc_attr( $section ),
787 esc_attr( $name ),
788 'author_or_role',
789 esc_attr( wp_stream_json_encode( $author_or_role_values ) ),
790 isset( $author_or_role_selected['id'] ) ? esc_attr( $author_or_role_selected['id'] ) : '',
791 isset( $author_or_role_selected['text'] ) ? esc_attr( $author_or_role_selected['text'] ) : '',
792 esc_html__( 'Any Author or Role', 'stream' ),
793 esc_attr( wp_create_nonce( 'stream_get_users' ) )
794 );
795
796 // Context dropdown menu
797 $context_values = array();
798
799 foreach ( $this->get_terms_labels( 'context' ) as $context_id => $context_data ) {
800 if ( is_array( $context_data ) ) {
801 $child_values = array();
802 if ( isset( $context_data['children'] ) ) {
803 $child_values = array();
804 foreach ( $context_data['children'] as $child_id => $child_value ) {
805 $child_values[] = array( 'id' => $child_id, 'text' => $child_value, 'parent' => $context_id );
806 }
807 }
808 if ( isset( $context_data['label'] ) ) {
809 $context_values[] = array( 'id' => $context_id, 'text' => $context_data['label'], 'children' => $child_values );
810 }
811 } else {
812 $context_values[] = array( 'id' => $context_id, 'text' => $context_data );
813 }
814 }
815
816 $connector_input = sprintf(
817 '<input type="hidden" name="%1$s[%2$s_%3$s][%4$s][]" class="%4$s" value="%5$s">',
818 esc_attr( $option_key ),
819 esc_attr( $section ),
820 esc_attr( $name ),
821 esc_attr( 'connector' ),
822 esc_attr( $connector )
823 );
824
825 $context_input = sprintf(
826 '<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" />',
827 esc_attr( $option_key ),
828 esc_attr( $section ),
829 esc_attr( $name ),
830 'context',
831 esc_attr( wp_stream_json_encode( $context_values ) ),
832 esc_attr( $context ),
833 esc_html__( 'Any Context', 'stream' ),
834 'connector'
835 );
836
837 // Action dropdown menu
838 $action_values = array();
839
840 foreach ( $this->get_terms_labels( 'action' ) as $action_id => $action_data ) {
841 $action_values[] = array( 'id' => $action_id, 'text' => $action_data );
842 }
843
844 $action_input = sprintf(
845 '<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" />',
846 esc_attr( $option_key ),
847 esc_attr( $section ),
848 esc_attr( $name ),
849 'action',
850 esc_attr( wp_stream_json_encode( $action_values ) ),
851 esc_attr( $action ),
852 esc_html__( 'Any Action', 'stream' )
853 );
854
855 // IP Address input
856 $ip_address_input = sprintf(
857 '<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" />',
858 esc_attr( $option_key ),
859 esc_attr( $section ),
860 esc_attr( $name ),
861 'ip_address',
862 esc_attr( $ip_address ),
863 esc_html__( 'Any IP Address', 'stream' ),
864 esc_attr( wp_create_nonce( 'stream_get_ips' ) )
865 );
866
867 // Hidden helper input
868 $helper_input = sprintf(
869 '<input type="hidden" name="%1$s[%2$s_%3$s][%4$s][]" value="" />',
870 esc_attr( $option_key ),
871 esc_attr( $section ),
872 esc_attr( $name ),
873 'exclude_row'
874 );
875
876 $exclude_rows[] = sprintf(
877 '<tr class="%1$s %2$s">
878 <th scope="row" class="check-column">%3$s %4$s</th>
879 <td>%5$s</td>
880 <td>%6$s %7$s</td>
881 <td>%8$s</td>
882 <td>%9$s</td>
883 <th scope="row" class="actions-column">%10$s</th>
884 </tr>',
885 ( 0 !== $key % 2 ) ? 'alternate' : '',
886 ( 'helper' === $key ) ? 'hidden helper' : '',
887 '<input class="cb-select" type="checkbox" />',
888 $helper_input,
889 $author_or_role_input,
890 $connector_input,
891 $context_input,
892 $action_input,
893 $ip_address_input,
894 '<a href="#" class="exclude_rules_remove_rule_row">Delete</a>'
895 );
896 }
897
898 $no_rules_found_row = sprintf(
899 '<tr class="no-items hidden"><td class="colspanchange" colspan="5">%1$s</td></tr>',
900 esc_html__( 'No rules found.', 'stream' )
901 );
902
903 $output .= '<thead>' . $heading_row . '</thead>';
904 $output .= '<tfoot>' . $heading_row . '</tfoot>';
905 $output .= '<tbody>' . $no_rules_found_row . implode( '', $exclude_rows ) . '</tbody>';
906
907 $output .= '</table>';
908
909 $output .= sprintf( '<div class="tablenav bottom">%1$s</div>', $actions_bottom );
910
911 break;
912 }
913 $output .= ! empty( $description ) ? wp_kses_post( sprintf( '<p class="description">%s</p>', $description ) ) : null;
914
915 return $output;
916 }
917
918 /**
919 * Render Callback for post_types field
920 *
921 * @param array $field
922 *
923 * @return string
924 */
925 public function output_field( $field ) {
926 $method = 'output_' . $field['name'];
927
928 if ( method_exists( $this, $method ) ) {
929 return call_user_func( array( $this, $method ), $field );
930 }
931
932 $output = $this->render_field( $field );
933
934 echo $output; // xss ok
935 }
936
937 /**
938 * Get an array of user roles
939 *
940 * @return array
941 */
942 public function get_roles() {
943 $wp_roles = new WP_Roles();
944 $roles = array();
945
946 foreach ( $wp_roles->get_names() as $role => $label ) {
947 $roles[ $role ] = translate_user_role( $label );
948 }
949
950 return $roles;
951 }
952
953 /**
954 * Function will return all terms labels of given column
955 *
956 * @param string $column string Name of the column
957 *
958 * @return array
959 */
960 public function get_terms_labels( $column ) {
961 $return_labels = array();
962
963 if ( isset( $this->plugin->connectors->term_labels[ 'stream_' . $column ] ) ) {
964 if ( 'context' === $column && isset( $this->plugin->connectors->term_labels['stream_connector'] ) ) {
965 $connectors = $this->plugin->connectors->term_labels['stream_connector'];
966 $contexts = $this->plugin->connectors->term_labels['stream_context'];
967
968 foreach ( $connectors as $connector => $connector_label ) {
969 $return_labels[ $connector ]['label'] = $connector_label;
970 foreach ( $contexts as $context => $context_label ) {
971 if ( isset( $this->plugin->connectors->contexts[ $connector ] ) && array_key_exists( $context, $this->plugin->connectors->contexts[ $connector ] ) ) {
972 $return_labels[ $connector ]['children'][ $context ] = $context_label;
973 }
974 }
975 }
976 } else {
977 $return_labels = $this->plugin->connectors->term_labels[ 'stream_' . $column ];
978 }
979
980 ksort( $return_labels );
981 }
982
983 return $return_labels;
984 }
985
986 /**
987 * Remove records when records TTL is shortened
988 *
989 * @action update_option_wp_stream
990 *
991 * @param array $old_value
992 * @param array $new_value
993 */
994 public function updated_option_ttl_remove_records( $old_value, $new_value ) {
995 $ttl_before = isset( $old_value['general_records_ttl'] ) ? (int) $old_value['general_records_ttl'] : -1;
996 $ttl_after = isset( $new_value['general_records_ttl'] ) ? (int) $new_value['general_records_ttl'] : -1;
997
998 if ( $ttl_after < $ttl_before ) {
999 /**
1000 * Action assists in purging when TTL is shortened
1001 */
1002 do_action( 'wp_stream_auto_purge' );
1003 }
1004 }
1005
1006 /**
1007 * Get translations of serialized Stream settings
1008 *
1009 * @filter wp_stream_serialized_labels
1010 *
1011 * @return array Multidimensional array of fields
1012 */
1013 public function get_settings_translations( $labels ) {
1014 if ( ! isset( $labels[ $this->option_key ] ) ) {
1015 $labels[ $this->option_key ] = array();
1016 }
1017
1018 foreach ( $this->get_fields() as $section_slug => $section ) {
1019 foreach ( $section['fields'] as $field ) {
1020 $labels[ $this->option_key ][ sprintf( '%s_%s', $section_slug, $field['name'] ) ] = $field['title'];
1021 }
1022 }
1023
1024 return $labels;
1025 }
1026 }
1027