Stream - Version 3.0.7

Version Description

  • June 14, 2016 =

  • Tweak: Use get_sites instead of wp_get_sites when available (#856)

  • Tweak: More stable record actions (like exporting) (71e6ac1)

  • Tweak: Better multisite support (cfab041)

  • Fix: Exclude rule settings have been restored and enhanced (#855)

  • Fix: Loading users via ajax (#854)

  • Fix: Use the correct label for events relating to taxonomies which are registered late (#859)

Props @chacha, @lukecarbis, Eugene Kireev, @johnbillion

Download this release

Release Info

Developer lukecarbis
Plugin Icon 128x128 Stream
Version 3.0.7
Comparing to
See all releases

Code changes from version 3.0.6 to 3.0.7

classes/class-admin.php CHANGED
@@ -363,6 +363,7 @@ class Admin {
363
  wp_enqueue_script( 'wp-stream-timeago-locale' );
364
 
365
  wp_enqueue_script( 'wp-stream-admin', $this->plugin->locations['url'] . 'ui/js/admin.js', array( 'jquery', 'wp-stream-select2' ), $this->plugin->get_version() );
 
366
  wp_enqueue_script( 'wp-stream-live-updates', $this->plugin->locations['url'] . 'ui/js/live-updates.js', array( 'jquery', 'heartbeat' ), $this->plugin->get_version() );
367
 
368
  wp_localize_script(
363
  wp_enqueue_script( 'wp-stream-timeago-locale' );
364
 
365
  wp_enqueue_script( 'wp-stream-admin', $this->plugin->locations['url'] . 'ui/js/admin.js', array( 'jquery', 'wp-stream-select2' ), $this->plugin->get_version() );
366
+ wp_enqueue_script( 'wp-stream-admin-exclude', $this->plugin->locations['url'] . 'ui/js/exclude.js', array( 'jquery', 'wp-stream-select2' ), $this->plugin->get_version() );
367
  wp_enqueue_script( 'wp-stream-live-updates', $this->plugin->locations['url'] . 'ui/js/live-updates.js', array( 'jquery', 'heartbeat' ), $this->plugin->get_version() );
368
 
369
  wp_localize_script(
classes/class-form-generator.php ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WP_Stream;
3
+
4
+ class Form_Generator {
5
+
6
+ /**
7
+ * List of all registered fields.
8
+ *
9
+ * @var array
10
+ */
11
+ public $fields = array();
12
+
13
+ /**
14
+ * Adds a new field to the form.
15
+ *
16
+ * @param string $field_type The type of field being added.
17
+ * @param array $args Options for the field. See render_field().
18
+ * @return void
19
+ */
20
+ public function add_field( $field_type, $args ) {
21
+ $this->fields[] = array(
22
+ 'type' => $field_type,
23
+ 'args' => $args,
24
+ );
25
+ }
26
+
27
+ /**
28
+ * Renders all fields currently registered.
29
+ *
30
+ * @return string
31
+ */
32
+ public function render_fields() {
33
+ $output = '';
34
+ foreach ( $this->fields as $data ) {
35
+ $output .= $this->render_field( $data['type'], $data['args'] );
36
+ }
37
+ return $output;
38
+ }
39
+
40
+ /**
41
+ * Renders all fields currently registered as a table.
42
+ *
43
+ * @return string
44
+ */
45
+ public function render_fields_table() {
46
+ $output = '<table class="form-table">';
47
+ foreach ( $this->fields as $data ) {
48
+ $title = ( array_key_exists( 'title', $data['args'] ) ) ? $data['args']['title'] : '';
49
+
50
+ $output .= '<tr><th>' . $title . '</th><td>';
51
+ $output .= $this->render_field( $data['type'], $data['args'] );
52
+ $output .= '</td><tr>';
53
+ }
54
+ $output .= '</table>';
55
+ return $output;
56
+ }
57
+
58
+ /**
59
+ * Renders a single field.
60
+ *
61
+ * @param string $field_type The type of field being rendered.
62
+ * @param array $args The options for the field type.
63
+ *
64
+ * @return string
65
+ */
66
+ public function render_field( $field_type, $args ) {
67
+ $args = wp_parse_args( $args, array(
68
+ 'name' => '',
69
+ 'value' => '',
70
+ 'options' => array(),
71
+ 'description' => '',
72
+ 'classes' => '',
73
+ 'data' => array(),
74
+ 'multiple' => false,
75
+ ) );
76
+
77
+ $output = '';
78
+ switch ( $field_type ) {
79
+ case 'text':
80
+ $output = sprintf(
81
+ '<input type="text" name="%1$s" id="%1$s" class="%2$s" value="%3$s" />',
82
+ esc_attr( $args['name'] ),
83
+ esc_attr( $args['classes'] ),
84
+ esc_attr( $args['value'] )
85
+ );
86
+ break;
87
+ case 'hidden':
88
+ $output = sprintf(
89
+ '<input type="hidden" name="%1$s" id="%1$s" class="%2$s" value="%3$s" />',
90
+ esc_attr( $args['name'] ),
91
+ esc_attr( $args['classes'] ),
92
+ esc_attr( $args['value'] )
93
+ );
94
+ break;
95
+ case 'select':
96
+ $current_value = $args['value'];
97
+
98
+ $output = sprintf(
99
+ '<select name="%1$s" class="%2$s" id="%1$s">',
100
+ esc_attr( $args['name'] ),
101
+ esc_attr( $args['classes'] )
102
+ );
103
+
104
+ foreach ( $args['options'] as $value => $label ) {
105
+ $output .= sprintf(
106
+ '<option value="%1$s" %2$s>%3$s</option>',
107
+ esc_attr( $value ),
108
+ selected( $value === $current_value, true, false ),
109
+ esc_html( $label )
110
+ );
111
+ }
112
+ $output .= '</select>';
113
+ break;
114
+ case 'select2':
115
+ $values = array();
116
+
117
+ $multiple = ( $args['multiple'] ) ? 'multiple ' : '';
118
+ $output = sprintf(
119
+ '<select name="%1$s" id="%1$s" class="select2-select %2$s" %3$s%4$s/>',
120
+ esc_attr( $args['name'] ),
121
+ esc_attr( $args['classes'] ),
122
+ $this->prepare_data_attributes_string( $args['data'] ),
123
+ $multiple
124
+ );
125
+
126
+ if ( array_key_exists( 'placeholder', $args['data'] ) && ! $multiple ) {
127
+ $output .= '<option value=""></option>';
128
+ }
129
+
130
+ foreach ( $args['options'] as $parent ) {
131
+ $parent = wp_parse_args( $parent, array(
132
+ 'value' => '',
133
+ 'text' => '',
134
+ 'children' => array(),
135
+ ) );
136
+ if ( is_array( $args['value'] ) ) {
137
+ $selected = selected( in_array( $parent['value'], $args['value'], true ), true, false );
138
+ } else {
139
+ $selected = selected( $args['value'], $parent['value'], false );
140
+ }
141
+ $output .= sprintf(
142
+ '<option class="parent" value="%1$s" %3$s>%2$s</option>',
143
+ $parent['value'],
144
+ $parent['text'],
145
+ $selected
146
+ );
147
+ $values[] = $parent['value'];
148
+ if ( ! empty( $parent['children'] ) ) {
149
+ foreach ( $parent['children'] as $child ) {
150
+ $output .= sprintf(
151
+ '<option class="child" value="%1$s" %3$s>%2$s</option>',
152
+ $child['value'],
153
+ $child['text'],
154
+ selected( $args['value'], $child['value'], false )
155
+ );
156
+ $values[] = $child['value'];
157
+ }
158
+ $output .= '</optgroup>';
159
+ }
160
+ }
161
+
162
+ $selected_values = explode( ',', $args['value'] );
163
+
164
+ foreach ( $selected_values as $selected_value ) {
165
+ if ( ! empty( $selected_value ) && ! in_array( $selected_value, $values, true ) ) {
166
+ $output .= sprintf(
167
+ '<option value="%1$s" %2$s>%1$s</option>',
168
+ $selected_value,
169
+ selected( true, true, false )
170
+ );
171
+ }
172
+ }
173
+
174
+ $output .= '</select>';
175
+ break;
176
+ case 'checkbox':
177
+ $output = sprintf(
178
+ '<input type="checkbox" name="%1$s" id="%1$s" value="1" %3$s>%2$s',
179
+ $args['name'],
180
+ $args['text'],
181
+ checked( $args['value'], true, false )
182
+ );
183
+ break;
184
+ default:
185
+ $output = apply_filters( 'wp_stream_form_render_field', $output, $field_type, $args );
186
+ break;
187
+ }
188
+
189
+ $output .= ! empty( $args['description'] ) ? wp_kses_post( sprintf( '<p class="description">%s</p>', $args['description'] ) ) : null;
190
+
191
+ return $output;
192
+ }
193
+
194
+ /**
195
+ * Prepares string with HTML data attributes
196
+ *
197
+ * @param $data array List of key/value data pairs to prepare.
198
+ * @return string
199
+ */
200
+ public function prepare_data_attributes_string( $data ) {
201
+ $output = '';
202
+ foreach ( $data as $key => $value ) {
203
+ $key = 'data-' . esc_attr( $key );
204
+ $output .= $key . '="' . esc_attr( $value ) . '" ';
205
+ }
206
+ return $output;
207
+ }
208
+ }
classes/class-list-table.php CHANGED
@@ -57,9 +57,6 @@ class List_Table extends \WP_List_Table {
57
  if ( 'top' === $which ) {
58
  echo $this->filters_form(); // xss ok
59
  }
60
- if ( 'bottom' === $which ) {
61
- echo $this->record_actions_form(); // xss ok
62
- }
63
  }
64
 
65
  function no_items() {
@@ -572,13 +569,19 @@ class List_Table extends \WP_List_Table {
572
  }
573
 
574
  function filters_form() {
575
- $user_id = get_current_user_id();
576
  $filters = $this->get_filters();
577
 
578
  $filters_string = sprintf( '<input type="hidden" name="page" value="%s" />', 'wp_stream' );
579
  $filters_string .= sprintf( '<span class="filter_info hidden">%s</span>', esc_html__( 'Show filter controls via the screen options tab above.', 'stream' ) );
580
 
581
  foreach ( $filters as $name => $data ) {
 
 
 
 
 
 
 
582
  if ( 'date' === $name ) {
583
  $filters_string .= $this->filter_date( $data['items'] );
584
  } else {
@@ -617,18 +620,19 @@ class List_Table extends \WP_List_Table {
617
  // Sort top-level items by label
618
  array_multisort( $labels, SORT_ASC, $data['items'] );
619
 
620
- // Ouput a hidden input to handle the connector value
621
- $filters_string .= '<input type="hidden" name="connector" class="record-filter-connector" />';
 
 
 
622
  }
623
 
624
- $filters_string .= $this->filter_select( $name, $data['title'], $data['items'] );
625
  }
626
  }
627
 
628
  $filters_string .= sprintf( '<input type="submit" id="record-query-submit" class="button" value="%s" />', __( 'Filter', 'stream' ) );
629
 
630
- $filters_string .= wp_nonce_field( 'stream_filters_user_search_nonce', 'stream_filters_user_search_nonce' );
631
-
632
  // Parse all query vars into an array
633
  $query_vars = array();
634
 
@@ -836,7 +840,18 @@ class List_Table extends \WP_List_Table {
836
  }
837
  echo '</select></div>';
838
  wp_nonce_field( 'stream_record_actions_nonce', 'stream_record_actions_nonce' );
 
 
 
 
 
 
 
 
 
 
839
  printf( '<input type="submit" name="" id="record-actions-submit" class="button" value="%s">', esc_attr__( 'Apply', 'stream' ) );
 
840
 
841
  return ob_get_clean();
842
  }
@@ -846,9 +861,12 @@ class List_Table extends \WP_List_Table {
846
 
847
  echo '<form method="get" action="' . esc_url( $url ) . '" id="record-filter-form">';
848
  echo $this->filter_search(); // xss ok
849
-
850
  parent::display();
851
  echo '</form>';
 
 
 
 
852
  }
853
 
854
  function display_tablenav( $which ) {
57
  if ( 'top' === $which ) {
58
  echo $this->filters_form(); // xss ok
59
  }
 
 
 
60
  }
61
 
62
  function no_items() {
569
  }
570
 
571
  function filters_form() {
 
572
  $filters = $this->get_filters();
573
 
574
  $filters_string = sprintf( '<input type="hidden" name="page" value="%s" />', 'wp_stream' );
575
  $filters_string .= sprintf( '<span class="filter_info hidden">%s</span>', esc_html__( 'Show filter controls via the screen options tab above.', 'stream' ) );
576
 
577
  foreach ( $filters as $name => $data ) {
578
+
579
+ $data = wp_parse_args( $data, array(
580
+ 'title' => '',
581
+ 'items' => array(),
582
+ 'ajax' => false,
583
+ ) );
584
+
585
  if ( 'date' === $name ) {
586
  $filters_string .= $this->filter_date( $data['items'] );
587
  } else {
620
  // Sort top-level items by label
621
  array_multisort( $labels, SORT_ASC, $data['items'] );
622
 
623
+ // Output a hidden input to handle the connector value
624
+ $filters_string .= sprintf(
625
+ '<input type="hidden" name="connector" class="record-filter-connector" value="%s" />',
626
+ esc_attr( wp_stream_filter_input( INPUT_GET, 'connector' ) )
627
+ );
628
  }
629
 
630
+ $filters_string .= $this->filter_select( $name, $data['title'], $data['items'], $data['ajax'] );
631
  }
632
  }
633
 
634
  $filters_string .= sprintf( '<input type="submit" id="record-query-submit" class="button" value="%s" />', __( 'Filter', 'stream' ) );
635
 
 
 
636
  // Parse all query vars into an array
637
  $query_vars = array();
638
 
840
  }
841
  echo '</select></div>';
842
  wp_nonce_field( 'stream_record_actions_nonce', 'stream_record_actions_nonce' );
843
+
844
+ printf( '<input type="hidden" name="page" value="%s">', esc_attr( wp_stream_filter_input( INPUT_GET, 'page' ) ) );
845
+ printf( '<input type="hidden" name="date_predefined" value="%s">', esc_attr( wp_stream_filter_input( INPUT_GET, 'date_predefined' ) ) );
846
+ printf( '<input type="hidden" name="date_from" value="%s">', esc_attr( wp_stream_filter_input( INPUT_GET, 'date_from' ) ) );
847
+ printf( '<input type="hidden" name="date_to" value="%s">', esc_attr( wp_stream_filter_input( INPUT_GET, 'date_to' ) ) );
848
+ printf( '<input type="hidden" name="user_id" value="%s">', esc_attr( wp_stream_filter_input( INPUT_GET, 'user_id' ) ) );
849
+ printf( '<input type="hidden" name="connector" value="%s">', esc_attr( wp_stream_filter_input( INPUT_GET, 'connector' ) ) );
850
+ printf( '<input type="hidden" name="context" value="%s">', esc_attr( wp_stream_filter_input( INPUT_GET, 'context' ) ) );
851
+ printf( '<input type="hidden" name="action" value="%s">', esc_attr( wp_stream_filter_input( INPUT_GET, 'action' ) ) );
852
+
853
  printf( '<input type="submit" name="" id="record-actions-submit" class="button" value="%s">', esc_attr__( 'Apply', 'stream' ) );
854
+ echo '<div class="clear"></div>';
855
 
856
  return ob_get_clean();
857
  }
861
 
862
  echo '<form method="get" action="' . esc_url( $url ) . '" id="record-filter-form">';
863
  echo $this->filter_search(); // xss ok
 
864
  parent::display();
865
  echo '</form>';
866
+
867
+ echo '<form method="get" action="' . esc_url( $url ) . '" id="record-actions-form">';
868
+ echo $this->record_actions_form(); // xss ok
869
+ echo '</form>';
870
  }
871
 
872
  function display_tablenav( $which ) {
classes/class-log.php CHANGED
@@ -220,7 +220,13 @@ class Log {
220
  $excluded = true;
221
 
222
  foreach ( $exclude_rules as $exclude_key => $exclude_value ) {
223
- if ( $record[ $exclude_key ] !== $exclude_value ) {
 
 
 
 
 
 
224
  $excluded = false;
225
  break;
226
  }
220
  $excluded = true;
221
 
222
  foreach ( $exclude_rules as $exclude_key => $exclude_value ) {
223
+ if ( 'ip_address' === $exclude_key ) {
224
+ $ip_addresses = explode( ',', $exclude_value );
225
+ if ( ! in_array( $record['ip_address'], $ip_addresses, true ) ) {
226
+ $excluded = false;
227
+ break;
228
+ }
229
+ } elseif ( $record[ $exclude_key ] !== $exclude_value ) {
230
  $excluded = false;
231
  break;
232
  }
classes/class-network.php CHANGED
@@ -394,10 +394,10 @@ class Network {
394
  );
395
 
396
  // add all sites
397
- foreach ( wp_get_sites() as $blog ) {
398
- $blog_data = get_blog_details( $blog );
399
 
400
- $blogs[ $blog['blog_id'] ] = array(
401
  'label' => $blog_data->blogname,
402
  'disabled' => '',
403
  );
394
  );
395
 
396
  // add all sites
397
+ foreach ( wp_stream_get_sites() as $blog ) {
398
+ $blog_data = get_blog_details( $blog->blog_id );
399
 
400
+ $blogs[ $blog->blog_id ] = array(
401
  'label' => $blog_data->blogname,
402
  'disabled' => '',
403
  );
classes/class-plugin.php CHANGED
@@ -7,7 +7,7 @@ class Plugin {
7
  *
8
  * @const string
9
  */
10
- const VERSION = '3.0.6';
11
 
12
  /**
13
  * WP-CLI command
7
  *
8
  * @const string
9
  */
10
+ const VERSION = '3.0.7';
11
 
12
  /**
13
  * WP-CLI command
classes/class-settings.php CHANGED
@@ -84,11 +84,8 @@ class Settings {
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
  );
@@ -169,7 +166,7 @@ class Settings {
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' ),
@@ -189,7 +186,14 @@ class Settings {
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 );
@@ -745,6 +749,7 @@ class Settings {
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' ) );
@@ -790,7 +795,7 @@ class Settings {
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
 
@@ -799,8 +804,8 @@ class Settings {
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;
@@ -809,21 +814,21 @@ class Settings {
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();
@@ -834,67 +839,67 @@ class Settings {
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(
@@ -909,16 +914,17 @@ class Settings {
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,
84
  'message' => esc_html__( 'There was an error in the request', 'stream' ),
85
  );
86
 
87
+ $search = wp_unslash( trim( wp_stream_filter_input( INPUT_POST, 'find' ) ) );
88
+
 
 
 
89
  $request = (object) array(
90
  'find' => $search,
91
  );
166
  if ( empty( $search ) || preg_match( '/wp|cli|system|unknown/i', $search ) ) {
167
  $author = new Author( 0 );
168
  $response->users[] = array(
169
+ 'id' => '0',
170
  'text' => $author->get_display_name(),
171
  'icon' => $author->get_avatar_src( 32 ),
172
  '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' ),
186
 
187
  check_ajax_referer( 'stream_get_ips', 'nonce' );
188
 
189
+ $ips = $this->plugin->db->existing_records( 'ip' );
190
+ $find = wp_stream_filter_input( INPUT_POST, 'find' );
191
+
192
+ if ( isset( $find['term'] ) && '' !== $find['term'] ) {
193
+ $ips = array_filter( $ips, function ( $ip ) use ( $find ) {
194
+ return 0 === strpos( $ip, $find['term'] );
195
+ } );
196
+ }
197
 
198
  if ( $ips ) {
199
  wp_send_json_success( $ips );
749
 
750
  break;
751
  case 'rule_list' :
752
+ $form = new Form_Generator;
753
  $output = '<p class="description">' . esc_html( $description ) . '</p>';
754
 
755
  $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' ) );
795
  $author_or_role_selected = array();
796
 
797
  foreach ( $this->get_roles() as $role_id => $role ) {
798
+ $args = array( 'value' => $role_id, 'text' => $role );
799
  $users = count_users();
800
  $count = isset( $users['avail_roles'][ $role_id ] ) ? $users['avail_roles'][ $role_id ] : 0;
801
 
804
  }
805
 
806
  if ( $role_id === $author_or_role ) {
807
+ $author_or_role_selected['value'] = $role_id;
808
+ $author_or_role_selected['text'] = $role;
809
  }
810
 
811
  $author_or_role_values[] = $args;
814
  if ( empty( $author_or_role_selected ) && is_numeric( $author_or_role ) ) {
815
  $user = new WP_User( $author_or_role );
816
  $display_name = ( 0 === $user->ID ) ? esc_html__( 'N/A', 'stream' ) : $user->display_name;
817
+ $author_or_role_selected = array( 'value' => $user->ID, 'text' => $display_name );
818
+ $author_or_role_values[] = $author_or_role_selected;
819
  }
820
 
821
+ $author_or_role_input = $form->render_field( 'select2', array(
822
+ 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]' , $option_key, $section, $name, 'author_or_role' ) ),
823
+ 'options' => $author_or_role_values,
824
+ 'classes' => 'author_or_role',
825
+ 'data' => array(
826
+ 'placeholder' => esc_html__( 'Any Author or Role', 'stream' ),
827
+ 'nonce' => esc_attr( wp_create_nonce( 'stream_get_users' ) ),
828
+ 'selected-id' => isset( $author_or_role_selected['value'] ) ? esc_attr( $author_or_role_selected['value'] ) : '',
829
+ 'selected-text' => isset( $author_or_role_selected['text'] ) ? esc_attr( $author_or_role_selected['text'] ) : '',
830
+ ),
831
+ ) );
 
832
 
833
  // Context dropdown menu
834
  $context_values = array();
839
  if ( isset( $context_data['children'] ) ) {
840
  $child_values = array();
841
  foreach ( $context_data['children'] as $child_id => $child_value ) {
842
+ $child_values[] = array( 'value' => $context_id . '-' . $child_id, 'text' => $child_value, 'parent' => $context_id );
843
  }
844
  }
845
  if ( isset( $context_data['label'] ) ) {
846
+ $context_values[] = array( 'value' => $context_id, 'text' => $context_data['label'], 'children' => $child_values );
847
  }
848
  } else {
849
+ $context_values[] = array( 'value' => $context_id, 'text' => $context_data );
850
  }
851
  }
852
 
853
+ $connector_or_context_input = $form->render_field( 'select2', array(
854
+ 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]' , $option_key, $section, $name, 'connector_or_context' ) ),
855
+ 'options' => $context_values,
856
+ 'classes' => 'connector_or_context',
857
+ 'data' => array(
858
+ 'group' => 'connector',
859
+ 'placeholder' => __( 'Any Context', 'stream' ),
860
+ ),
861
+ ) );
862
 
863
+ $connector_input = $form->render_field( 'hidden', array(
864
+ 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]' , $option_key, $section, $name, 'connector' ) ),
865
+ 'value' => $connector,
866
+ 'classes' => 'connector',
867
+ ) );
868
+
869
+ $context_input = $form->render_field( 'hidden', array(
870
+ 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]' , $option_key, $section, $name, 'context' ) ),
871
+ 'value' => $context,
872
+ 'classes' => 'context',
873
+ ) );
874
 
875
  // Action dropdown menu
876
  $action_values = array();
877
 
878
  foreach ( $this->get_terms_labels( 'action' ) as $action_id => $action_data ) {
879
+ $action_values[] = array( 'value' => $action_id, 'text' => $action_data );
880
  }
881
 
882
+ $action_input = $form->render_field( 'select2', array(
883
+ 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]' , $option_key, $section, $name, 'action' ) ),
884
+ 'value' => $action,
885
+ 'options' => $action_values,
886
+ 'classes' => 'action',
887
+ 'data' => array(
888
+ 'placeholder' => __( 'Any Action', 'stream' ),
889
+ ),
890
+ ) );
 
891
 
892
  // IP Address input
893
+ $ip_address_input = $form->render_field( 'select2', array(
894
+ 'name' => esc_attr( sprintf( '%1$s[%2$s_%3$s][%4$s][]' , $option_key, $section, $name, 'ip_address' ) ),
895
+ 'value' => $ip_address,
896
+ 'classes' => 'ip_address',
897
+ 'data' => array(
898
+ 'placeholder' => esc_attr__( 'Any IP Address', 'stream' ),
899
+ 'nonce' => esc_attr( wp_create_nonce( 'stream_get_ips' ) ),
900
+ ),
901
+ 'multiple' => true,
902
+ ) );
903
 
904
  // Hidden helper input
905
  $helper_input = sprintf(
914
  '<tr class="%1$s %2$s">
915
  <th scope="row" class="check-column">%3$s %4$s</th>
916
  <td>%5$s</td>
917
+ <td>%6$s %7$s %8$s</td>
 
918
  <td>%9$s</td>
919
+ <td>%10$s</td>
920
+ <th scope="row" class="actions-column">%11$s</th>
921
  </tr>',
922
  ( 0 !== $key % 2 ) ? 'alternate' : '',
923
  ( 'helper' === $key ) ? 'hidden helper' : '',
924
  '<input class="cb-select" type="checkbox" />',
925
  $helper_input,
926
  $author_or_role_input,
927
+ $connector_or_context_input,
928
  $connector_input,
929
  $context_input,
930
  $action_input,
classes/class-uninstall.php CHANGED
@@ -136,8 +136,8 @@ class Uninstall {
136
  $wpdb->query( "DELETE FROM {$wpdb->sitemeta} WHERE meta_key LIKE '%wp_stream%';" );
137
 
138
  // Delete options from each blog on network
139
- foreach ( wp_get_sites() as $blog ) {
140
- $this->delete_blog_options( absint( $blog['blog_id'] ) );
141
  }
142
  }
143
 
136
  $wpdb->query( "DELETE FROM {$wpdb->sitemeta} WHERE meta_key LIKE '%wp_stream%';" );
137
 
138
  // Delete options from each blog on network
139
+ foreach ( wp_stream_get_sites() as $blog ) {
140
+ $this->delete_blog_options( absint( $blog->blog_id ) );
141
  }
142
  }
143
 
connectors/class-connector-blogs.php CHANGED
@@ -63,10 +63,10 @@ class Connector_Blogs extends Connector {
63
  $labels = array();
64
 
65
  if ( is_multisite() && ! wp_is_large_network() ) {
66
- $blogs = wp_get_sites();
67
 
68
  foreach ( $blogs as $blog ) {
69
- $blog_details = get_blog_details( $blog['blog_id'] );
70
  $key = sanitize_key( $blog_details->blogname );
71
  $labels[ $key ] = $blog_details->blogname;
72
  }
63
  $labels = array();
64
 
65
  if ( is_multisite() && ! wp_is_large_network() ) {
66
+ $blogs = wp_stream_get_sites();
67
 
68
  foreach ( $blogs as $blog ) {
69
+ $blog_details = get_blog_details( $blog->blog_id );
70
  $key = sanitize_key( $blog_details->blogname );
71
  $labels[ $key ] = $blog_details->blogname;
72
  }
connectors/class-connector-taxonomies.php CHANGED
@@ -123,7 +123,7 @@ class Connector_Taxonomies extends Connector {
123
  unset( $object_type );
124
 
125
  $taxonomy_obj = (object) $args;
126
- $label = get_taxonomy_labels( $taxonomy_obj )->name;
127
 
128
  $this->context_labels[ $taxonomy ] = $label;
129
 
123
  unset( $object_type );
124
 
125
  $taxonomy_obj = (object) $args;
126
+ $label = get_taxonomy_labels( $taxonomy_obj )->singular_name;
127
 
128
  $this->context_labels[ $taxonomy ] = $label;
129
 
includes/functions.php CHANGED
@@ -1,5 +1,4 @@
1
  <?php
2
-
3
  /**
4
  * Gets a specific external variable by name and optionally filters it.
5
  *
@@ -87,6 +86,24 @@ function wp_stream_json_encode( $data, $options = 0, $depth = 512 ) {
87
  return $json;
88
  }
89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  /**
91
  * Check if Stream is running on WordPress.com VIP
92
  *
1
  <?php
 
2
  /**
3
  * Gets a specific external variable by name and optionally filters it.
4
  *
86
  return $json;
87
  }
88
 
89
+ /**
90
+ * Return an array of sites for a network in a way that is also backwards compatible
91
+ *
92
+ * @return array
93
+ */
94
+ function wp_stream_get_sites() {
95
+ if ( function_exists( 'get_sites' ) ) {
96
+ $sites = get_sites();
97
+ } else {
98
+ $sites = array();
99
+ foreach ( wp_get_sites() as $site ) {
100
+ $sites[] = WP_Site::get_instance( $site['blog_id'] );
101
+ }
102
+ }
103
+
104
+ return $sites;
105
+ }
106
+
107
  /**
108
  * Check if Stream is running on WordPress.com VIP
109
  *
readme.txt CHANGED
@@ -1,9 +1,9 @@
1
  === Stream ===
2
- Contributors: fjarrett, lukecarbis, shadyvb, powelski, chacha102, stream, xwp
3
  Tags: actions, activity, activity log, activity logs, admin actions, analytics, audit, audit log, audit logs, blackbox, black box, change, changes, dashboard, log, logs, stream, tracking, troubleshooting, wp stream
4
  Requires at least: 3.9
5
- Tested up to: 4.5
6
- Stable tag: 3.0.6
7
  License: GPLv2 or later
8
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -79,18 +79,29 @@ Thank you for wanting to make Stream better for everyone!
79
 
80
  == Changelog ==
81
 
82
- = 3.0.6 - April 31, 2015 =
 
 
 
 
 
 
 
 
 
 
 
83
 
84
  * New: Better support for default themes ([#831](https://github.com/xwp/stream/pull/831))
85
- * New: Upgrade filter menus to Select2 4 ([c3f6c65](https://github.com/xwp/stream/pull/c3f6c65c1bd95cebb26da7f00a720050a9144586))
86
  * Fix: Security Fixes
87
  * Fix: Cron for purging old records has been fixed ([#843](https://github.com/xwp/stream/pull/843))
88
  * Fix: Better at storing records for Super Admins ([#835](https://github.com/xwp/stream/pull/835))
89
  * Fix: Allow Super Admins to be ignored and filtered ([#835](https://github.com/xwp/stream/pull/835))
90
 
91
- Props [@chacha](https://github.com/chacha), [@lukecarbis](https://github.com/lukecarbis), [@Stayallive](https://github.com/Stayallive), [@barryceelen](https://github.com/barryceelen), Jonathan Desrosiers, [@marcin-lawrowski](https://github.com/marcin-lawrowski)
92
 
93
- = 3.0.5 - March 15, 2015 =
94
 
95
  * New: Export your Stream records as CSV or JSON. ([#823](https://github.com/xwp/stream/pull/823))
96
  * Tweak: More mobile responsive list table ([#810](https://github.com/xwp/stream/pull/810))
1
  === Stream ===
2
+ Contributors: fjarrett, lukecarbis, chacha102, stream, xwp
3
  Tags: actions, activity, activity log, activity logs, admin actions, analytics, audit, audit log, audit logs, blackbox, black box, change, changes, dashboard, log, logs, stream, tracking, troubleshooting, wp stream
4
  Requires at least: 3.9
5
+ Tested up to: 4.6
6
+ Stable tag: 3.0.7
7
  License: GPLv2 or later
8
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
9
 
79
 
80
  == Changelog ==
81
 
82
+ = 3.0.7 - June 14, 2016 =
83
+
84
+ * Tweak: Use get_sites instead of wp_get_sites when available ([#856](https://github.com/xwp/stream/pull/856))
85
+ * Tweak: More stable record actions (like exporting) ([71e6ac1](https://github.com/xwp/stream/commit/71e6ac1ff66e4415909c7ae29b243733a1fd209d))
86
+ * Tweak: Better multisite support ([cfab041](https://github.com/xwp/stream/commit/cfab0413e67b83d969bd6612c895ecdb05dbfce4))
87
+ * Fix: Exclude rule settings have been restored and enhanced ([#855](https://github.com/xwp/stream/pull/855))
88
+ * Fix: Loading users via ajax ([#854](https://github.com/xwp/stream/pull/854))
89
+ * Fix: Use the correct label for events relating to taxonomies which are registered late ([#859](https://github.com/xwp/stream/pull/859))
90
+
91
+ Props [@chacha](https://github.com/chacha), [@lukecarbis](https://github.com/lukecarbis), Eugene Kireev, [@johnbillion](https://github.com/johnbillion)
92
+
93
+ = 3.0.6 - May 31, 2016 =
94
 
95
  * New: Better support for default themes ([#831](https://github.com/xwp/stream/pull/831))
96
+ * New: Upgrade filter menus to Select2 4 ([c3f6c65](https://github.com/xwp/stream/commit/c3f6c65c1bd95cebb26da7f00a720050a9144586))
97
  * Fix: Security Fixes
98
  * Fix: Cron for purging old records has been fixed ([#843](https://github.com/xwp/stream/pull/843))
99
  * Fix: Better at storing records for Super Admins ([#835](https://github.com/xwp/stream/pull/835))
100
  * Fix: Allow Super Admins to be ignored and filtered ([#835](https://github.com/xwp/stream/pull/835))
101
 
102
+ Props [@chacha](https://github.com/chacha), [@lukecarbis](https://github.com/lukecarbis), [@marcin-lawrowski](https://github.com/marcin-lawrowski)
103
 
104
+ = 3.0.5 - March 15, 2016 =
105
 
106
  * New: Export your Stream records as CSV or JSON. ([#823](https://github.com/xwp/stream/pull/823))
107
  * Tweak: More mobile responsive list table ([#810](https://github.com/xwp/stream/pull/810))
stream.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: Stream
4
  * Plugin URI: https://wp-stream.com/
5
  * Description: Stream tracks logged-in user activity so you can monitor every change made on your WordPress site in beautifully organized detail. All activity is organized by context, action and IP address for easy filtering. Developers can extend Stream with custom connectors to log any kind of action.
6
- * Version: 3.0.6
7
  * Author: XWP
8
  * Author URI: https://xwp.co/
9
  * License: GPLv2+
3
  * Plugin Name: Stream
4
  * Plugin URI: https://wp-stream.com/
5
  * Description: Stream tracks logged-in user activity so you can monitor every change made on your WordPress site in beautifully organized detail. All activity is organized by context, action and IP address for easy filtering. Developers can extend Stream with custom connectors to log any kind of action.
6
+ * Version: 3.0.7
7
  * Author: XWP
8
  * Author URI: https://xwp.co/
9
  * License: GPLv2+
ui/css/admin.css CHANGED
@@ -4,8 +4,13 @@
4
  padding-top: 6px;
5
  }
6
 
7
- .toplevel_page_wp_stream .tablenav.bottom .button {
8
- margin-right: 6px;
 
 
 
 
 
9
  }
10
 
11
  .toplevel_page_wp_stream .tablenav .actions {
@@ -13,14 +18,28 @@
13
  overflow: visible;
14
  }
15
 
16
- .toplevel_page_wp_stream .tablenav .select2-selection {
17
  border-color: #ccc;
18
  background: #f7f7f7;
19
  -webkit-box-shadow: 0 1px 0 #ccc;
20
  box-shadow: 0 1px 0 #ccc;
21
  }
22
 
23
- .toplevel_page_wp_stream .tablenav .select2-selection .select2-selection__rendered {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  color: #555;
25
  }
26
 
@@ -235,10 +254,11 @@
235
 
236
  /* Settings */
237
 
238
- .wp_stream_settings .select2-container,
239
- .wp_stream_network_settings .select2-container,
240
- .wp_stream_default_settings .select2-container {
241
- width: 100%;
 
242
  }
243
 
244
  .wp_stream_settings .tablenav,
@@ -336,16 +356,8 @@
336
  color: #999;
337
  }
338
 
339
- .wp_stream_screen li.select2-result {
340
- margin-bottom: 0;
341
- }
342
-
343
- .wp_stream_screen li.select2-result.level-1 {
344
- font-weight: bold;
345
- }
346
-
347
- .wp_stream_screen li.select2-result.level-2 {
348
- padding-left: 1em;
349
  }
350
 
351
  .wp_stream_screen .select2-results .select2-disabled {
@@ -353,15 +365,21 @@
353
  color: #aaa;
354
  }
355
 
356
- .wp_stream_screen .select2-search-choice .icon16 {
357
- margin: -6px 1px 0 -3px;
 
 
 
 
 
 
358
  padding: 0;
359
  width: 16px;
360
  height: 16px;
361
  }
362
 
363
- .wp_stream_screen .select2-chosen .icon16 {
364
- padding-right: 1px;
365
  }
366
 
367
  .wp_stream_screen .select2-chosen .icon16:before,
@@ -452,6 +470,14 @@
452
  border: 1px solid rgba(160,0,0,0.75);
453
  }
454
 
 
 
 
 
 
 
 
 
455
  @media screen and ( max-width: 900px ) {
456
  .wp_stream_settings .stream-exclude-list .actions-column,
457
  .wp_stream_network_settings .stream-exclude-list .actions-column,
@@ -461,6 +487,11 @@
461
  }
462
 
463
  @media screen and ( max-width: 782px ) {
 
 
 
 
 
464
  .wp_stream_settings .stream-exclude-list td,
465
  .wp_stream_network_settings .stream-exclude-list td,
466
  .wp_stream_default_settings .stream-exclude-list td {
4
  padding-top: 6px;
5
  }
6
 
7
+ .toplevel_page_wp_stream #record-actions-form {
8
+ margin-top: -32px;
9
+ }
10
+
11
+ .toplevel_page_wp_stream #record-actions-form .button {
12
+ margin-left: 6px;
13
+ float: left;
14
  }
15
 
16
  .toplevel_page_wp_stream .tablenav .actions {
18
  overflow: visible;
19
  }
20
 
21
+ .wp_stream_screen .select2 .select2-selection {
22
  border-color: #ccc;
23
  background: #f7f7f7;
24
  -webkit-box-shadow: 0 1px 0 #ccc;
25
  box-shadow: 0 1px 0 #ccc;
26
  }
27
 
28
+ .wp_stream_screen .select2 .select2-selection--multiple {
29
+ font-size: 0;
30
+ min-height: 28px;
31
+ }
32
+
33
+ .wp_stream_screen .select2-container.select2-container--focus .select2-selection--multiple {
34
+ border: solid #ccc 1px;
35
+ }
36
+
37
+ .wp_stream_screen .select2-container .select2-selection--multiple .select2-selection__choice {
38
+ margin-top: 4px;
39
+ margin-bottom: 3px;
40
+ }
41
+
42
+ .wp_stream_screen .select2 .select2-selection .select2-selection__rendered {
43
  color: #555;
44
  }
45
 
254
 
255
  /* Settings */
256
 
257
+ .wp_stream_settings .select2.select2-container,
258
+ .wp_stream_network_settings .select2.select2-container,
259
+ .wp_stream_default_settings .select2.select2-container {
260
+ min-width: 160px;
261
+ max-width: 100%;
262
  }
263
 
264
  .wp_stream_settings .tablenav,
356
  color: #999;
357
  }
358
 
359
+ .wp_stream_screen .select2 .select2-selection .select2-selection__placeholder {
360
+ color: #72777c;
 
 
 
 
 
 
 
 
361
  }
362
 
363
  .wp_stream_screen .select2-results .select2-disabled {
365
  color: #aaa;
366
  }
367
 
368
+ .wp_stream_screen .select2 .select2-search--inline {
369
+ float: none;
370
+ margin-bottom: 4px;
371
+ margin-left: 2px;
372
+ }
373
+
374
+ .wp_stream_screen .select2-selection .icon16 {
375
+ margin: -3px 1px 0 -3px;
376
  padding: 0;
377
  width: 16px;
378
  height: 16px;
379
  }
380
 
381
+ .wp_stream_screen .select2-selection .icon16 {
382
+ padding-right: 8px;
383
  }
384
 
385
  .wp_stream_screen .select2-chosen .icon16:before,
470
  border: 1px solid rgba(160,0,0,0.75);
471
  }
472
 
473
+ .wp_stream_screen .select2-results__option .parent {
474
+ font-weight: bold;
475
+ }
476
+
477
+ .wp_stream_screen .select2-results__option .child {
478
+ padding-left: 8px;
479
+ }
480
+
481
  @media screen and ( max-width: 900px ) {
482
  .wp_stream_settings .stream-exclude-list .actions-column,
483
  .wp_stream_network_settings .stream-exclude-list .actions-column,
487
  }
488
 
489
  @media screen and ( max-width: 782px ) {
490
+ .toplevel_page_wp_stream #record-actions-form {
491
+ margin-top: 0;
492
+ margin-bottom: 35px;
493
+ }
494
+
495
  .wp_stream_settings .stream-exclude-list td,
496
  .wp_stream_network_settings .stream-exclude-list td,
497
  .wp_stream_default_settings .stream-exclude-list td {
ui/js/admin.js CHANGED
@@ -15,8 +15,8 @@ jQuery( function( $ ) {
15
 
16
  $( '.toplevel_page_wp_stream :input.chosen-select' ).each( function( i, el ) {
17
  var args = {},
18
- formatResult = function( record, container ) {
19
- var result = '',
20
  $elem = $( record.element ),
21
  icon = '';
22
 
@@ -24,23 +24,28 @@ jQuery( function( $ ) {
24
  record.text = record.text.substring( 2 );
25
  }
26
 
 
 
 
 
 
 
 
 
27
  if ( undefined !== record.icon ) {
28
  icon = record.icon;
29
- } else if ( undefined !== $elem.attr( 'data-icon' ) ) {
30
  icon = $elem.data( 'icon' );
31
  }
32
- if ( icon ) {
33
- result += '<img src="' + icon + '" class="wp-stream-select2-icon">';
34
- }
35
-
36
- result += record.text;
37
 
38
- // Add more info to the container
39
- container.attr( 'title', $elem.attr( 'title' ) );
 
 
40
 
41
- return result;
42
  },
43
- formatSelection = function( record ) {
44
  if ( '- ' === record.text.substring( 0, 2 ) ) {
45
  record.text = record.text.substring( 2 );
46
  }
@@ -50,8 +55,8 @@ jQuery( function( $ ) {
50
  if ( $( el ).find( 'option' ).not( ':selected' ).not( ':empty' ).length > 0 ) {
51
  args = {
52
  minimumResultsForSearch: 10,
53
- formatResult: formatResult,
54
- formatSelection: formatSelection,
55
  allowClear: true,
56
  width: '165px'
57
  };
@@ -85,8 +90,8 @@ jQuery( function( $ ) {
85
  };
86
  }
87
  },
88
- formatResult: formatResult,
89
- formatSelection: formatSelection
90
  };
91
  }
92
 
@@ -94,10 +99,11 @@ jQuery( function( $ ) {
94
  });
95
 
96
  var $queryVars = $.streamGetQueryVars();
97
- var $contextInput = $( '.toplevel_page_wp_stream :input.chosen-select[name="context"]' );
98
 
99
  if ( ( 'undefined' === typeof $queryVars.context || '' === $queryVars.context ) && 'undefined' !== typeof $queryVars.connector ) {
100
- $contextInput.select2( 'val', 'group-' + $queryVars.connector );
 
101
  }
102
 
103
  $( 'input[type=submit]', '#record-filter-form' ).click( function() {
15
 
16
  $( '.toplevel_page_wp_stream :input.chosen-select' ).each( function( i, el ) {
17
  var args = {},
18
+ templateResult = function( record ) {
19
+ var $result = $( '<span>' ),
20
  $elem = $( record.element ),
21
  icon = '';
22
 
24
  record.text = record.text.substring( 2 );
25
  }
26
 
27
+ if ( 'undefined' !== typeof record.id ) {
28
+ if ( record.id.indexOf( 'group-' ) === 0 ) {
29
+ $result.addClass( 'parent' );
30
+ } else if ( $elem.hasClass( 'level-2' ) ) {
31
+ $result.addClass( 'child' );
32
+ }
33
+ }
34
+
35
  if ( undefined !== record.icon ) {
36
  icon = record.icon;
37
+ } else if ( undefined !== $elem && '' !== $elem.data( 'icon' ) ) {
38
  icon = $elem.data( 'icon' );
39
  }
 
 
 
 
 
40
 
41
+ if ( icon ) {
42
+ $result.html( '<img src="' + icon + '" class="wp-stream-select2-icon">' );
43
+ }
44
+ $result.append( record.text );
45
 
46
+ return $result;
47
  },
48
+ templateSelection = function( record ) {
49
  if ( '- ' === record.text.substring( 0, 2 ) ) {
50
  record.text = record.text.substring( 2 );
51
  }
55
  if ( $( el ).find( 'option' ).not( ':selected' ).not( ':empty' ).length > 0 ) {
56
  args = {
57
  minimumResultsForSearch: 10,
58
+ templateResult: templateResult,
59
+ templateSelection: templateSelection,
60
  allowClear: true,
61
  width: '165px'
62
  };
90
  };
91
  }
92
  },
93
+ templateResult: templateResult,
94
+ templateSelection: templateSelection
95
  };
96
  }
97
 
99
  });
100
 
101
  var $queryVars = $.streamGetQueryVars();
102
+ var $contextInput = $( '.toplevel_page_wp_stream select.chosen-select[name="context"]' );
103
 
104
  if ( ( 'undefined' === typeof $queryVars.context || '' === $queryVars.context ) && 'undefined' !== typeof $queryVars.connector ) {
105
+ $contextInput.val( 'group-' + $queryVars.connector );
106
+ $contextInput.trigger( 'change' );
107
  }
108
 
109
  $( 'input[type=submit]', '#record-filter-form' ).click( function() {
ui/js/exclude.js ADDED
@@ -0,0 +1,378 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* globals ajaxurl, wp_stream_regenerate_alt_rows */
2
+ jQuery( function( $ ) {
3
+ var initSettingsSelect2 = function() {
4
+ var $input_user;
5
+
6
+ $( '.stream-exclude-list tr:not(.hidden) select.select2-select.connector_or_context' ).each( function( k, el ) {
7
+ $( el ).select2({
8
+ allowClear: true,
9
+ templateResult : function( item ) {
10
+ if ( typeof item.id === 'undefined' ) {
11
+ return item.text;
12
+ }
13
+ if ( item.id.indexOf( '-' ) === -1 ) {
14
+ return $( '<span class="parent">' + item.text + '</span>' );
15
+ } else {
16
+ return $( '<span class="child">' + item.text + '</span>' );
17
+ }
18
+ },
19
+ matcher: function( params, data ) {
20
+ var match = $.extend( true, {}, data );
21
+
22
+ if ( params.term == null || $.trim( params.term ) === '') {
23
+ return match;
24
+ }
25
+
26
+ var term = params.term.toLowerCase();
27
+
28
+ match.id = match.id.replace( 'blogs', 'sites' );
29
+ if ( match.id.toLowerCase().indexOf( term ) >= 0 ) {
30
+ return match;
31
+ }
32
+
33
+ if ( match.children ) {
34
+
35
+ for ( var i = match.children.length - 1; i >= 0; i--) {
36
+ var child = match.children[i];
37
+
38
+ // Remove term from results if it doesn't match.
39
+ if ( child.id.toLowerCase().indexOf( term ) === -1 ) {
40
+ match.children.splice( i, 1 );
41
+ }
42
+ }
43
+
44
+ if ( match.children.length > 0 ) {
45
+ return match;
46
+ }
47
+ }
48
+
49
+ return null;
50
+ }
51
+ });
52
+ });
53
+
54
+ $( '.stream-exclude-list tr:not(.hidden) select.select2-select.action' ).each( function( k, el ) {
55
+ $( el ).select2({
56
+ allowClear: true
57
+ });
58
+ });
59
+
60
+ $( '.stream-exclude-list tr:not(.hidden) select.select2-select.author_or_role' ).each( function( k, el ) {
61
+ $input_user = $( el );
62
+
63
+ $input_user.select2({
64
+ ajax: {
65
+ type: 'POST',
66
+ url: ajaxurl,
67
+ dataType: 'json',
68
+ quietMillis: 500,
69
+ data: function( term, page ) {
70
+ return {
71
+ find: term,
72
+ limit: 10,
73
+ pager: page,
74
+ action: 'stream_get_users',
75
+ nonce: $input_user.data( 'nonce' )
76
+ };
77
+ },
78
+ processResults: function( response ) {
79
+ var roles = [];
80
+
81
+ $( 'option', $input_user ).each( function() {
82
+ if ( $( this ).val() === '' ) {
83
+ return;
84
+ }
85
+ if ( ! $.isNumeric( $( this ).val() ) ) {
86
+ roles.push({
87
+ 'id' : $( this ).val(),
88
+ 'text' : $( this ).text()
89
+ });
90
+ }
91
+ });
92
+
93
+ roles = $.grep(
94
+ roles,
95
+ function( role ) {
96
+ var roleVal = $input_user.data( 'select2' ).dropdown.$search
97
+ .val()
98
+ .toLowerCase();
99
+ var rolePos = role
100
+ .text
101
+ .toLowerCase()
102
+ .indexOf( roleVal );
103
+ return rolePos >= 0;
104
+ }
105
+ );
106
+
107
+ var answer = {
108
+ results: [
109
+ { text: '', id: '' },
110
+ { text: 'Roles', children: roles },
111
+ { text: 'Users', children: [] }
112
+ ]
113
+ };
114
+
115
+ if ( true !== response.success || undefined === response.data || true !== response.data.status ) {
116
+ return answer;
117
+ }
118
+
119
+ $.each( response.data.users, function( k, user ) {
120
+ if ( $.contains( roles, user.id ) ) {
121
+ user.disabled = true;
122
+ }
123
+ });
124
+
125
+ answer.results[ 2 ].children = response.data.users;
126
+
127
+ // Notice we return the value of more so Select2 knows if more results can be loaded
128
+ return answer;
129
+ }
130
+ },
131
+ templateResult: function( object ) {
132
+ var $result = $( '<div>' ).text( object.text );
133
+
134
+ if ( 'undefined' !== typeof object.icon && object.icon ) {
135
+ $result.prepend( $( '<img src="' + object.icon + '" class="wp-stream-select2-icon">' ) );
136
+
137
+ // Add more info to the container
138
+ $result.attr( 'title', object.tooltip );
139
+ }
140
+
141
+ // Add more info to the container
142
+ if ( 'undefined' !== typeof object.tooltip ) {
143
+ $result.attr( 'title', object.tooltip );
144
+ } else if ( 'undefined' !== typeof object.user_count ) {
145
+ $result.attr( 'title', object.user_count );
146
+ }
147
+
148
+ return $result;
149
+ },
150
+ templateSelection: function( object ) {
151
+ var $result = $( '<div>' ).text( object.text );
152
+
153
+ if ( $.isNumeric( object.id ) && object.text.indexOf( 'icon-users' ) < 0 ) {
154
+ $result.append( $( '<i class="icon16 icon-users"></i>' ) );
155
+ }
156
+
157
+ return $result;
158
+ },
159
+ allowClear: true,
160
+ placeholder: $input_user.data( 'placeholder' )
161
+ }).on( 'change', function() {
162
+ var value = $( this ).select2( 'data' );
163
+
164
+ $( this ).data( 'selected-id', value.id );
165
+ $( this ).data( 'selected-text', value.text );
166
+ });
167
+ });
168
+
169
+ $( '.stream-exclude-list tr:not(.hidden) select.select2-select.ip_address' ).each( function( k, el ) {
170
+ var $input_ip = $( el ),
171
+ searchTerm = '';
172
+
173
+ $input_ip.select2({
174
+ ajax: {
175
+ type: 'POST',
176
+ url: ajaxurl,
177
+ dataType: 'json',
178
+ quietMillis: 500,
179
+ data: function( term ) {
180
+ searchTerm = term.term;
181
+ return {
182
+ find: term,
183
+ limit: 10,
184
+ action: 'stream_get_ips',
185
+ nonce: $input_ip.data( 'nonce' )
186
+ };
187
+ },
188
+ processResults: function( response ) {
189
+ var answer = { results: [] },
190
+ ip_chunks = [];
191
+
192
+ if ( true === response.success && undefined !== response.data ) {
193
+ $.each( response.data, function( key, ip ) {
194
+ answer.results.push({
195
+ id: ip,
196
+ text: ip
197
+ });
198
+ });
199
+ }
200
+
201
+ if ( undefined === searchTerm ) {
202
+ return answer;
203
+ }
204
+
205
+ ip_chunks = searchTerm.match( /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ );
206
+
207
+ if ( null === ip_chunks ) {
208
+ return answer;
209
+ }
210
+
211
+ // remove whole match
212
+ ip_chunks.shift();
213
+
214
+ ip_chunks = $.grep(
215
+ ip_chunks,
216
+ function( chunk ) {
217
+ var numeric = parseInt( chunk, 10 );
218
+ return numeric <= 255 && numeric.toString() === chunk;
219
+ }
220
+ );
221
+
222
+ if ( ip_chunks.length >= 4 ) {
223
+ answer.results.push({
224
+ id: searchTerm,
225
+ text: searchTerm
226
+ });
227
+ }
228
+
229
+ return answer;
230
+ }
231
+ },
232
+ allowClear: false,
233
+ multiple: true,
234
+ maximumSelectionSize: 1,
235
+ placeholder: $input_ip.data( 'placeholder' ),
236
+ tags: true
237
+ });
238
+ }).on( 'change', function() {
239
+ $( this ).prev( '.select2-container' ).find( 'input.select2-input' ).blur();
240
+ });
241
+
242
+ $( 'ul.select2-choices, ul.select2-choices li, input.select2-input', '.stream-exclude-list tr:not(.hidden) .ip_address' ).on( 'mousedown click focus', function() {
243
+ var $container = $( this ).closest( '.select2-container' ),
244
+ $input = $container.find( 'input.select2-input' ),
245
+ value = $container.select2( 'data' );
246
+
247
+ if ( value.length >= 1 ) {
248
+ $input.blur();
249
+ return false;
250
+ }
251
+ });
252
+
253
+ $( '.stream-exclude-list tr:not(.hidden) .exclude_rules_remove_rule_row' ).on( 'click', function() {
254
+ var $thisRow = $( this ).closest( 'tr' );
255
+
256
+ $thisRow.remove();
257
+
258
+ recalculate_rules_found();
259
+ recalculate_rules_selected();
260
+ });
261
+ };
262
+
263
+ initSettingsSelect2();
264
+
265
+ $( '.stream-exclude-list tr:not(.hidden) select.select2-select.author_or_role' ).each( function() {
266
+ $( this ).val( $( this ).data( 'selected-id' ) ).trigger( 'change' );
267
+ });
268
+
269
+ $( '.stream-exclude-list tr:not(.hidden) select.select2-select.connector_or_context' ).each( function() {
270
+ var parts = [
271
+ $( this ).siblings( '.connector' ).val(),
272
+ $( this ).siblings( '.context' ).val()
273
+ ];
274
+ if ( parts[1] === '' ) {
275
+ parts.splice( 1, 1 );
276
+ }
277
+ $( this ).val( parts.join( '-' ) ).trigger( 'change' );
278
+ });
279
+
280
+ $( '#exclude_rules_new_rule' ).on( 'click', function() {
281
+ var $excludeList = $( 'table.stream-exclude-list' );
282
+
283
+ $( 'tr:not(.hidden) select.select2-select', $excludeList ).each( function() {
284
+ $( this ).select2( 'destroy' );
285
+ });
286
+
287
+ var $lastRow = $( 'tr', $excludeList ).last(),
288
+ $newRow = $lastRow.clone();
289
+
290
+ $newRow.removeAttr( 'class' );
291
+ $( '.stream-exclude-list tbody :input' ).off();
292
+ $( ':input', $newRow ).off().val( '' );
293
+
294
+ $lastRow.after( $newRow );
295
+
296
+ initSettingsSelect2();
297
+
298
+ recalculate_rules_found();
299
+ recalculate_rules_selected();
300
+ });
301
+
302
+ $( '#exclude_rules_remove_rules' ).on( 'click', function() {
303
+ var $excludeList = $( 'table.stream-exclude-list' ),
304
+ selectedRows = $( 'tbody input.cb-select:checked', $excludeList ).closest( 'tr' );
305
+
306
+ if ( ( $( 'tbody tr', $excludeList ).length - selectedRows.length ) >= 2 ) {
307
+ selectedRows.remove();
308
+ } else {
309
+ $( ':input', selectedRows ).val( '' );
310
+ $( selectedRows ).not( ':first' ).remove();
311
+ $( '.select2-select', selectedRows ).select2( 'val', '' );
312
+ }
313
+
314
+ $excludeList.find( 'input.cb-select' ).prop( 'checked', false );
315
+
316
+ recalculate_rules_found();
317
+ recalculate_rules_selected();
318
+ });
319
+
320
+ $( '.stream-exclude-list' ).closest( 'form' ).submit( function() {
321
+ $( '.stream-exclude-list tbody tr.hidden', this ).each( function() {
322
+ $( this ).find( ':input' ).removeAttr( 'name' );
323
+ });
324
+ $( '.stream-exclude-list tbody tr:not(.hidden) select.select2-select.connector_or_context', this ).each( function() {
325
+ var parts = $( this ).val().split( '-' );
326
+ $( this ).siblings( '.connector' ).val( parts[0] );
327
+ $( this ).siblings( '.context' ).val( parts[1] );
328
+ $( this ).removeAttr( 'name' );
329
+ });
330
+ $( '.stream-exclude-list tbody tr:not(.hidden) select.select2-select.ip_address', this ).each( function() {
331
+ var firstSelected = $( 'option:selected', this ).first();
332
+ $( 'option:selected:not(:first)', this ).each( function() {
333
+ firstSelected.attr( 'value', firstSelected.attr( 'value' ) + ',' + $( this ).attr( 'value' ) );
334
+ $( this ).removeAttr( 'selected' );
335
+ });
336
+ });
337
+ });
338
+
339
+ $( '.stream-exclude-list' ).closest( 'td' ).prev( 'th' ).hide();
340
+
341
+ $( 'table.stream-exclude-list' ).on( 'click', 'input.cb-select', function() {
342
+ recalculate_rules_selected();
343
+ });
344
+
345
+ function recalculate_rules_selected() {
346
+ var $selectedRows = $( 'table.stream-exclude-list tbody tr:not( .hidden ) input.cb-select:checked' ),
347
+ $deleteButton = $( '#exclude_rules_remove_rules' );
348
+
349
+ if ( 0 === $selectedRows.length ) {
350
+ $deleteButton.prop( 'disabled', true );
351
+ } else {
352
+ $deleteButton.prop( 'disabled', false );
353
+ }
354
+ }
355
+
356
+ function recalculate_rules_found() {
357
+ var $allRows = $( 'table.stream-exclude-list tbody tr:not( .hidden )' ),
358
+ $noRulesFound = $( 'table.stream-exclude-list tbody tr.no-items' ),
359
+ $selectAll = $( '.check-column.manage-column input.cb-select' ),
360
+ $deleteButton = $( '#exclude_rules_remove_rules' );
361
+
362
+ if ( 0 === $allRows.length ) {
363
+ $noRulesFound.show();
364
+ $selectAll.prop( 'disabled', true );
365
+ $deleteButton.prop( 'disabled', true );
366
+ } else {
367
+ $noRulesFound.hide();
368
+ $selectAll.prop( 'disabled', false );
369
+ }
370
+
371
+ wp_stream_regenerate_alt_rows( $allRows );
372
+ }
373
+
374
+ $( document ).ready( function() {
375
+ recalculate_rules_found();
376
+ recalculate_rules_selected();
377
+ });
378
+ });
ui/js/settings.js CHANGED
@@ -1,238 +1,6 @@
1
  /* globals confirm, wp_stream, ajaxurl, wp_stream_regenerate_alt_rows */
2
  jQuery( function( $ ) {
3
 
4
- var initSettingsSelect2 = function() {
5
- $( '.stream-exclude-list tr:not(.hidden) input[type=hidden].select2-select.with-source' ).each( function( k, el ) {
6
- var $input = $( el ),
7
- $connector = $( this ).prevAll( ':input.connector' );
8
-
9
- $input.select2({
10
- data: $input.data( 'values' ),
11
- allowClear: true,
12
- placeholder: $input.data( 'placeholder' )
13
- });
14
-
15
- if ( '' === $input.val() && '' !== $connector.val() ) {
16
- $input.select2( 'val', $connector.val() );
17
- $input.val( '' );
18
- }
19
- });
20
-
21
- var $input_user, $input_ip;
22
-
23
- $( '.stream-exclude-list tr:not(.hidden) input[type=hidden].select2-select.ip_address' ).each( function( k, el ) {
24
- $input_ip = $( el );
25
-
26
- $input_ip.select2({
27
- ajax: {
28
- type: 'POST',
29
- url: ajaxurl,
30
- dataType: 'json',
31
- quietMillis: 500,
32
- data: function( term ) {
33
- return {
34
- find: term,
35
- limit: 10,
36
- action: 'stream_get_ips',
37
- nonce: $input_ip.data( 'nonce' )
38
- };
39
- },
40
- results: function( response ) {
41
- var answer = { results: [] };
42
-
43
- if ( true !== response.success || undefined === response.data ) {
44
- return answer;
45
- }
46
-
47
- $.each( response.data, function( key, ip ) {
48
- answer.results.push({
49
- id: ip,
50
- text: ip
51
- });
52
- });
53
-
54
- return answer;
55
- }
56
- },
57
- initSelection: function( item, callback ) {
58
- var data = [];
59
-
60
- data.push( { id: item.val(), text: item.val() } );
61
-
62
- callback( data );
63
- },
64
- createSearchChoice: function( term ) {
65
- var ip_chunks = [];
66
-
67
- ip_chunks = term.match( /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ );
68
-
69
- if ( null === ip_chunks ) {
70
- return;
71
- }
72
-
73
- // remove whole match
74
- ip_chunks.shift();
75
-
76
- ip_chunks = $.grep(
77
- ip_chunks,
78
- function( chunk ) {
79
- var numeric = parseInt( chunk, 10 );
80
- return numeric <= 255 && numeric.toString() === chunk;
81
- }
82
- );
83
-
84
- if ( ip_chunks.length < 4 ) {
85
- return;
86
- }
87
-
88
- return {
89
- id: term,
90
- text: term
91
- };
92
- },
93
- allowClear: true,
94
- multiple: true,
95
- maximumSelectionSize: 1,
96
- placeholder: $input_ip.data( 'placeholder' )
97
- });
98
- }).on( 'change', function() {
99
- $( this ).prev( '.select2-container' ).find( 'input.select2-input' ).blur();
100
- });
101
-
102
- $( '.stream-exclude-list tr:not(.hidden) input[type=hidden].select2-select.author_or_role' ).each( function( k, el ) {
103
- $input_user = $( el );
104
-
105
- $input_user.select2({
106
- ajax: {
107
- type: 'POST',
108
- url: ajaxurl,
109
- dataType: 'json',
110
- quietMillis: 500,
111
- data: function( term, page ) {
112
- return {
113
- find: term,
114
- limit: 10,
115
- pager: page,
116
- action: 'stream_get_users',
117
- nonce: $input_user.data( 'nonce' )
118
- };
119
- },
120
- results: function( response ) {
121
- var roles = [],
122
- answer = [];
123
-
124
- roles = $.grep(
125
- $input_user.data( 'values' ),
126
- function( role ) {
127
- var roleVal = $input_user.data( 'select2' )
128
- .search
129
- .val()
130
- .toLowerCase();
131
- var rolePos = role
132
- .text
133
- .toLowerCase()
134
- .indexOf( roleVal );
135
- return rolePos >= 0;
136
- }
137
- );
138
-
139
- answer = {
140
- results: [
141
- { text: 'Roles', children: roles },
142
- { text: 'Users', children: [] }
143
- ]
144
- };
145
-
146
- if ( true !== response.success || undefined === response.data || true !== response.data.status ) {
147
- return answer;
148
- }
149
-
150
- $.each( response.data.users, function( k, user ) {
151
- if ( $.contains( roles, user.id ) ) {
152
- user.disabled = true;
153
- }
154
- });
155
-
156
- answer.results[ 1 ].children = response.data.users;
157
-
158
- // Notice we return the value of more so Select2 knows if more results can be loaded
159
- return answer;
160
- }
161
- },
162
- initSelection: function( item, callback ) {
163
- callback( { id: item.data( 'selected-id' ), text: item.data( 'selected-text' ) } );
164
- },
165
- formatResult: function( object, container ) {
166
- var result = object.text;
167
-
168
- if ( 'undefined' !== typeof object.icon && object.icon ) {
169
- result = '<img src="' + object.icon + '" class="wp-stream-select2-icon">' + result;
170
-
171
- // Add more info to the container
172
- container.attr( 'title', object.tooltip );
173
- }
174
-
175
- // Add more info to the container
176
- if ( 'undefined' !== typeof object.tooltip ) {
177
- container.attr( 'title', object.tooltip );
178
- } else if ( 'undefined' !== typeof object.user_count ) {
179
- container.attr( 'title', object.user_count );
180
- }
181
-
182
- return result;
183
- },
184
- formatSelection: function( object ) {
185
- if ( $.isNumeric( object.id ) && object.text.indexOf( 'icon-users' ) < 0 ) {
186
- object.text += '<i class="icon16 icon-users"></i>';
187
- }
188
-
189
- return object.text;
190
- },
191
- allowClear: true,
192
- placeholder: $input_user.data( 'placeholder' )
193
- }).on( 'change', function() {
194
- var value = $( this ).select2( 'data' );
195
-
196
- $( this ).data( 'selected-id', value.id );
197
- $( this ).data( 'selected-text', value.text );
198
- });
199
- });
200
-
201
- $( 'ul.select2-choices, ul.select2-choices li, input.select2-input', '.stream-exclude-list tr:not(.hidden) .ip_address' ).on( 'mousedown click focus', function() {
202
- var $container = $( this ).closest( '.select2-container' ),
203
- $input = $container.find( 'input.select2-input' ),
204
- value = $container.select2( 'data' );
205
-
206
- if ( value.length >= 1 ) {
207
- $input.blur();
208
- return false;
209
- }
210
- });
211
-
212
- $( '.stream-exclude-list tr:not(.hidden) input[type=hidden].select2-select.context' ).on( 'change', function( val ) {
213
- var $connector = $( this ).prevAll( ':input.connector' );
214
-
215
- if ( undefined !== val.added && undefined !== val.added.parent ) {
216
- $connector.val( val.added.parent );
217
- } else {
218
- $connector.val( $( this ).val() );
219
- $( this ).val( '' );
220
- }
221
- });
222
-
223
- $( '.stream-exclude-list tr:not(.hidden) .exclude_rules_remove_rule_row' ).on( 'click', function() {
224
- var $thisRow = $( this ).closest( 'tr' );
225
-
226
- $thisRow.remove();
227
-
228
- recalculate_rules_found();
229
- recalculate_rules_selected();
230
- });
231
-
232
- };
233
-
234
- initSettingsSelect2();
235
-
236
  var keepRecordsIndefinitely = $( '#wp_stream\\[general_keep_records_indefinitely\\]' ),
237
  keepRecordsFor = $( '#wp_stream_general_records_ttl' ),
238
  keepRecordsForRow = keepRecordsFor.closest( 'tr' );
@@ -253,95 +21,6 @@ jQuery( function( $ ) {
253
 
254
  toggleKeepRecordsFor();
255
 
256
- $( '#exclude_rules_new_rule' ).on( 'click', function() {
257
- var $excludeList = $( 'table.stream-exclude-list' );
258
-
259
- $( '.select2-select', $excludeList ).each( function() {
260
- $( this ).select2( 'destroy' );
261
- });
262
-
263
- var $lastRow = $( 'tr', $excludeList ).last(),
264
- $newRow = $lastRow.clone();
265
-
266
- $newRow.removeAttr( 'class' );
267
- $( '.stream-exclude-list tbody :input' ).off();
268
- $( ':input', $newRow ).off().val( '' );
269
-
270
- $lastRow.after( $newRow );
271
-
272
- initSettingsSelect2();
273
-
274
- recalculate_rules_found();
275
- recalculate_rules_selected();
276
- });
277
-
278
- $( '#exclude_rules_remove_rules' ).on( 'click', function() {
279
- var $excludeList = $( 'table.stream-exclude-list' ),
280
- selectedRows = $( 'tbody input.cb-select:checked', $excludeList ).closest( 'tr' );
281
-
282
- if ( ( $( 'tbody tr', $excludeList ).length - selectedRows.length ) >= 2 ) {
283
- selectedRows.remove();
284
- } else {
285
- $( ':input', selectedRows ).val( '' );
286
- $( selectedRows ).not( ':first' ).remove();
287
- $( '.select2-select', selectedRows ).select2( 'val', '' );
288
- }
289
-
290
- $excludeList.find( 'input.cb-select' ).prop( 'checked', false );
291
-
292
- recalculate_rules_found();
293
- recalculate_rules_selected();
294
- });
295
-
296
- $( '.stream-exclude-list' ).closest( 'form' ).submit( function() {
297
- $( '.stream-exclude-list tbody tr', this ).each( function() {
298
- if ( 0 === $( this ).find( ':input[value][value!=""]' ).length ) {
299
- // Don't send inputs in this row
300
- $( this ).find( ':input[value]' ).removeAttr( 'name' );
301
- }
302
- });
303
- });
304
-
305
- $( '.stream-exclude-list' ).closest( 'td' ).prev( 'th' ).hide();
306
-
307
- $( 'table.stream-exclude-list' ).on( 'click', 'input.cb-select', function() {
308
- recalculate_rules_selected();
309
- });
310
-
311
- function recalculate_rules_selected() {
312
- var $selectedRows = $( 'table.stream-exclude-list tbody tr:not( .hidden ) input.cb-select:checked' ),
313
- $deleteButton = $( '#exclude_rules_remove_rules' );
314
-
315
- if ( 0 === $selectedRows.length ) {
316
- $deleteButton.prop( 'disabled', true );
317
- } else {
318
- $deleteButton.prop( 'disabled', false );
319
- }
320
- }
321
-
322
- function recalculate_rules_found() {
323
- var $allRows = $( 'table.stream-exclude-list tbody tr:not( .hidden )' ),
324
- $noRulesFound = $( 'table.stream-exclude-list tbody tr.no-items' ),
325
- $selectAll = $( '.check-column.manage-column input.cb-select' ),
326
- $deleteButton = $( '#exclude_rules_remove_rules' );
327
-
328
- if ( 0 === $allRows.length ) {
329
- $noRulesFound.show();
330
- $selectAll.prop( 'disabled', true );
331
- $deleteButton.prop( 'disabled', true );
332
- } else {
333
- $noRulesFound.hide();
334
- $selectAll.prop( 'disabled', false );
335
- }
336
-
337
- wp_stream_regenerate_alt_rows( $allRows );
338
- }
339
-
340
- $( document ).ready( function() {
341
- recalculate_rules_found();
342
- recalculate_rules_selected();
343
- });
344
-
345
  // Confirmation on some important actions
346
  $( '#wp_stream_general_reset_site_settings' ).click( function( e ) {
347
  if ( ! confirm( wp_stream.i18n.confirm_defaults ) ) {
1
  /* globals confirm, wp_stream, ajaxurl, wp_stream_regenerate_alt_rows */
2
  jQuery( function( $ ) {
3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  var keepRecordsIndefinitely = $( '#wp_stream\\[general_keep_records_indefinitely\\]' ),
5
  keepRecordsFor = $( '#wp_stream_general_records_ttl' ),
6
  keepRecordsForRow = keepRecordsFor.closest( 'tr' );
21
 
22
  toggleKeepRecordsFor();
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  // Confirmation on some important actions
25
  $( '#wp_stream_general_reset_site_settings' ).click( function( e ) {
26
  if ( ! confirm( wp_stream.i18n.confirm_defaults ) ) {