PluginProbe
Stream – Activity Log & Audit Trail / 4.0.2
Stream – Activity Log & Audit Trail v4.0.2
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
← All changes | classes/class-settings.php +364 -193 3.0.44.0.2 View file →
@@ -1,14 +1,25 @@
1 1 <?php
2 +/**
3 + * Renders and manages the plugin Settings page.
4 + *
5 + * @package WP_Stream
6 + */
7 +
2 8 namespace WP_Stream;
3 9
4 -use \WP_Roles;
5 -use \WP_User;
6 -use \WP_User_Query;
10 +use WP_Roles;
11 +use WP_User;
12 +use WP_User_Query;
7 13
14 +/**
15 + * Class - Settings
16 + */
8 17 class Settings {
18 +
9 19 /**
10 - * Hold Plugin class
20 + * Holds instance of plugin object
21 + *
11 22 * @var Plugin
12 23 */
13 24 public $plugin;
14 25
@@ -42,9 +53,9 @@
42 53
43 54 /**
44 55 * Class constructor.
45 56 *
46 - * @param Plugin $plugin The main Plugin class.
57 + * @param Plugin $plugin Instance of plugin object.
47 58 */
48 59 public function __construct( $plugin ) {
49 60 $this->plugin = $plugin;
50 61
@@ -50,21 +61,35 @@
50 61
51 62 $this->option_key = $this->get_option_key();
52 63 $this->options = $this->get_options();
53 64
54 - // Register settings, and fields
65 + // Register settings, and fields.
55 66 add_action( 'admin_init', array( $this, 'register_settings' ) );
56 67
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 );
68 + // Remove records when records TTL is shortened.
69 + add_action(
70 + 'update_option_' . $this->option_key,
71 + array(
72 + $this,
73 + 'updated_option_ttl_remove_records',
74 + ),
75 + 10,
76 + 2
77 + );
59 78
60 - // Apply label translations for settings
61 - add_filter( 'wp_stream_serialized_labels', array( $this, 'get_settings_translations' ) );
79 + // Apply label translations for settings.
80 + add_filter(
81 + 'wp_stream_serialized_labels',
82 + array(
83 + $this,
84 + 'get_settings_translations',
85 + )
86 + );
62 87
63 - // Ajax callback function to search users
88 + // Ajax callback function to search users.
64 89 add_action( 'wp_ajax_stream_get_users', array( $this, 'get_users' ) );
65 90
66 - // Ajax callback function to search IPs
91 + // Ajax callback function to search IPs.
67 92 add_action( 'wp_ajax_stream_get_ips', array( $this, 'get_ips' ) );
68 93 }
69 94
70 95 /**
@@ -84,21 +109,31 @@
84 109 'message' => esc_html__( 'There was an error in the request', 'stream' ),
85 110 );
86 111
87 112 $search = '';
88 - if ( isset( $_POST['find'] ) ) {
89 - // @TODO: Make this pass phpcs
90 - $search = esc_html( wp_unslash( trim( $_POST['find'] ) ) ); // input var okay
113 + $input = wp_stream_filter_input( INPUT_POST, 'find' );
114 +
115 + if ( ! isset( $input['term'] ) ) {
116 + $search = wp_unslash( trim( $input['term'] ) );
91 117 }
118 +
92 119 $request = (object) array(
93 120 'find' => $search,
94 121 );
95 122
96 - add_filter( 'user_search_columns', array( $this, 'add_display_name_search_columns' ), 10, 3 );
123 + add_filter(
124 + 'user_search_columns',
125 + array(
126 + $this,
127 + 'add_display_name_search_columns',
128 + ),
129 + 10,
130 + 3
131 + );
97 132
98 133 $users = new WP_User_Query(
99 134 array(
100 - 'search' => "*{$request->find}*",
135 + 'search' => "*{$request->find}*",
101 136 'search_columns' => array(
102 137 'user_login',
103 138 'user_nicename',
104 139 'user_email',
@@ -103,24 +138,49 @@
103 138 'user_nicename',
104 139 'user_email',
105 140 'user_url',
106 141 ),
107 - 'orderby' => 'display_name',
108 - 'number' => $this->plugin->admin->preload_users_max,
142 + 'orderby' => 'display_name',
143 + 'number' => $this->plugin->admin->preload_users_max,
109 144 )
110 145 );
111 146
112 - remove_filter( 'user_search_columns', array( $this, 'add_display_name_search_columns' ), 10 );
147 + remove_filter(
148 + 'user_search_columns',
149 + array(
150 + $this,
151 + 'add_display_name_search_columns',
152 + ),
153 + 10
154 + );
113 155
114 156 if ( 0 === $users->get_total() ) {
115 157 wp_send_json_error( $response );
116 158 }
159 + $users_array = $users->results;
117 160
118 - $response->status = true;
119 - $response->message = '';
120 - $response->users = array();
161 + if ( is_multisite() && is_super_admin() ) {
162 + $super_admins = get_super_admins();
163 + foreach ( $super_admins as $admin ) {
164 + $user = get_user_by( 'login', $admin );
165 + $users_array[] = $user;
166 + }
167 + }
121 168
122 - foreach ( $users->results as $key => $user ) {
169 + $response->status = true;
170 + $response->message = '';
171 + $response->roles = $this->get_roles();
172 + $response->users = array();
173 + $users_added_to_response = array();
174 +
175 + foreach ( $users_array as $key => $user ) {
176 + // exclude duplications.
177 + if ( array_key_exists( $user->ID, $users_added_to_response ) ) {
178 + continue;
179 + } else {
180 + $users_added_to_response[ $user->ID ] = true;
181 + }
182 +
123 183 $author = new Author( $user->ID );
124 184
125 185 $args = array(
126 186 'id' => $author->ID,
@@ -128,9 +188,10 @@
128 188 );
129 189
130 190 $args['tooltip'] = esc_attr(
131 191 sprintf(
132 - __( "ID: %d\nUser: %s\nEmail: %s\nRole: %s", 'stream' ),
192 + /* translators: %1$d: user ID, %2$s: username, %3$s: email, %4$s: user role (e.g. "42", "administrator", "foo@bar.com", "subscriber") */
193 + __( 'ID: %1$d\nUser: %2$s\nEmail: %3$s\nRole: %4$s', 'stream' ),
133 194 $author->id,
134 195 $author->user_login,
135 196 $author->user_email,
136 197 ucwords( $author->get_role() )
@@ -141,12 +202,19 @@
141 202
142 203 $response->users[] = $args;
143 204 }
144 205
206 + usort(
207 + $response->users,
208 + function ( $a, $b ) {
209 + return strcmp( $a['text'], $b['text'] );
210 + }
211 + );
212 +
145 213 if ( empty( $search ) || preg_match( '/wp|cli|system|unknown/i', $search ) ) {
146 - $author = new Author( 0 );
214 + $author = new Author( 0 );
147 215 $response->users[] = array(
148 - 'id' => $author->id,
216 + 'id' => '0',
149 217 'text' => $author->get_display_name(),
150 218 'icon' => $author->get_avatar_src( 32 ),
151 219 '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 220 );
@@ -155,10 +223,10 @@
155 223 wp_send_json_success( $response );
156 224 }
157 225
158 226 /**
159 - * Ajax callback function to search IP addresses, used on exclude setting page
160 - */
227 + * Ajax callback function to search IP addresses, used on exclude setting page
228 + */
161 229 public function get_ips() {
162 230 if ( ! defined( 'DOING_AJAX' ) || ! current_user_can( $this->plugin->admin->settings_cap ) ) {
163 231 return;
164 232 }
@@ -164,10 +232,20 @@
164 232 }
165 233
166 234 check_ajax_referer( 'stream_get_ips', 'nonce' );
167 235
168 - $ips = $this->plugin->db->existing_records( 'ip' );
236 + $ips = $this->plugin->db->existing_records( 'ip' );
237 + $find = wp_stream_filter_input( INPUT_POST, 'find' );
169 238
239 + if ( isset( $find['term'] ) && '' !== $find['term'] ) {
240 + $ips = array_filter(
241 + $ips,
242 + function ( $ip ) use ( $find ) {
243 + return 0 === strpos( $ip, $find['term'] );
244 + }
245 + );
246 + }
247 +
170 248 if ( $ips ) {
171 249 wp_send_json_success( $ips );
172 250 } else {
173 251 wp_send_json_error();
@@ -176,11 +254,11 @@
176 254
177 255 /**
178 256 * Filter the columns to search in a WP_User_Query search.
179 257 *
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.
258 + * @param array $search_columns Array of column names to be searched.
259 + * @param string $search Text being searched.
260 + * @param \WP_User_Query $query current WP_User_Query instance.
183 261 *
184 262 * @return array
185 263 */
186 264 public function add_display_name_search_columns( $search_columns, $search, $query ) {
@@ -219,18 +297,18 @@
219 297 * @return array
220 298 */
221 299 public function get_fields() {
222 300 $fields = array(
223 - 'general' => array(
301 + 'general' => array(
224 302 'title' => esc_html__( 'General', 'stream' ),
225 303 'fields' => array(
226 304 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' ),
305 + 'name' => 'role_access',
306 + 'title' => esc_html__( 'Role Access', 'stream' ),
307 + 'type' => 'multi_checkbox',
308 + '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' ),
309 + 'choices' => $this->get_roles(),
310 + 'default' => array( 'administrator' ),
233 311 ),
234 312 array(
235 313 'name' => 'records_ttl',
236 314 'title' => esc_html__( 'Keep Records for', 'stream' ),
@@ -252,18 +330,18 @@
252 330 'default' => 0,
253 331 ),
254 332 ),
255 333 ),
256 - 'exclude' => array(
334 + 'exclude' => array(
257 335 'title' => esc_html__( 'Exclude', 'stream' ),
258 336 'fields' => array(
259 337 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',
338 + 'name' => 'rules',
339 + 'title' => esc_html__( 'Exclude Rules', 'stream' ),
340 + 'type' => 'rule_list',
341 + 'desc' => esc_html__( 'Create rules to exclude certain kinds of activity from being recorded by Stream.', 'stream' ),
342 + 'default' => array(),
343 + 'nonce' => 'stream_get_ips',
266 344 ),
267 345 ),
268 346 ),
269 347 'advanced' => array(
@@ -277,28 +355,28 @@
277 355 'after_field' => esc_html__( 'Enabled', 'stream' ),
278 356 'default' => 0,
279 357 ),
280 358 array(
281 - 'name' => 'delete_all_records',
282 - 'title' => esc_html__( 'Reset Stream Database', 'stream' ),
283 - 'type' => 'link',
284 - 'href' => add_query_arg(
359 + 'name' => 'delete_all_records',
360 + 'title' => esc_html__( 'Reset Stream Database', 'stream' ),
361 + 'type' => 'link',
362 + 'href' => add_query_arg(
285 363 array(
286 - 'action' => 'wp_stream_reset',
287 - 'wp_stream_nonce' => wp_create_nonce( 'stream_nonce' ),
364 + 'action' => 'wp_stream_reset',
365 + 'wp_stream_nonce_reset' => wp_create_nonce( 'stream_nonce_reset' ),
288 366 ),
289 367 admin_url( 'admin-ajax.php' )
290 368 ),
291 - 'class' => 'warning',
292 - 'desc' => esc_html__( 'Warning: This will delete all activity records from the database.', 'stream' ),
293 - 'default' => 0,
294 - 'sticky' => 'bottom',
369 + 'class' => 'warning',
370 + 'desc' => esc_html__( 'Warning: This will delete all activity records from the database.', 'stream' ),
371 + 'default' => 0,
372 + 'sticky' => 'bottom',
295 373 ),
296 374 ),
297 375 ),
298 376 );
299 377
300 - // If Akismet is active, allow Admins to opt-in to Akismet tracking
378 + // If Akismet is active, allow Admins to opt-in to Akismet tracking.
301 379 if ( class_exists( 'Akismet' ) ) {
302 380 $akismet_tracking = array(
303 381 'name' => 'akismet_tracking',
304 382 'title' => esc_html__( 'Akismet Tracking', 'stream' ),
@@ -310,21 +388,18 @@
310 388
311 389 array_push( $fields['advanced']['fields'], $akismet_tracking );
312 390 }
313 391
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 - );
392 + $wp_cron_tracking = array(
393 + 'name' => 'wp_cron_tracking',
394 + 'title' => esc_html__( 'WP Cron Tracking', 'stream' ),
395 + 'type' => 'checkbox',
396 + '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' ),
397 + 'after_field' => esc_html__( 'Enabled', 'stream' ),
398 + 'default' => 0,
399 + );
324 400
325 - array_push( $fields['advanced']['fields'], $wp_cron_tracking );
326 - }
401 + array_push( $fields['advanced']['fields'], $wp_cron_tracking );
327 402
328 403 /**
329 404 * Filter allows for modification of options fields
330 405 *
@@ -331,9 +406,9 @@
331 406 * @return array Array of option fields
332 407 */
333 408 $this->fields = apply_filters( 'wp_stream_settings_option_fields', $fields );
334 409
335 - // Sort option fields in each tab by title ASC
410 + // Sort option fields in each tab by title ASC.
336 411 foreach ( $this->fields as $tab => $options ) {
337 412 $titles = array();
338 413
339 414 foreach ( $options['fields'] as $field ) {
@@ -403,9 +478,16 @@
403 478 */
404 479 public function register_settings() {
405 480 $sections = $this->get_fields();
406 481
407 - register_setting( $this->option_key, $this->option_key, array( $this, 'sanitize_settings' ) );
482 + register_setting(
483 + $this->option_key,
484 + $this->option_key,
485 + array(
486 + $this,
487 + 'sanitize_settings',
488 + )
489 + );
408 490
409 491 foreach ( $sections as $section_name => $section ) {
410 492 add_settings_section(
411 493 $section_name,
@@ -414,9 +496,10 @@
414 496 $this->option_key
415 497 );
416 498
417 499 foreach ( $section['fields'] as $field_idx => $field ) {
418 - if ( ! isset( $field['type'] ) ) { // No field type associated, skip, no GUI
500 + // No field type associated, skip, no GUI.
501 + if ( ! isset( $field['type'] ) ) {
419 502 continue;
420 503 }
421 504
422 505 add_settings_field(
@@ -421,14 +504,17 @@
421 504
422 505 add_settings_field(
423 506 $field['name'],
424 507 $field['title'],
425 - ( isset( $field['callback'] ) ? $field['callback'] : array( $this, 'output_field' ) ),
508 + ( isset( $field['callback'] ) ? $field['callback'] : array(
509 + $this,
510 + 'output_field',
511 + ) ),
426 512 $this->option_key,
427 513 $section_name,
428 514 $field + array(
429 515 'section' => $section_name,
430 - 'label_for' => sprintf( '%s_%s_%s', $this->option_key, $section_name, $field['name'] ), // xss ok
516 + 'label_for' => sprintf( '%s_%s_%s', $this->option_key, $section_name, $field['name'] ),
431 517 )
432 518 );
433 519 }
434 520 }
@@ -436,9 +522,9 @@
436 522
437 523 /**
438 524 * Sanitization callback for settings field values before save
439 525 *
440 - * @param array $input
526 + * @param array $input Raw input.
441 527 *
442 528 * @return array
443 529 */
444 530 public function sanitize_settings( $input ) {
@@ -457,38 +543,56 @@
457 543 if ( empty( $type ) || ! isset( $input[ $name ] ) || '' === $input[ $name ] ) {
458 544 continue;
459 545 }
460 546
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 ];
547 + $output[ $name ] = $this->sanitize_setting_by_field_type( $input[ $name ], $type );
548 + }
549 + }
472 550
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 ] );
551 + return $output;
552 + }
553 +
554 + /**
555 + * Sanitizes a setting value based on the field type.
556 + *
557 + * @param mixed $value The value to be sanitized.
558 + * @param string $field_type The type of field.
559 + *
560 + * @return mixed The sanitized value.
561 + */
562 + public function sanitize_setting_by_field_type( $value, $field_type ) {
563 +
564 + // Sanitize depending on the type of field.
565 + switch ( $field_type ) {
566 + case 'number':
567 + $sanitized_value = is_numeric( $value ) ? intval( trim( $value ) ) : '';
568 + break;
569 + case 'checkbox':
570 + $sanitized_value = is_numeric( $value ) ? absint( trim( $value ) ) : '';
571 + break;
572 + default:
573 + if ( is_array( $value ) ) {
574 + $sanitized_value = $value;
575 +
576 + // Support all values in multidimentional arrays too.
577 + array_walk_recursive(
578 + $sanitized_value,
579 + function ( &$v ) {
580 + $v = sanitize_text_field( trim( $v ) );
479 581 }
582 + );
583 + } else {
584 + $sanitized_value = sanitize_text_field( trim( $value ) );
480 585 }
481 - }
482 586 }
483 587
484 - return $output;
588 + return $sanitized_value;
485 589 }
486 590
487 591 /**
488 592 * Compile HTML needed for displaying the field
489 593 *
490 - * @param array $field Field settings
594 + * @param array $field Field settings.
491 595 *
492 596 * @return string HTML to be displayed
493 597 */
494 598 public function render_field( $field ) {
@@ -511,14 +615,12 @@
511 615 $nonce = isset( $field['nonce'] ) ? $field['nonce'] : null;
512 616
513 617 if ( isset( $field['value'] ) ) {
514 618 $current_value = $field['value'];
619 + } elseif ( isset( $this->options[ $section . '_' . $name ] ) ) {
620 + $current_value = $this->options[ $section . '_' . $name ];
515 621 } else {
516 - if ( isset( $this->options[ $section . '_' . $name ] ) ) {
517 - $current_value = $this->options[ $section . '_' . $name ];
518 - } else {
519 - $current_value = null;
520 - }
622 + $current_value = null;
521 623 }
522 624
523 625 $option_key = $this->option_key;
524 626
@@ -529,11 +631,9 @@
529 631 if ( ! $type || ! $section || ! $name ) {
530 632 return '';
531 633 }
532 634
533 - if ( 'multi_checkbox' === $type
534 - && ( empty( $field['choices'] ) || ! is_array( $field['choices'] ) )
535 - ) {
635 + if ( 'multi_checkbox' === $type && ( empty( $field['choices'] ) || ! is_array( $field['choices'] ) ) ) {
536 636 return '';
537 637 }
538 638
539 639 switch ( $type ) {
@@ -592,10 +692,10 @@
592 692 esc_attr( $option_key ),
593 693 esc_attr( $section ),
594 694 esc_attr( $name )
595 695 );
596 - // Fallback if nothing is selected
597 - $output .= sprintf(
696 + // Fallback if nothing is selected.
697 + $output .= sprintf(
598 698 '<input type="hidden" name="%1$s[%2$s_%3$s][]" value="__placeholder__" />',
599 699 esc_attr( $option_key ),
600 700 esc_attr( $section ),
601 701 esc_attr( $name )
@@ -600,9 +700,9 @@
600 700 esc_attr( $section ),
601 701 esc_attr( $name )
602 702 );
603 703 $current_value = (array) $current_value;
604 - $choices = $field['choices'];
704 + $choices = $field['choices'];
605 705 if ( is_callable( $choices ) ) {
606 706 $choices = call_user_func( $choices );
607 707 }
608 708 foreach ( $choices as $value => $label ) {
@@ -613,9 +713,9 @@
613 713 esc_attr( $option_key ),
614 714 esc_attr( $section ),
615 715 esc_attr( $name ),
616 716 esc_attr( $value ),
617 - checked( in_array( $value, $current_value ), true, false )
717 + checked( in_array( $value, $current_value, true ), true, false )
618 718 ),
619 719 esc_html( $label )
620 720 );
621 721 }
@@ -667,9 +767,9 @@
667 767 esc_attr( $href ),
668 768 esc_attr( $title )
669 769 );
670 770 break;
671 - case 'select2' :
771 + case 'select2':
672 772 if ( ! isset( $current_value ) ) {
673 773 $current_value = '';
674 774 }
675 775
@@ -686,16 +786,26 @@
686 786 $child_values = array();
687 787 if ( isset( $value['children'] ) ) {
688 788 $child_values = array();
689 789 foreach ( $value['children'] as $child_key => $child_value ) {
690 - $child_values[] = array( 'id' => $child_key, 'text' => $child_value );
790 + $child_values[] = array(
791 + 'id' => $child_key,
792 + 'text' => $child_value,
793 + );
691 794 }
692 795 }
693 796 if ( isset( $value['label'] ) ) {
694 - $data_values[] = array( 'id' => $key, 'text' => $value['label'], 'children' => $child_values );
797 + $data_values[] = array(
798 + 'id' => $key,
799 + 'text' => $value['label'],
800 + 'children' => $child_values,
801 + );
695 802 }
696 803 } else {
697 - $data_values[] = array( 'id' => $key, 'text' => $value );
804 + $data_values[] = array(
805 + 'id' => $key,
806 + 'text' => $value,
807 + );
698 808 }
699 809 }
700 810 $class .= ' with-source';
701 811 }
@@ -704,11 +814,12 @@
704 814 '<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 815 esc_attr( $option_key ),
706 816 esc_attr( $section ),
707 817 esc_attr( $name ),
708 - esc_attr( wp_stream_json_encode( $data_values ) ),
818 + esc_attr( wp_json_encode( $data_values ) ),
709 819 esc_attr( $current_value ),
710 820 esc_attr( $class ),
821 + /* translators: %s: the title of the dropdown menu (e.g. "users") */
711 822 sprintf( esc_html__( 'Any %s', 'stream' ), $title )
712 823 );
713 824
714 825 $output = sprintf(
@@ -719,13 +830,15 @@
719 830 $input_html
720 831 );
721 832
722 833 break;
723 - case 'rule_list' :
834 + case 'rule_list':
835 + $users = count_users();
836 + $form = new Form_Generator();
724 837 $output = '<p class="description">' . esc_html( $description ) . '</p>';
725 838
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' ) );
839 + $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' ) );
840 + $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 841
729 842 $output .= sprintf( '<div class="tablenav top">%1$s</div>', $actions_top );
730 843 $output .= '<table class="wp-list-table widefat fixed stream-exclude-list">';
731 844
@@ -749,13 +862,18 @@
749 862 );
750 863
751 864 $exclude_rows = array();
752 865
753 - // Prepend an empty row
754 - $current_value['exclude_row'] = array( 'helper' => '' ) + ( isset( $current_value['exclude_row'] ) ? $current_value['exclude_row'] : array() );
866 + // Account for when no rules have been added yet.
867 + if ( ! is_array( $current_value ) ) {
868 + $current_value = array();
869 + }
755 870
871 + // Prepend an empty row.
872 + $current_value['exclude_row'] = ( isset( $current_value['exclude_row'] ) ? $current_value['exclude_row'] : array() ) + array( 'helper' => '' );
873 +
756 874 foreach ( $current_value['exclude_row'] as $key => $value ) {
757 - // Prepare values
875 + // Prepare values.
758 876 $author_or_role = isset( $current_value['author_or_role'][ $key ] ) ? $current_value['author_or_role'][ $key ] : '';
759 877 $connector = isset( $current_value['connector'][ $key ] ) ? $current_value['connector'][ $key ] : '';
760 878 $context = isset( $current_value['context'][ $key ] ) ? $current_value['context'][ $key ] : '';
761 879 $action = isset( $current_value['action'][ $key ] ) ? $current_value['action'][ $key ] : '';
@@ -760,24 +878,27 @@
760 878 $context = isset( $current_value['context'][ $key ] ) ? $current_value['context'][ $key ] : '';
761 879 $action = isset( $current_value['action'][ $key ] ) ? $current_value['action'][ $key ] : '';
762 880 $ip_address = isset( $current_value['ip_address'][ $key ] ) ? $current_value['ip_address'][ $key ] : '';
763 881
764 - // Author or Role dropdown menu
882 + // Author or Role dropdown menu.
765 883 $author_or_role_values = array();
766 884 $author_or_role_selected = array();
767 885
768 886 foreach ( $this->get_roles() as $role_id => $role ) {
769 - $args = array( 'id' => $role_id, 'text' => $role );
770 - $users = count_users();
887 + $args = array(
888 + 'value' => $role_id,
889 + 'text' => $role,
890 + );
771 891 $count = isset( $users['avail_roles'][ $role_id ] ) ? $users['avail_roles'][ $role_id ] : 0;
772 892
773 893 if ( ! empty( $count ) ) {
774 - $args['user_count'] = sprintf( _n( '1 user', '%s users', absint( $count ), 'stream' ), absint( $count ) );
894 + /* translators: %d: a number of users (e.g. "42") */
895 + $args['user_count'] = sprintf( _n( '%d user', '%d users', absint( $count ), 'stream' ), absint( $count ) );
775 896 }
776 897
777 898 if ( $role_id === $author_or_role ) {
778 - $author_or_role_selected['id'] = $role_id;
779 - $author_or_role_selected['text'] = $role;
899 + $author_or_role_selected['value'] = $role_id;
900 + $author_or_role_selected['text'] = $role;
780 901 }
781 902
782 903 $author_or_role_values[] = $args;
783 904 }
@@ -784,25 +905,33 @@
784 905
785 906 if ( empty( $author_or_role_selected ) && is_numeric( $author_or_role ) ) {
786 907 $user = new WP_User( $author_or_role );
787 908 $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 );
909 + $author_or_role_selected = array(
910 + 'value' => $user->ID,
911 + 'text' => $display_name,
912 + );
913 + $author_or_role_values[] = $author_or_role_selected;
789 914 }
790 915
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' ) )
916 + $author_or_role_input = $form->render_field(
917 + 'select2',
918 + array(
919 + 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]', $option_key, $section, $name, 'author_or_role' ) ),
920 + 'options' => $author_or_role_values,
921 + 'classes' => 'author_or_role',
922 + // Data attributes are escaped in Form_Generator::prepare_data_attributes_string().
923 + 'data' => array(
924 + 'placeholder' => __( 'Any Author or Role', 'stream' ),
925 + 'nonce' => wp_create_nonce( 'stream_get_users' ),
926 + 'selected-id' => isset( $author_or_role_selected['value'] ) ? $author_or_role_selected['value'] : '',
927 + 'selected-text' => isset( $author_or_role_selected['text'] ) ? $author_or_role_selected['text'] : '',
928 + ),
929 + ),
930 + false
802 931 );
803 932
804 - // Context dropdown menu
933 + // Context dropdown menu.
805 934 $context_values = array();
806 935
807 936 foreach ( $this->get_terms_labels( 'context' ) as $context_id => $context_data ) {
808 937 if ( is_array( $context_data ) ) {
@@ -809,71 +938,108 @@
809 938 $child_values = array();
810 939 if ( isset( $context_data['children'] ) ) {
811 940 $child_values = array();
812 941 foreach ( $context_data['children'] as $child_id => $child_value ) {
813 - $child_values[] = array( 'id' => $child_id, 'text' => $child_value, 'parent' => $context_id );
942 + $child_values[] = array(
943 + 'value' => $context_id . '-' . $child_id,
944 + 'text' => $child_value,
945 + 'parent' => $context_id,
946 + );
814 947 }
815 948 }
816 949 if ( isset( $context_data['label'] ) ) {
817 - $context_values[] = array( 'id' => $context_id, 'text' => $context_data['label'], 'children' => $child_values );
950 + $context_values[] = array(
951 + 'value' => $context_id,
952 + 'text' => $context_data['label'],
953 + 'children' => $child_values,
954 + );
818 955 }
819 956 } else {
820 - $context_values[] = array( 'id' => $context_id, 'text' => $context_data );
957 + $context_values[] = array(
958 + 'value' => $context_id,
959 + 'text' => $context_data,
960 + );
821 961 }
822 962 }
823 963
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 )
964 + $connector_or_context_input = $form->render_field(
965 + 'select2',
966 + array(
967 + 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]', $option_key, $section, $name, 'connector_or_context' ) ),
968 + 'options' => $context_values,
969 + 'classes' => 'connector_or_context',
970 + // Data attributes are escaped in Form_Generator::prepare_data_attributes_string().
971 + 'data' => array(
972 + 'group' => 'connector',
973 + 'placeholder' => __( 'Any Context', 'stream' ),
974 + ),
975 + ),
976 + false
831 977 );
832 978
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'
979 + $connector_input = $form->render_field(
980 + 'hidden',
981 + array(
982 + 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]', $option_key, $section, $name, 'connector' ) ),
983 + 'value' => $connector,
984 + 'classes' => 'connector',
985 + ),
986 + false
843 987 );
844 988
845 - // Action dropdown menu
989 + $context_input = $form->render_field(
990 + 'hidden',
991 + array(
992 + 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]', $option_key, $section, $name, 'context' ) ),
993 + 'value' => $context,
994 + 'classes' => 'context',
995 + ),
996 + false
997 + );
998 +
999 + // Action dropdown menu.
846 1000 $action_values = array();
847 1001
848 1002 foreach ( $this->get_terms_labels( 'action' ) as $action_id => $action_data ) {
849 - $action_values[] = array( 'id' => $action_id, 'text' => $action_data );
1003 + $action_values[] = array(
1004 + 'value' => $action_id,
1005 + 'text' => $action_data,
1006 + );
850 1007 }
851 1008
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' )
1009 + $action_input = $form->render_field(
1010 + 'select2',
1011 + array(
1012 + 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]', $option_key, $section, $name, 'action' ) ),
1013 + 'value' => $action,
1014 + 'options' => $action_values,
1015 + 'classes' => 'action',
1016 + // Data attributes are escaped in Form_Generator::prepare_data_attributes_string().
1017 + 'data' => array(
1018 + 'placeholder' => __( 'Any Action', 'stream' ),
1019 + ),
1020 + ),
1021 + false
861 1022 );
862 1023
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' ) )
1024 + // IP Address input.
1025 + $ip_address_input = $form->render_field(
1026 + 'select2',
1027 + array(
1028 + 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]', $option_key, $section, $name, 'ip_address' ) ),
1029 + 'value' => $ip_address,
1030 + 'classes' => 'ip_address',
1031 + // Data attributes are escaped in Form_Generator::prepare_data_attributes_string().
1032 + 'data' => array(
1033 + 'placeholder' => __( 'Any IP Address', 'stream' ),
1034 + 'nonce' => wp_create_nonce( 'stream_get_ips' ),
1035 + ),
1036 + 'multiple' => true,
1037 + ),
1038 + false
873 1039 );
874 1040
875 - // Hidden helper input
1041 + // Hidden helper input.
876 1042 $helper_input = sprintf(
877 1043 '<input type="hidden" name="%1$s[%2$s_%3$s][%4$s][]" value="" />',
878 1044 esc_attr( $option_key ),
879 1045 esc_attr( $section ),
@@ -884,23 +1050,26 @@
884 1050 $exclude_rows[] = sprintf(
885 1051 '<tr class="%1$s %2$s">
886 1052 <th scope="row" class="check-column">%3$s %4$s</th>
887 1053 <td>%5$s</td>
888 - <td>%6$s %7$s</td>
889 - <td>%8$s</td>
1054 + <td>%6$s %7$s %8$s</td>
890 1055 <td>%9$s</td>
891 - <th scope="row" class="actions-column">%10$s</th>
1056 + <td>%10$s</td>
1057 + <th scope="row" class="actions-column">
1058 + <a href="#" class="exclude_rules_remove_rule_row">%11$s</a>
1059 + </th>
892 1060 </tr>',
893 - ( 0 !== $key % 2 ) ? 'alternate' : '',
894 - ( 'helper' === $key ) ? 'hidden helper' : '',
1061 + ( 0 !== (int) $key % 2 ) ? 'alternate' : '',
1062 + ( 'helper' === (string) $key ) ? 'hidden helper' : '',
895 1063 '<input class="cb-select" type="checkbox" />',
896 1064 $helper_input,
897 1065 $author_or_role_input,
1066 + $connector_or_context_input,
898 1067 $connector_input,
899 1068 $context_input,
900 1069 $action_input,
901 1070 $ip_address_input,
902 - '<a href="#" class="exclude_rules_remove_rule_row">Delete</a>'
1071 + esc_html__( 'Delete', 'stream' )
903 1072 );
904 1073 }
905 1074
906 1075 $no_rules_found_row = sprintf(
@@ -925,9 +1094,9 @@
925 1094
926 1095 /**
927 1096 * Render Callback for post_types field
928 1097 *
929 - * @param array $field
1098 + * @param array $field Field to be rendered.
930 1099 *
931 1100 * @return string
932 1101 */
933 1102 public function output_field( $field ) {
@@ -938,9 +1107,9 @@
938 1107 }
939 1108
940 1109 $output = $this->render_field( $field );
941 1110
942 - echo $output; // xss ok
1111 + echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
943 1112 }
944 1113
945 1114 /**
946 1115 * Get an array of user roles
@@ -960,9 +1129,9 @@
960 1129
961 1130 /**
962 1131 * Function will return all terms labels of given column
963 1132 *
964 - * @param string $column string Name of the column
1133 + * @param string $column Name of the column.
965 1134 *
966 1135 * @return array
967 1136 */
968 1137 public function get_terms_labels( $column ) {
@@ -995,14 +1164,14 @@
995 1164 * Remove records when records TTL is shortened
996 1165 *
997 1166 * @action update_option_wp_stream
998 1167 *
999 - * @param array $old_value
1000 - * @param array $new_value
1168 + * @param array $old_value Old value.
1169 + * @param array $new_value New value.
1001 1170 */
1002 1171 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;
1172 + $ttl_before = isset( $old_value['general_records_ttl'] ) ? (int) $old_value['general_records_ttl'] : - 1;
1173 + $ttl_after = isset( $new_value['general_records_ttl'] ) ? (int) $new_value['general_records_ttl'] : - 1;
1005 1174
1006 1175 if ( $ttl_after < $ttl_before ) {
1007 1176 /**
1008 1177 * Action assists in purging when TTL is shortened
@@ -1014,8 +1183,10 @@
1014 1183 /**
1015 1184 * Get translations of serialized Stream settings
1016 1185 *
1017 1186 * @filter wp_stream_serialized_labels
1187 + *
1188 + * @param array $labels Setting labels.
1018 1189 *
1019 1190 * @return array Multidimensional array of fields
1020 1191 */
1021 1192 public function get_settings_translations( $labels ) {