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 = ''; + foreach ( $this->fields as $data ) { + $title = ( array_key_exists( 'title', $data['args'] ) ) ? $data['args']['title'] : ''; + + $output .= ''; + } + $output .= '
' . $title . ''; + $output .= $this->render_field( $data['type'], $data['args'] ); + $output .= '
'; + return $output; + } + + /** + * Renders a single field. + * + * @param string $field_type The type of field being rendered. + * @param array $args The options for the field type. + * + * @return string + */ + public function render_field( $field_type, $args ) { + $args = wp_parse_args( $args, array( + 'name' => '', + 'value' => '', + 'options' => array(), + 'description' => '', + 'classes' => '', + 'data' => array(), + 'multiple' => false, + ) ); + + $output = ''; + switch ( $field_type ) { + case 'text': + $output = sprintf( + '', + esc_attr( $args['name'] ), + esc_attr( $args['classes'] ), + esc_attr( $args['value'] ) + ); + break; + case 'hidden': + $output = sprintf( + '', + esc_attr( $args['name'] ), + esc_attr( $args['classes'] ), + esc_attr( $args['value'] ) + ); + break; + case 'select': + $current_value = $args['value']; + + $output = sprintf( + ''; + break; + case 'select2': + $values = array(); + + $multiple = ( $args['multiple'] ) ? 'multiple ' : ''; + $output = sprintf( + ''; + break; + case 'checkbox': + $output = sprintf( + '%2$s', + $args['name'], + $args['text'], + checked( $args['value'], true, false ) + ); + break; + default: + $output = apply_filters( 'wp_stream_form_render_field', $output, $field_type, $args ); + break; + } + + $output .= ! empty( $args['description'] ) ? wp_kses_post( sprintf( '

