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

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