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

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