%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 $this->filter_search(); // xss ok - parent::display(); echo '
'; + + echo '
'; + echo $this->record_actions_form(); // xss ok + 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 ) { ' %3$s %4$s %5$s - %6$s %7$s - %8$s + %6$s %7$s %8$s %9$s - %10$s + %10$s + %11$s ', ( 0 !== $key % 2 ) ? 'alternate' : '', ( 'helper' === $key ) ? 'hidden helper' : '', '', $helper_input, $author_or_role_input, + $connector_or_context_input, $connector_input, $context_input, $action_input, diff --git a/ui/css/admin.css b/ui/css/admin.css index 3f3a56cd3..e4fc9e767 100644 --- a/ui/css/admin.css +++ b/ui/css/admin.css @@ -4,8 +4,13 @@ padding-top: 6px; } -.toplevel_page_wp_stream .tablenav.bottom .button { - margin-right: 6px; +.toplevel_page_wp_stream #record-actions-form { + margin-top: -32px; +} + +.toplevel_page_wp_stream #record-actions-form .button { + margin-left: 6px; + float: left; } .toplevel_page_wp_stream .tablenav .actions { @@ -13,14 +18,28 @@ overflow: visible; } -.toplevel_page_wp_stream .tablenav .select2-selection { +.wp_stream_screen .select2 .select2-selection { border-color: #ccc; background: #f7f7f7; -webkit-box-shadow: 0 1px 0 #ccc; box-shadow: 0 1px 0 #ccc; } -.toplevel_page_wp_stream .tablenav .select2-selection .select2-selection__rendered { +.wp_stream_screen .select2 .select2-selection--multiple { + font-size: 0; + min-height: 28px; +} + +.wp_stream_screen .select2-container.select2-container--focus .select2-selection--multiple { + border: solid #ccc 1px; +} + +.wp_stream_screen .select2-container .select2-selection--multiple .select2-selection__choice { + margin-top: 4px; + margin-bottom: 3px; +} + +.wp_stream_screen .select2 .select2-selection .select2-selection__rendered { color: #555; } @@ -235,10 +254,11 @@ /* Settings */ -.wp_stream_settings .select2-container, -.wp_stream_network_settings .select2-container, -.wp_stream_default_settings .select2-container { - width: 100%; +.wp_stream_settings .select2.select2-container, +.wp_stream_network_settings .select2.select2-container, +.wp_stream_default_settings .select2.select2-container { + min-width: 160px; + max-width: 100%; } .wp_stream_settings .tablenav, @@ -336,16 +356,8 @@ color: #999; } -.wp_stream_screen li.select2-result { - margin-bottom: 0; -} - -.wp_stream_screen li.select2-result.level-1 { - font-weight: bold; -} - -.wp_stream_screen li.select2-result.level-2 { - padding-left: 1em; +.wp_stream_screen .select2 .select2-selection .select2-selection__placeholder { + color: #72777c; } .wp_stream_screen .select2-results .select2-disabled { @@ -353,15 +365,21 @@ color: #aaa; } -.wp_stream_screen .select2-search-choice .icon16 { - margin: -6px 1px 0 -3px; +.wp_stream_screen .select2 .select2-search--inline { + float: none; + margin-bottom: 4px; + margin-left: 2px; +} + +.wp_stream_screen .select2-selection .icon16 { + margin: -3px 1px 0 -3px; padding: 0; width: 16px; height: 16px; } -.wp_stream_screen .select2-chosen .icon16 { - padding-right: 1px; +.wp_stream_screen .select2-selection .icon16 { + padding-right: 8px; } .wp_stream_screen .select2-chosen .icon16:before, @@ -452,6 +470,14 @@ border: 1px solid rgba(160,0,0,0.75); } +.wp_stream_screen .select2-results__option .parent { + font-weight: bold; +} + +.wp_stream_screen .select2-results__option .child { + padding-left: 8px; +} + @media screen and ( max-width: 900px ) { .wp_stream_settings .stream-exclude-list .actions-column, .wp_stream_network_settings .stream-exclude-list .actions-column, @@ -461,6 +487,11 @@ } @media screen and ( max-width: 782px ) { + .toplevel_page_wp_stream #record-actions-form { + margin-top: 0; + margin-bottom: 35px; + } + .wp_stream_settings .stream-exclude-list td, .wp_stream_network_settings .stream-exclude-list td, .wp_stream_default_settings .stream-exclude-list td { diff --git a/ui/js/admin.js b/ui/js/admin.js index 725b6f515..14a01071f 100644 --- a/ui/js/admin.js +++ b/ui/js/admin.js @@ -15,8 +15,8 @@ jQuery( function( $ ) { $( '.toplevel_page_wp_stream :input.chosen-select' ).each( function( i, el ) { var args = {}, - formatResult = function( record, container ) { - var result = '', + templateResult = function( record ) { + var $result = $( '' ), $elem = $( record.element ), icon = ''; @@ -24,23 +24,28 @@ jQuery( function( $ ) { record.text = record.text.substring( 2 ); } + if ( 'undefined' !== typeof record.id ) { + if ( record.id.indexOf( 'group-' ) === 0 ) { + $result.addClass( 'parent' ); + } else if ( $elem.hasClass( 'level-2' ) ) { + $result.addClass( 'child' ); + } + } + if ( undefined !== record.icon ) { icon = record.icon; - } else if ( undefined !== $elem.attr( 'data-icon' ) ) { + } else if ( undefined !== $elem && '' !== $elem.data( 'icon' ) ) { icon = $elem.data( 'icon' ); } - if ( icon ) { - result += ''; - } - - result += record.text; - // Add more info to the container - container.attr( 'title', $elem.attr( 'title' ) ); + if ( icon ) { + $result.html( '' ); + } + $result.append( record.text ); - return result; + return $result; }, - formatSelection = function( record ) { + templateSelection = function( record ) { if ( '- ' === record.text.substring( 0, 2 ) ) { record.text = record.text.substring( 2 ); } @@ -50,8 +55,8 @@ jQuery( function( $ ) { if ( $( el ).find( 'option' ).not( ':selected' ).not( ':empty' ).length > 0 ) { args = { minimumResultsForSearch: 10, - formatResult: formatResult, - formatSelection: formatSelection, + templateResult: templateResult, + templateSelection: templateSelection, allowClear: true, width: '165px' }; @@ -85,8 +90,8 @@ jQuery( function( $ ) { }; } }, - formatResult: formatResult, - formatSelection: formatSelection + templateResult: templateResult, + templateSelection: templateSelection }; } @@ -94,10 +99,11 @@ jQuery( function( $ ) { }); var $queryVars = $.streamGetQueryVars(); - var $contextInput = $( '.toplevel_page_wp_stream :input.chosen-select[name="context"]' ); + var $contextInput = $( '.toplevel_page_wp_stream select.chosen-select[name="context"]' ); if ( ( 'undefined' === typeof $queryVars.context || '' === $queryVars.context ) && 'undefined' !== typeof $queryVars.connector ) { - $contextInput.select2( 'val', 'group-' + $queryVars.connector ); + $contextInput.val( 'group-' + $queryVars.connector ); + $contextInput.trigger( 'change' ); } $( 'input[type=submit]', '#record-filter-form' ).click( function() { diff --git a/ui/js/exclude.js b/ui/js/exclude.js new file mode 100644 index 000000000..5d928b850 --- /dev/null +++ b/ui/js/exclude.js @@ -0,0 +1,378 @@ +/* globals ajaxurl, wp_stream_regenerate_alt_rows */ +jQuery( function( $ ) { + var initSettingsSelect2 = function() { + var $input_user; + + $( '.stream-exclude-list tr:not(.hidden) select.select2-select.connector_or_context' ).each( function( k, el ) { + $( el ).select2({ + allowClear: true, + templateResult : function( item ) { + if ( typeof item.id === 'undefined' ) { + return item.text; + } + if ( item.id.indexOf( '-' ) === -1 ) { + return $( '' + item.text + '' ); + } else { + return $( '' + item.text + '' ); + } + }, + matcher: function( params, data ) { + var match = $.extend( true, {}, data ); + + if ( params.term == null || $.trim( params.term ) === '') { + return match; + } + + var term = params.term.toLowerCase(); + + match.id = match.id.replace( 'blogs', 'sites' ); + if ( match.id.toLowerCase().indexOf( term ) >= 0 ) { + return match; + } + + if ( match.children ) { + + for ( var i = match.children.length - 1; i >= 0; i--) { + var child = match.children[i]; + + // Remove term from results if it doesn't match. + if ( child.id.toLowerCase().indexOf( term ) === -1 ) { + match.children.splice( i, 1 ); + } + } + + if ( match.children.length > 0 ) { + return match; + } + } + + return null; + } + }); + }); + + $( '.stream-exclude-list tr:not(.hidden) select.select2-select.action' ).each( function( k, el ) { + $( el ).select2({ + allowClear: true + }); + }); + + $( '.stream-exclude-list tr:not(.hidden) select.select2-select.author_or_role' ).each( function( k, el ) { + $input_user = $( el ); + + $input_user.select2({ + ajax: { + type: 'POST', + url: ajaxurl, + dataType: 'json', + quietMillis: 500, + data: function( term, page ) { + return { + find: term, + limit: 10, + pager: page, + action: 'stream_get_users', + nonce: $input_user.data( 'nonce' ) + }; + }, + processResults: function( response ) { + var roles = []; + + $( 'option', $input_user ).each( function() { + if ( $( this ).val() === '' ) { + return; + } + if ( ! $.isNumeric( $( this ).val() ) ) { + roles.push({ + 'id' : $( this ).val(), + 'text' : $( this ).text() + }); + } + }); + + roles = $.grep( + roles, + function( role ) { + var roleVal = $input_user.data( 'select2' ).dropdown.$search + .val() + .toLowerCase(); + var rolePos = role + .text + .toLowerCase() + .indexOf( roleVal ); + return rolePos >= 0; + } + ); + + var answer = { + results: [ + { text: '', id: '' }, + { text: 'Roles', children: roles }, + { text: 'Users', children: [] } + ] + }; + + if ( true !== response.success || undefined === response.data || true !== response.data.status ) { + return answer; + } + + $.each( response.data.users, function( k, user ) { + if ( $.contains( roles, user.id ) ) { + user.disabled = true; + } + }); + + answer.results[ 2 ].children = response.data.users; + + // Notice we return the value of more so Select2 knows if more results can be loaded + return answer; + } + }, + templateResult: function( object ) { + var $result = $( '
' ).text( object.text ); + + if ( 'undefined' !== typeof object.icon && object.icon ) { + $result.prepend( $( '' ) ); + + // Add more info to the container + $result.attr( 'title', object.tooltip ); + } + + // Add more info to the container + if ( 'undefined' !== typeof object.tooltip ) { + $result.attr( 'title', object.tooltip ); + } else if ( 'undefined' !== typeof object.user_count ) { + $result.attr( 'title', object.user_count ); + } + + return $result; + }, + templateSelection: function( object ) { + var $result = $( '
' ).text( object.text ); + + if ( $.isNumeric( object.id ) && object.text.indexOf( 'icon-users' ) < 0 ) { + $result.append( $( '' ) ); + } + + return $result; + }, + allowClear: true, + placeholder: $input_user.data( 'placeholder' ) + }).on( 'change', function() { + var value = $( this ).select2( 'data' ); + + $( this ).data( 'selected-id', value.id ); + $( this ).data( 'selected-text', value.text ); + }); + }); + + $( '.stream-exclude-list tr:not(.hidden) select.select2-select.ip_address' ).each( function( k, el ) { + var $input_ip = $( el ), + searchTerm = ''; + + $input_ip.select2({ + ajax: { + type: 'POST', + url: ajaxurl, + dataType: 'json', + quietMillis: 500, + data: function( term ) { + searchTerm = term.term; + return { + find: term, + limit: 10, + action: 'stream_get_ips', + nonce: $input_ip.data( 'nonce' ) + }; + }, + processResults: function( response ) { + var answer = { results: [] }, + ip_chunks = []; + + if ( true === response.success && undefined !== response.data ) { + $.each( response.data, function( key, ip ) { + answer.results.push({ + id: ip, + text: ip + }); + }); + } + + if ( undefined === searchTerm ) { + return answer; + } + + ip_chunks = searchTerm.match( /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ ); + + if ( null === ip_chunks ) { + return answer; + } + + // remove whole match + ip_chunks.shift(); + + ip_chunks = $.grep( + ip_chunks, + function( chunk ) { + var numeric = parseInt( chunk, 10 ); + return numeric <= 255 && numeric.toString() === chunk; + } + ); + + if ( ip_chunks.length >= 4 ) { + answer.results.push({ + id: searchTerm, + text: searchTerm + }); + } + + return answer; + } + }, + allowClear: false, + multiple: true, + maximumSelectionSize: 1, + placeholder: $input_ip.data( 'placeholder' ), + tags: true + }); + }).on( 'change', function() { + $( this ).prev( '.select2-container' ).find( 'input.select2-input' ).blur(); + }); + + $( 'ul.select2-choices, ul.select2-choices li, input.select2-input', '.stream-exclude-list tr:not(.hidden) .ip_address' ).on( 'mousedown click focus', function() { + var $container = $( this ).closest( '.select2-container' ), + $input = $container.find( 'input.select2-input' ), + value = $container.select2( 'data' ); + + if ( value.length >= 1 ) { + $input.blur(); + return false; + } + }); + + $( '.stream-exclude-list tr:not(.hidden) .exclude_rules_remove_rule_row' ).on( 'click', function() { + var $thisRow = $( this ).closest( 'tr' ); + + $thisRow.remove(); + + recalculate_rules_found(); + recalculate_rules_selected(); + }); + }; + + initSettingsSelect2(); + + $( '.stream-exclude-list tr:not(.hidden) select.select2-select.author_or_role' ).each( function() { + $( this ).val( $( this ).data( 'selected-id' ) ).trigger( 'change' ); + }); + + $( '.stream-exclude-list tr:not(.hidden) select.select2-select.connector_or_context' ).each( function() { + var parts = [ + $( this ).siblings( '.connector' ).val(), + $( this ).siblings( '.context' ).val() + ]; + if ( parts[1] === '' ) { + parts.splice( 1, 1 ); + } + $( this ).val( parts.join( '-' ) ).trigger( 'change' ); + }); + + $( '#exclude_rules_new_rule' ).on( 'click', function() { + var $excludeList = $( 'table.stream-exclude-list' ); + + $( 'tr:not(.hidden) select.select2-select', $excludeList ).each( function() { + $( this ).select2( 'destroy' ); + }); + + var $lastRow = $( 'tr', $excludeList ).last(), + $newRow = $lastRow.clone(); + + $newRow.removeAttr( 'class' ); + $( '.stream-exclude-list tbody :input' ).off(); + $( ':input', $newRow ).off().val( '' ); + + $lastRow.after( $newRow ); + + initSettingsSelect2(); + + recalculate_rules_found(); + recalculate_rules_selected(); + }); + + $( '#exclude_rules_remove_rules' ).on( 'click', function() { + var $excludeList = $( 'table.stream-exclude-list' ), + selectedRows = $( 'tbody input.cb-select:checked', $excludeList ).closest( 'tr' ); + + if ( ( $( 'tbody tr', $excludeList ).length - selectedRows.length ) >= 2 ) { + selectedRows.remove(); + } else { + $( ':input', selectedRows ).val( '' ); + $( selectedRows ).not( ':first' ).remove(); + $( '.select2-select', selectedRows ).select2( 'val', '' ); + } + + $excludeList.find( 'input.cb-select' ).prop( 'checked', false ); + + recalculate_rules_found(); + recalculate_rules_selected(); + }); + + $( '.stream-exclude-list' ).closest( 'form' ).submit( function() { + $( '.stream-exclude-list tbody tr.hidden', this ).each( function() { + $( this ).find( ':input' ).removeAttr( 'name' ); + }); + $( '.stream-exclude-list tbody tr:not(.hidden) select.select2-select.connector_or_context', this ).each( function() { + var parts = $( this ).val().split( '-' ); + $( this ).siblings( '.connector' ).val( parts[0] ); + $( this ).siblings( '.context' ).val( parts[1] ); + $( this ).removeAttr( 'name' ); + }); + $( '.stream-exclude-list tbody tr:not(.hidden) select.select2-select.ip_address', this ).each( function() { + var firstSelected = $( 'option:selected', this ).first(); + $( 'option:selected:not(:first)', this ).each( function() { + firstSelected.attr( 'value', firstSelected.attr( 'value' ) + ',' + $( this ).attr( 'value' ) ); + $( this ).removeAttr( 'selected' ); + }); + }); + }); + + $( '.stream-exclude-list' ).closest( 'td' ).prev( 'th' ).hide(); + + $( 'table.stream-exclude-list' ).on( 'click', 'input.cb-select', function() { + recalculate_rules_selected(); + }); + + function recalculate_rules_selected() { + var $selectedRows = $( 'table.stream-exclude-list tbody tr:not( .hidden ) input.cb-select:checked' ), + $deleteButton = $( '#exclude_rules_remove_rules' ); + + if ( 0 === $selectedRows.length ) { + $deleteButton.prop( 'disabled', true ); + } else { + $deleteButton.prop( 'disabled', false ); + } + } + + function recalculate_rules_found() { + var $allRows = $( 'table.stream-exclude-list tbody tr:not( .hidden )' ), + $noRulesFound = $( 'table.stream-exclude-list tbody tr.no-items' ), + $selectAll = $( '.check-column.manage-column input.cb-select' ), + $deleteButton = $( '#exclude_rules_remove_rules' ); + + if ( 0 === $allRows.length ) { + $noRulesFound.show(); + $selectAll.prop( 'disabled', true ); + $deleteButton.prop( 'disabled', true ); + } else { + $noRulesFound.hide(); + $selectAll.prop( 'disabled', false ); + } + + wp_stream_regenerate_alt_rows( $allRows ); + } + + $( document ).ready( function() { + recalculate_rules_found(); + recalculate_rules_selected(); + }); +}); diff --git a/ui/js/settings.js b/ui/js/settings.js index 115d384ec..250afda43 100644 --- a/ui/js/settings.js +++ b/ui/js/settings.js @@ -1,238 +1,6 @@ /* globals confirm, wp_stream, ajaxurl, wp_stream_regenerate_alt_rows */ jQuery( function( $ ) { - var initSettingsSelect2 = function() { - $( '.stream-exclude-list tr:not(.hidden) input[type=hidden].select2-select.with-source' ).each( function( k, el ) { - var $input = $( el ), - $connector = $( this ).prevAll( ':input.connector' ); - - $input.select2({ - data: $input.data( 'values' ), - allowClear: true, - placeholder: $input.data( 'placeholder' ) - }); - - if ( '' === $input.val() && '' !== $connector.val() ) { - $input.select2( 'val', $connector.val() ); - $input.val( '' ); - } - }); - - var $input_user, $input_ip; - - $( '.stream-exclude-list tr:not(.hidden) input[type=hidden].select2-select.ip_address' ).each( function( k, el ) { - $input_ip = $( el ); - - $input_ip.select2({ - ajax: { - type: 'POST', - url: ajaxurl, - dataType: 'json', - quietMillis: 500, - data: function( term ) { - return { - find: term, - limit: 10, - action: 'stream_get_ips', - nonce: $input_ip.data( 'nonce' ) - }; - }, - results: function( response ) { - var answer = { results: [] }; - - if ( true !== response.success || undefined === response.data ) { - return answer; - } - - $.each( response.data, function( key, ip ) { - answer.results.push({ - id: ip, - text: ip - }); - }); - - return answer; - } - }, - initSelection: function( item, callback ) { - var data = []; - - data.push( { id: item.val(), text: item.val() } ); - - callback( data ); - }, - createSearchChoice: function( term ) { - var ip_chunks = []; - - ip_chunks = term.match( /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ ); - - if ( null === ip_chunks ) { - return; - } - - // remove whole match - ip_chunks.shift(); - - ip_chunks = $.grep( - ip_chunks, - function( chunk ) { - var numeric = parseInt( chunk, 10 ); - return numeric <= 255 && numeric.toString() === chunk; - } - ); - - if ( ip_chunks.length < 4 ) { - return; - } - - return { - id: term, - text: term - }; - }, - allowClear: true, - multiple: true, - maximumSelectionSize: 1, - placeholder: $input_ip.data( 'placeholder' ) - }); - }).on( 'change', function() { - $( this ).prev( '.select2-container' ).find( 'input.select2-input' ).blur(); - }); - - $( '.stream-exclude-list tr:not(.hidden) input[type=hidden].select2-select.author_or_role' ).each( function( k, el ) { - $input_user = $( el ); - - $input_user.select2({ - ajax: { - type: 'POST', - url: ajaxurl, - dataType: 'json', - quietMillis: 500, - data: function( term, page ) { - return { - find: term, - limit: 10, - pager: page, - action: 'stream_get_users', - nonce: $input_user.data( 'nonce' ) - }; - }, - results: function( response ) { - var roles = [], - answer = []; - - roles = $.grep( - $input_user.data( 'values' ), - function( role ) { - var roleVal = $input_user.data( 'select2' ) - .search - .val() - .toLowerCase(); - var rolePos = role - .text - .toLowerCase() - .indexOf( roleVal ); - return rolePos >= 0; - } - ); - - answer = { - results: [ - { text: 'Roles', children: roles }, - { text: 'Users', children: [] } - ] - }; - - if ( true !== response.success || undefined === response.data || true !== response.data.status ) { - return answer; - } - - $.each( response.data.users, function( k, user ) { - if ( $.contains( roles, user.id ) ) { - user.disabled = true; - } - }); - - answer.results[ 1 ].children = response.data.users; - - // Notice we return the value of more so Select2 knows if more results can be loaded - return answer; - } - }, - initSelection: function( item, callback ) { - callback( { id: item.data( 'selected-id' ), text: item.data( 'selected-text' ) } ); - }, - formatResult: function( object, container ) { - var result = object.text; - - if ( 'undefined' !== typeof object.icon && object.icon ) { - result = '' + result; - - // Add more info to the container - container.attr( 'title', object.tooltip ); - } - - // Add more info to the container - if ( 'undefined' !== typeof object.tooltip ) { - container.attr( 'title', object.tooltip ); - } else if ( 'undefined' !== typeof object.user_count ) { - container.attr( 'title', object.user_count ); - } - - return result; - }, - formatSelection: function( object ) { - if ( $.isNumeric( object.id ) && object.text.indexOf( 'icon-users' ) < 0 ) { - object.text += ''; - } - - return object.text; - }, - allowClear: true, - placeholder: $input_user.data( 'placeholder' ) - }).on( 'change', function() { - var value = $( this ).select2( 'data' ); - - $( this ).data( 'selected-id', value.id ); - $( this ).data( 'selected-text', value.text ); - }); - }); - - $( 'ul.select2-choices, ul.select2-choices li, input.select2-input', '.stream-exclude-list tr:not(.hidden) .ip_address' ).on( 'mousedown click focus', function() { - var $container = $( this ).closest( '.select2-container' ), - $input = $container.find( 'input.select2-input' ), - value = $container.select2( 'data' ); - - if ( value.length >= 1 ) { - $input.blur(); - return false; - } - }); - - $( '.stream-exclude-list tr:not(.hidden) input[type=hidden].select2-select.context' ).on( 'change', function( val ) { - var $connector = $( this ).prevAll( ':input.connector' ); - - if ( undefined !== val.added && undefined !== val.added.parent ) { - $connector.val( val.added.parent ); - } else { - $connector.val( $( this ).val() ); - $( this ).val( '' ); - } - }); - - $( '.stream-exclude-list tr:not(.hidden) .exclude_rules_remove_rule_row' ).on( 'click', function() { - var $thisRow = $( this ).closest( 'tr' ); - - $thisRow.remove(); - - recalculate_rules_found(); - recalculate_rules_selected(); - }); - - }; - - initSettingsSelect2(); - var keepRecordsIndefinitely = $( '#wp_stream\\[general_keep_records_indefinitely\\]' ), keepRecordsFor = $( '#wp_stream_general_records_ttl' ), keepRecordsForRow = keepRecordsFor.closest( 'tr' ); @@ -253,95 +21,6 @@ jQuery( function( $ ) { toggleKeepRecordsFor(); - $( '#exclude_rules_new_rule' ).on( 'click', function() { - var $excludeList = $( 'table.stream-exclude-list' ); - - $( '.select2-select', $excludeList ).each( function() { - $( this ).select2( 'destroy' ); - }); - - var $lastRow = $( 'tr', $excludeList ).last(), - $newRow = $lastRow.clone(); - - $newRow.removeAttr( 'class' ); - $( '.stream-exclude-list tbody :input' ).off(); - $( ':input', $newRow ).off().val( '' ); - - $lastRow.after( $newRow ); - - initSettingsSelect2(); - - recalculate_rules_found(); - recalculate_rules_selected(); - }); - - $( '#exclude_rules_remove_rules' ).on( 'click', function() { - var $excludeList = $( 'table.stream-exclude-list' ), - selectedRows = $( 'tbody input.cb-select:checked', $excludeList ).closest( 'tr' ); - - if ( ( $( 'tbody tr', $excludeList ).length - selectedRows.length ) >= 2 ) { - selectedRows.remove(); - } else { - $( ':input', selectedRows ).val( '' ); - $( selectedRows ).not( ':first' ).remove(); - $( '.select2-select', selectedRows ).select2( 'val', '' ); - } - - $excludeList.find( 'input.cb-select' ).prop( 'checked', false ); - - recalculate_rules_found(); - recalculate_rules_selected(); - }); - - $( '.stream-exclude-list' ).closest( 'form' ).submit( function() { - $( '.stream-exclude-list tbody tr', this ).each( function() { - if ( 0 === $( this ).find( ':input[value][value!=""]' ).length ) { - // Don't send inputs in this row - $( this ).find( ':input[value]' ).removeAttr( 'name' ); - } - }); - }); - - $( '.stream-exclude-list' ).closest( 'td' ).prev( 'th' ).hide(); - - $( 'table.stream-exclude-list' ).on( 'click', 'input.cb-select', function() { - recalculate_rules_selected(); - }); - - function recalculate_rules_selected() { - var $selectedRows = $( 'table.stream-exclude-list tbody tr:not( .hidden ) input.cb-select:checked' ), - $deleteButton = $( '#exclude_rules_remove_rules' ); - - if ( 0 === $selectedRows.length ) { - $deleteButton.prop( 'disabled', true ); - } else { - $deleteButton.prop( 'disabled', false ); - } - } - - function recalculate_rules_found() { - var $allRows = $( 'table.stream-exclude-list tbody tr:not( .hidden )' ), - $noRulesFound = $( 'table.stream-exclude-list tbody tr.no-items' ), - $selectAll = $( '.check-column.manage-column input.cb-select' ), - $deleteButton = $( '#exclude_rules_remove_rules' ); - - if ( 0 === $allRows.length ) { - $noRulesFound.show(); - $selectAll.prop( 'disabled', true ); - $deleteButton.prop( 'disabled', true ); - } else { - $noRulesFound.hide(); - $selectAll.prop( 'disabled', false ); - } - - wp_stream_regenerate_alt_rows( $allRows ); - } - - $( document ).ready( function() { - recalculate_rules_found(); - recalculate_rules_selected(); - }); - // Confirmation on some important actions $( '#wp_stream_general_reset_site_settings' ).click( function( e ) { if ( ! confirm( wp_stream.i18n.confirm_defaults ) ) {