diff --git a/classes/class-admin.php b/classes/class-admin.php index 1ad12a5b1..d5f6ce369 100644 --- a/classes/class-admin.php +++ b/classes/class-admin.php @@ -363,6 +363,7 @@ public function admin_enqueue_scripts( $hook ) { wp_enqueue_script( 'wp-stream-timeago-locale' ); wp_enqueue_script( 'wp-stream-admin', $this->plugin->locations['url'] . 'ui/js/admin.js', array( 'jquery', 'wp-stream-select2' ), $this->plugin->get_version() ); + wp_enqueue_script( 'wp-stream-admin-exclude', $this->plugin->locations['url'] . 'ui/js/exclude.js', array( 'jquery', 'wp-stream-select2' ), $this->plugin->get_version() ); wp_enqueue_script( 'wp-stream-live-updates', $this->plugin->locations['url'] . 'ui/js/live-updates.js', array( 'jquery', 'heartbeat' ), $this->plugin->get_version() ); wp_localize_script( diff --git a/classes/class-form-generator.php b/classes/class-form-generator.php new file mode 100644 index 000000000..00566d8f0 --- /dev/null +++ b/classes/class-form-generator.php @@ -0,0 +1,208 @@ +fields[] = array( + 'type' => $field_type, + 'args' => $args, + ); + } + + /** + * Renders all fields currently registered. + * + * @return string + */ + public function render_fields() { + $output = ''; + foreach ( $this->fields as $data ) { + $output .= $this->render_field( $data['type'], $data['args'] ); + } + return $output; + } + + /** + * Renders all fields currently registered as a table. + * + * @return string + */ + public function render_fields_table() { + $output = '
| ' . $title . ' | '; + $output .= $this->render_field( $data['type'], $data['args'] ); + $output .= ' |
|---|---|
%s
', $args['description'] ) ) : null; + + return $output; + } + + /** + * Prepares string with HTML data attributes + * + * @param $data array List of key/value data pairs to prepare. + * @return string + */ + public function prepare_data_attributes_string( $data ) { + $output = ''; + foreach ( $data as $key => $value ) { + $key = 'data-' . esc_attr( $key ); + $output .= $key . '="' . esc_attr( $value ) . '" '; + } + return $output; + } +} diff --git a/classes/class-list-table.php b/classes/class-list-table.php index ab6451525..1287f71b7 100644 --- a/classes/class-list-table.php +++ b/classes/class-list-table.php @@ -57,9 +57,6 @@ function extra_tablenav( $which ) { if ( 'top' === $which ) { echo $this->filters_form(); // xss ok } - if ( 'bottom' === $which ) { - echo $this->record_actions_form(); // xss ok - } } function no_items() { @@ -572,13 +569,19 @@ public function get_filters() { } function filters_form() { - $user_id = get_current_user_id(); $filters = $this->get_filters(); $filters_string = sprintf( '', 'wp_stream' ); $filters_string .= sprintf( '', esc_html__( 'Show filter controls via the screen options tab above.', 'stream' ) ); foreach ( $filters as $name => $data ) { + + $data = wp_parse_args( $data, array( + 'title' => '', + 'items' => array(), + 'ajax' => false, + ) ); + if ( 'date' === $name ) { $filters_string .= $this->filter_date( $data['items'] ); } else { @@ -617,8 +620,11 @@ function filters_form() { // Sort top-level items by label array_multisort( $labels, SORT_ASC, $data['items'] ); - // Ouput a hidden input to handle the connector value - $filters_string .= ''; + // Output a hidden input to handle the connector value + $filters_string .= sprintf( + '', + esc_attr( wp_stream_filter_input( INPUT_GET, 'connector' ) ) + ); } $filters_string .= $this->filter_select( $name, $data['title'], $data['items'] ); @@ -627,8 +633,6 @@ function filters_form() { $filters_string .= sprintf( '', __( 'Filter', 'stream' ) ); - $filters_string .= wp_nonce_field( 'stream_filters_user_search_nonce', 'stream_filters_user_search_nonce' ); - // Parse all query vars into an array $query_vars = array(); @@ -836,7 +840,18 @@ function record_actions_form() { } echo ''; wp_nonce_field( 'stream_record_actions_nonce', 'stream_record_actions_nonce' ); + + printf( '', esc_attr( wp_stream_filter_input( INPUT_GET, 'page' ) ) ); + printf( '', esc_attr( wp_stream_filter_input( INPUT_GET, 'date_predefined' ) ) ); + printf( '', esc_attr( wp_stream_filter_input( INPUT_GET, 'date_from' ) ) ); + printf( '', esc_attr( wp_stream_filter_input( INPUT_GET, 'date_to' ) ) ); + printf( '', esc_attr( wp_stream_filter_input( INPUT_GET, 'user_id' ) ) ); + printf( '', esc_attr( wp_stream_filter_input( INPUT_GET, 'connector' ) ) ); + printf( '', esc_attr( wp_stream_filter_input( INPUT_GET, 'context' ) ) ); + printf( '', esc_attr( wp_stream_filter_input( INPUT_GET, 'action' ) ) ); + printf( '', esc_attr__( 'Apply', 'stream' ) ); + echo ''; return ob_get_clean(); } @@ -846,9 +861,12 @@ function display() { echo ''; + + echo ''; } function display_tablenav( $which ) { diff --git a/classes/class-log.php b/classes/class-log.php index f53bae43b..1231db4f0 100644 --- a/classes/class-log.php +++ b/classes/class-log.php @@ -220,7 +220,13 @@ public function is_record_excluded( $connector, $context, $action, $user = null, $excluded = true; foreach ( $exclude_rules as $exclude_key => $exclude_value ) { - if ( $record[ $exclude_key ] !== $exclude_value ) { + if ( 'ip_address' === $exclude_key ) { + $ip_addresses = explode( ',', $exclude_value ); + if ( ! in_array( $record['ip_address'], $ip_addresses, true ) ) { + $excluded = false; + break; + } + } elseif ( $record[ $exclude_key ] !== $exclude_value ) { $excluded = false; break; } diff --git a/classes/class-settings.php b/classes/class-settings.php index 830f9a319..8de61ef3d 100644 --- a/classes/class-settings.php +++ b/classes/class-settings.php @@ -84,11 +84,8 @@ public function get_users() { 'message' => esc_html__( 'There was an error in the request', 'stream' ), ); - $search = ''; - if ( isset( $_POST['find'] ) ) { - // @TODO: Make this pass phpcs - $search = esc_html( wp_unslash( trim( $_POST['find'] ) ) ); // input var okay - } + $search = wp_unslash( trim( wp_stream_filter_input( INPUT_POST, 'find' ) ) ); + $request = (object) array( 'find' => $search, ); @@ -169,7 +166,7 @@ function( $a, $b ) { if ( empty( $search ) || preg_match( '/wp|cli|system|unknown/i', $search ) ) { $author = new Author( 0 ); $response->users[] = array( - 'id' => $author->id, + 'id' => '0', 'text' => $author->get_display_name(), 'icon' => $author->get_avatar_src( 32 ), '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' ), @@ -189,7 +186,14 @@ public function get_ips() { check_ajax_referer( 'stream_get_ips', 'nonce' ); - $ips = $this->plugin->db->existing_records( 'ip' ); + $ips = $this->plugin->db->existing_records( 'ip' ); + $find = wp_stream_filter_input( INPUT_POST, 'find' ); + + if ( isset( $find['term'] ) && '' !== $find['term'] ) { + $ips = array_filter( $ips, function ( $ip ) use ( $find ) { + return 0 === strpos( $ip, $find['term'] ); + } ); + } if ( $ips ) { wp_send_json_success( $ips ); @@ -745,6 +749,7 @@ public function render_field( $field ) { break; case 'rule_list' : + $form = new Form_Generator; $output = '' . esc_html( $description ) . '
'; $actions_top = sprintf( '', esc_attr( $section . '_' . $name ), esc_html__( 'Add New Rule', 'stream' ) ); @@ -790,7 +795,7 @@ public function render_field( $field ) { $author_or_role_selected = array(); foreach ( $this->get_roles() as $role_id => $role ) { - $args = array( 'id' => $role_id, 'text' => $role ); + $args = array( 'value' => $role_id, 'text' => $role ); $users = count_users(); $count = isset( $users['avail_roles'][ $role_id ] ) ? $users['avail_roles'][ $role_id ] : 0; @@ -799,8 +804,8 @@ public function render_field( $field ) { } if ( $role_id === $author_or_role ) { - $author_or_role_selected['id'] = $role_id; - $author_or_role_selected['text'] = $role; + $author_or_role_selected['value'] = $role_id; + $author_or_role_selected['text'] = $role; } $author_or_role_values[] = $args; @@ -809,21 +814,21 @@ public function render_field( $field ) { if ( empty( $author_or_role_selected ) && is_numeric( $author_or_role ) ) { $user = new WP_User( $author_or_role ); $display_name = ( 0 === $user->ID ) ? esc_html__( 'N/A', 'stream' ) : $user->display_name; - $author_or_role_selected = array( 'id' => $user->ID, 'text' => $display_name ); + $author_or_role_selected = array( 'value' => $user->ID, 'text' => $display_name ); + $author_or_role_values[] = $author_or_role_selected; } - $author_or_role_input = sprintf( - '', - esc_attr( $option_key ), - esc_attr( $section ), - esc_attr( $name ), - 'author_or_role', - esc_attr( wp_stream_json_encode( $author_or_role_values ) ), - isset( $author_or_role_selected['id'] ) ? esc_attr( $author_or_role_selected['id'] ) : '', - isset( $author_or_role_selected['text'] ) ? esc_attr( $author_or_role_selected['text'] ) : '', - esc_html__( 'Any Author or Role', 'stream' ), - esc_attr( wp_create_nonce( 'stream_get_users' ) ) - ); + $author_or_role_input = $form->render_field( 'select2', array( + 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]' , $option_key, $section, $name, 'author_or_role' ) ), + 'options' => $author_or_role_values, + 'classes' => 'author_or_role', + 'data' => array( + 'placeholder' => esc_html__( 'Any Author or Role', 'stream' ), + 'nonce' => esc_attr( wp_create_nonce( 'stream_get_users' ) ), + 'selected-id' => isset( $author_or_role_selected['value'] ) ? esc_attr( $author_or_role_selected['value'] ) : '', + 'selected-text' => isset( $author_or_role_selected['text'] ) ? esc_attr( $author_or_role_selected['text'] ) : '', + ), + ) ); // Context dropdown menu $context_values = array(); @@ -834,67 +839,67 @@ public function render_field( $field ) { if ( isset( $context_data['children'] ) ) { $child_values = array(); foreach ( $context_data['children'] as $child_id => $child_value ) { - $child_values[] = array( 'id' => $child_id, 'text' => $child_value, 'parent' => $context_id ); + $child_values[] = array( 'value' => $context_id . '-' . $child_id, 'text' => $child_value, 'parent' => $context_id ); } } if ( isset( $context_data['label'] ) ) { - $context_values[] = array( 'id' => $context_id, 'text' => $context_data['label'], 'children' => $child_values ); + $context_values[] = array( 'value' => $context_id, 'text' => $context_data['label'], 'children' => $child_values ); } } else { - $context_values[] = array( 'id' => $context_id, 'text' => $context_data ); + $context_values[] = array( 'value' => $context_id, 'text' => $context_data ); } } - $connector_input = sprintf( - '', - esc_attr( $option_key ), - esc_attr( $section ), - esc_attr( $name ), - esc_attr( 'connector' ), - esc_attr( $connector ) - ); + $connector_or_context_input = $form->render_field( 'select2', array( + 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]' , $option_key, $section, $name, 'connector_or_context' ) ), + 'options' => $context_values, + 'classes' => 'connector_or_context', + 'data' => array( + 'group' => 'connector', + 'placeholder' => __( 'Any Context', 'stream' ), + ), + ) ); - $context_input = sprintf( - '', - esc_attr( $option_key ), - esc_attr( $section ), - esc_attr( $name ), - 'context', - esc_attr( wp_stream_json_encode( $context_values ) ), - esc_attr( $context ), - esc_html__( 'Any Context', 'stream' ), - 'connector' - ); + $connector_input = $form->render_field( 'hidden', array( + 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]' , $option_key, $section, $name, 'connector' ) ), + 'value' => $connector, + 'classes' => 'connector', + ) ); + + $context_input = $form->render_field( 'hidden', array( + 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]' , $option_key, $section, $name, 'context' ) ), + 'value' => $context, + 'classes' => 'context', + ) ); // Action dropdown menu $action_values = array(); foreach ( $this->get_terms_labels( 'action' ) as $action_id => $action_data ) { - $action_values[] = array( 'id' => $action_id, 'text' => $action_data ); + $action_values[] = array( 'value' => $action_id, 'text' => $action_data ); } - $action_input = sprintf( - '', - esc_attr( $option_key ), - esc_attr( $section ), - esc_attr( $name ), - 'action', - esc_attr( wp_stream_json_encode( $action_values ) ), - esc_attr( $action ), - esc_html__( 'Any Action', 'stream' ) - ); + $action_input = $form->render_field( 'select2', array( + 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]' , $option_key, $section, $name, 'action' ) ), + 'value' => $action, + 'options' => $action_values, + 'classes' => 'action', + 'data' => array( + 'placeholder' => __( 'Any Action', 'stream' ), + ), + ) ); // IP Address input - $ip_address_input = sprintf( - '', - esc_attr( $option_key ), - esc_attr( $section ), - esc_attr( $name ), - 'ip_address', - esc_attr( $ip_address ), - esc_html__( 'Any IP Address', 'stream' ), - esc_attr( wp_create_nonce( 'stream_get_ips' ) ) - ); + $ip_address_input = $form->render_field( 'select2', array( + 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]' , $option_key, $section, $name, 'ip_address' ) ), + 'value' => $ip_address, + 'classes' => 'ip_address', + 'data' => array( + 'placeholder' => esc_attr__( 'Any IP Address', 'stream' ), + 'nonce' => esc_attr( wp_create_nonce( 'stream_get_ips' ) ), + ), + 'multiple' => true, + ) ); // Hidden helper input $helper_input = sprintf( @@ -909,16 +914,17 @@ public function render_field( $field ) { '