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

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